Commit 88b3ceb9 authored by Marek Kotewicz's avatar Marek Kotewicz
Browse files

replaced callbacks with promises, parsing rlp, displaying scanned transaciton details

parent 4f54ca94
......@@ -10,7 +10,7 @@ import Foundation
@objc(EthkeyBridge)
class EthkeyBridge: NSObject {
@objc func brainWalletAddress(_ seed: String, callback: RCTResponseSenderBlock) -> Void {
@objc func brainWalletAddress(_ seed: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var seed_ptr = seed.asPtr()
let keypair_ptr = ethkey_keypair_brainwallet(&seed_ptr)
let address_rust_str = ethkey_keypair_address(keypair_ptr)
......@@ -19,10 +19,10 @@ class EthkeyBridge: NSObject {
rust_string_ptr_destroy(address_rust_str_ptr)
rust_string_destroy(address_rust_str)
ethkey_keypair_destroy(keypair_ptr)
callback([address])
resolve(address)
}
@objc func brainWalletSecret(_ seed: String, callback: RCTResponseSenderBlock) -> Void {
@objc func brainWalletSecret(_ seed: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var seed_ptr = seed.asPtr()
let keypair_ptr = ethkey_keypair_brainwallet(&seed_ptr)
let secret_rust_str = ethkey_keypair_secret(keypair_ptr)
......@@ -31,10 +31,10 @@ class EthkeyBridge: NSObject {
rust_string_ptr_destroy(secret_rust_str_ptr)
rust_string_destroy(secret_rust_str)
ethkey_keypair_destroy(keypair_ptr)
callback([secret])
resolve(secret)
}
@objc func brainWalletSign(_ seed: String, message: String, callback: RCTResponseSenderBlock) -> Void {
@objc func brainWalletSign(_ seed: String, message: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var seed_ptr = seed.asPtr()
var message_ptr = message.asPtr()
let keypair_ptr = ethkey_keypair_brainwallet(&seed_ptr)
......@@ -44,16 +44,21 @@ class EthkeyBridge: NSObject {
rust_string_ptr_destroy(signature_rust_str_ptr)
rust_string_destroy(signature_rust_str)
ethkey_keypair_destroy(keypair_ptr)
callback([signature])
resolve(signature)
}
@objc func rlpItem(_ rlp: String, position: UInt32, callback: RCTResponseSenderBlock) -> Void {
@objc func rlpItem(_ rlp: String, position: UInt32, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var rlp_ptr = rlp.asPtr()
let item_rust_str = rlp_item(&rlp_ptr, position)
var error: UInt32 = 0
let item_rust_str = rlp_item(&rlp_ptr, position, &error)
let item_rust_str_ptr = rust_string_ptr(item_rust_str)
let item = String.fromStringPtr(ptr: item_rust_str_ptr!.pointee)
rust_string_ptr_destroy(item_rust_str_ptr)
rust_string_destroy(item_rust_str)
callback([item])
if (error == 0) {
resolve(item)
} else {
reject("invalid rlp", nil, nil)
}
}
}
......@@ -10,9 +10,12 @@
@interface RCT_EXTERN_MODULE(EthkeyBridge, NSObject)
RCT_EXTERN_METHOD(brainWalletAddress:(NSString*)seed callback:(RCTResponseSenderBlock)callback)
RCT_EXTERN_METHOD(brainWalletSecret:(NSString*)seed callback:(RCTResponseSenderBlock)callback)
RCT_EXTERN_METHOD(brainWalletSign:(NSString*)seed message:(NSString*)message callback:(RCTResponseSenderBlock)callback)
RCT_EXTERN_METHOD(rlpItem:(NSString*)rlp position:(NSUInteger)position callback:(RCTResponseSenderBlock)callback)
RCT_EXTERN_METHOD(brainWalletAddress:(NSString*)seed resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
//RCT_EXTERN_METHOD(brainWalletSecret:(NSString*)seed callback:(RCTResponseSenderBlock)callback)
RCT_EXTERN_METHOD(brainWalletSecret:(NSString*)seed resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
//RCT_EXTERN_METHOD(brainWalletSign:(NSString*)seed message:(NSString*)message callback:(RCTResponseSenderBlock)callback)
RCT_EXTERN_METHOD(brainWalletSign:(NSString*)seed message:(NSString*)message resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
//RCT_EXTERN_METHOD(rlpItem:(NSString*)rlp position:(NSUInteger)position callback:(RCTResponseSenderBlock)callback)
RCT_EXTERN_METHOD(rlpItem:(NSString*)rlp position:(NSUInteger)position resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@end
......@@ -9,6 +9,7 @@
"test": "jest"
},
"dependencies": {
"bignumber.js": "^4.0.0",
"debounce": "^1.0.0",
"react": "^15.4.2",
"react-native": "0.40.0",
......
......@@ -5,6 +5,7 @@ authors = ["debris <marek.kotewicz@gmail.com>"]
[dependencies]
libc = "0.2"
rustc-serialize = "0.3"
ethkey = { git = "https://github.com/ethcore/parity" }
rlp = { git = "https://github.com/ethcore/parity" }
......
......@@ -45,4 +45,4 @@ struct rust_string* ethkey_keypair_sign(const struct keypair_ptr* keypair, const
// rlp ffi
// returns rlp item at given position
struct rust_string* rlp_item(const struct rust_string_ptr* rlp, const unsigned position);
struct rust_string* rlp_item(const struct rust_string_ptr* rlp, const unsigned position, unsigned* error);
extern crate libc;
extern crate rustc_serialize;
extern crate ethkey;
extern crate rlp;
mod string;
use rustc_serialize::hex::{ToHex, FromHex};
use ethkey::{KeyPair, Generator, Brain, Message, sign};
use rlp::{UntrustedRlp, View};
use string::StringPtr;
// string ffi
......@@ -56,7 +61,39 @@ pub unsafe extern fn ethkey_keypair_sign(keypair: *mut KeyPair, message: *mut St
Box::into_raw(Box::new(signature))
}
fn safe_rlp_item(rlp: &str, position: u32) -> Result<String, String> {
let hex = rlp.from_hex().map_err(| e | e.to_string())?;
let rlp = UntrustedRlp::new(&hex);
let item = rlp.at(position as usize).map_err(| e | e.to_string())?;
let data = item.data().map_err(| e | e.to_string())?;
Ok(data.to_hex())
}
#[no_mangle]
pub unsafe extern fn rlp_item(rlp: *mut StringPtr, position: u32) -> *mut String {
unimplemented!();
pub unsafe extern fn rlp_item(rlp: *mut StringPtr, position: u32, error: *mut u32) -> *mut String {
match safe_rlp_item((*rlp).as_str(), position) {
Ok(result) => Box::into_raw(Box::new(result)),
Err(_err) => {
*error = 1;
let s: String = "".into();
Box::into_raw(Box::new(s))
}
}
}
#[cfg(test)]
mod tests {
use super::safe_rlp_item;
#[test]
fn test_rlp_item() {
let rlp = "f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804";
assert_eq!(safe_rlp_item(rlp, 0), Ok("".into()));
assert_eq!(safe_rlp_item(rlp, 1), Ok("01".into()));
assert_eq!(safe_rlp_item(rlp, 2), Ok("5208".into()));
assert_eq!(safe_rlp_item(rlp, 3), Ok("095e7baea6a6c7c4c2dfeb977efac326af552d87".into()));
assert_eq!(safe_rlp_item(rlp, 4), Ok("0a".into()));
assert_eq!(safe_rlp_item(rlp, 5), Ok("".into()));
}
}
......@@ -2,10 +2,11 @@
import { NEW_SCANNED_TX, SIGNED_TX } from '../constants/TransactionActions'
export function newScannedTx(data) {
export function scannedTx(rlp, transaction) {
return {
type: NEW_SCANNED_TX,
data: data,
rlp,
transaction,
}
}
......
......@@ -8,12 +8,12 @@ import Send from '../components/Send'
const mapStateToProps = (state, ownProps) => ({
nextButtonTitle: 'Next',
nextButtonDescription: 'Choose account',
txRecipientAddress: '0xbF35fAA9C265bAf50C9CFF8c389C363B05753275',
txValue: '20 eth',
txNonce: '100',
txGas: '200',
txGasPrice: '',
txData: '',
txRecipientAddress: state.transactions.pendingTransaction.transaction.action,
txValue: state.transactions.pendingTransaction.transaction.value,
txNonce: state.transactions.pendingTransaction.transaction.nonce,
txGas: state.transactions.pendingTransaction.transaction.gas,
txGasPrice: state.transactions.pendingTransaction.transaction.gasPrice,
txData: state.transactions.pendingTransaction.transaction.data,
})
const mapDispatchToProps = (dispatch, ownProps) => ({
......
......@@ -36,11 +36,18 @@ export class NewAccount extends Component {
name: '',
}
brainWalletAddress(seed, (address) => {
this.setState({
this.updateAddress(this, seed)
}
async updateAddress(self, seed) {
try {
let address = await brainWalletAddress(seed)
self.setState({
address: address,
})
})
} catch (e) {
}
}
render() {
......@@ -62,15 +69,8 @@ export class NewAccount extends Component {
/>
<Text style={AppStyles.hintText}>brain wallet seed</Text>
<NewAccountInput seed={this.state.seed} onChangeText={
debounce((text) => {
brainWalletAddress(text, (address) => {
self.setState({
seed: text,
address: address,
})
})
}, 100)}
/>
debounce((text) => { this.updateAddress(this, text) }, 100)
}/>
<Text style={AppStyles.valueText}>0x{this.state.address}</Text>
<Button
onPress={() => this.props.addAccount({
......
'use strict'
import React, { Component } from 'react'
import { Vibration } from 'react-native'
import { Vibration, Alert } from 'react-native'
import { connect } from 'react-redux'
import { Actions } from 'react-native-router-flux'
import Scanner from '../components/Scanner'
import { newScannedTx } from '../actions/transactions'
import { scannedTx } from '../actions/transactions'
import transaction from '../util/transaction'
const mapDispatchToProps = (dispatch, ownProps) => ({
onBarCodeRead: (scanned) => {
dispatch(newScannedTx(scanned.data))
var scanning = false
async function onScannedTransaction(data, dispatch) {
try {
if (scanning) {
return
}
scanning = true
let tx = await transaction(data);
dispatch(scannedTx(data, tx))
Vibration.vibrate()
Actions.confirm()
scanning = false
} catch (e) {
console.log(e)
Alert.alert('Invalid transaction', undefined, [
{ text: 'OK', onPress: () => { scanning = false }}
])
}
}
const mapDispatchToProps = (dispatch, ownProps) => ({
onBarCodeRead: (scanned) => {
onScannedTransaction(scanned.data, dispatch)
}
})
......
......@@ -2,17 +2,29 @@
import { NEW_SCANNED_TX, SIGNED_TX } from '../constants/TransactionActions'
const initialState = {}
const initialState = {
pendingTransaction: {
transaction: {},
rlp: '',
},
signedTransaction: {
transaction: {},
rlp: '',
},
}
export default function transactions(state = initialState, { type, data }) {
switch (type) {
export default function transactions(state = initialState, action) {
switch (action.type) {
case NEW_SCANNED_TX:
return Object.assign({}, state, {
pendingTx: data,
pendingTransaction: {
rlp: action.rlp,
transaction: action.transaction,
}
})
case SIGNED_TX:
return Object.assign({}, state, {
signedTx: data,
signedTransaction: action.data,
})
default:
return state
......
import { rlpItem } from './native'
import { fromWei } from './units'
class Transaction {
constructor(nonce, gasPrice, gas, action, value, data) {
this.nonce = nonce
this.gasPrice = gasPrice
this.gas = gas
this.nonce = nonce || "0"
this.gasPrice = parseInt(gasPrice, 16).toString()
this.gas = parseInt(gas, 16).toString()
this.action = action
this.value = value
this.data = data
this.value = fromWei(value)
this.data = data || '-'
}
}
export default function transactionFromRlp(rlp, callback) {
async function asyncTransaction(rlp, resolve, reject) {
try {
let nonce = await rlpItem(rlp, 0)
let gasPrice = await rlpItem(rlp, 1)
let gas = await rlpItem(rlp, 2)
let action = await rlpItem(rlp, 3)
let value = await rlpItem(rlp, 4)
let data = await rlpItem(rlp, 5)
let tx = new Transaction(nonce, gasPrice, gas, action, value, data)
resolve(tx)
} catch (e) {
reject(e)
}
}
export default function transaction(rlp) {
return new Promise((resolve, reject) => asyncTransaction(rlp, resolve, reject))
}
import BigNumber from 'bignumber.js'
let unitMap = {
'noether': '0',
'wei': '1',
'kwei': '1000',
'Kwei': '1000',
'babbage': '1000',
'femtoether': '1000',
'mwei': '1000000',
'Mwei': '1000000',
'lovelace': '1000000',
'picoether': '1000000',
'gwei': '1000000000',
'Gwei': '1000000000',
'shannon': '1000000000',
'nanoether': '1000000000',
'nano': '1000000000',
'szabo': '1000000000000',
'microether': '1000000000000',
'micro': '1000000000000',
'finney': '1000000000000000',
'milliether': '1000000000000000',
'milli': '1000000000000000',
'ether': '1000000000000000000',
'kether': '1000000000000000000000',
'grand': '1000000000000000000000',
'mether': '1000000000000000000000000',
'gether': '1000000000000000000000000000',
'tether': '1000000000000000000000000000000'
}
function getValueOfUnit(unit) {
unit = unit ? unit.toLowerCase() : 'ether'
var unitValue = unitMap[unit]
return new BigNumber(unitValue, 10)
}
export function fromWei(number, unit) {
return new BigNumber(number, 16).dividedBy(getValueOfUnit(unit)).toString(10)
}
import { fromWei } from './units'
describe('units', () => {
it('should properly convert units from wei', () => {
let wei = '5208';
let ether = fromWei(wei)
expect(ether).toEqual("0.000000000000021")
})
})
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment