Browse Source

Feature/legacywallet (#10)

ADD: scan legacy address and check balance
ADD: can creat tx from legacy address
OPS: new nodejs circleCI version
REF
localNotifications
Igor Korsakov 7 years ago
committed by GitHub
parent
commit
064caa05fc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .circleci/config.yml
  2. 49
      class/legacy-wallet.js
  3. 13
      class/segwit-p2sh-wallet.js
  4. 227
      models/signer.js
  5. 2
      package.json
  6. 88
      screen/selftest.js
  7. 5
      screen/transactions/RBF.js
  8. 10
      screen/wallets.js
  9. 2
      screen/wallets/add.js
  10. 57
      screen/wallets/scanQrWif.js
  11. 141
      screen/wallets/scanQrWifLegacyAddress.js
  12. 26
      tests/unit/signer.js

2
.circleci/config.yml

@ -7,7 +7,7 @@ jobs:
build:
docker:
# specify the version you desire here
- image: circleci/node:7.10
- image: circleci/node:8-stretch
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images

49
class/legacy-wallet.js

@ -5,6 +5,7 @@ import Frisbee from 'frisbee';
const isaac = require('isaac');
const BigNumber = require('bignumber.js');
const bitcoin = require('bitcoinjs-lib');
const signer = require('../models/signer');
/**
* Has private key and address signle like "1ABCD....."
@ -60,6 +61,12 @@ export class LegacyWallet extends AbstractWallet {
return this._address;
}
/**
* Fetches balance o the Wallet via API.
* Returns VOID. Get the balance from getter.
*
* @returns {Promise.<void>}
*/
async fetchBalance() {
let response;
let token = (array => {
@ -265,4 +272,46 @@ export class LegacyWallet extends AbstractWallet {
console.log('response', res.body);
return res.body;
}
/**
* Takes UTXOs (as presented by blockcypher api), transforms them into
* format expected by signer module, creates tx and returns signed string txhex.
*
* @param utxos Unspent outputs, expects blockcypher format
* @param amount
* @param fee
* @param toAddress
* @param memo
* @return string Signed txhex ready for broadcast
*/
createTx(utxos, amount, fee, toAddress, memo) {
// transforming UTXOs fields to how module expects it
for (let u of utxos) {
u.confirmations = 6; // hack to make module accept 0 confirmations
u.txid = u.tx_hash;
u.vout = u.tx_output_n;
u.amount = new BigNumber(u.value);
u.amount = u.amount.div(100000000);
u.amount = u.amount.toString(10);
}
console.log(
'creating legacy tx ',
amount,
' with fee ',
fee,
'secret=',
this.getSecret(),
'from address',
this.getAddress(),
);
let amountPlusFee = parseFloat(new BigNumber(amount).add(fee).toString(10));
return signer.createTransaction(
utxos,
toAddress,
amountPlusFee,
fee,
this.getSecret(),
this.getAddress(),
);
}
}

13
class/segwit-p2sh-wallet.js

@ -34,7 +34,20 @@ export class SegwitP2SHWallet extends LegacyWallet {
return this._address;
}
/**
* Takes UTXOs (as presented by blockcypher api), transforms them into
* format expected by signer module, creates tx and returns signed string txhex.
*
* @param utxos Unspent outputs, expects blockcypher format
* @param amount
* @param fee
* @param address
* @param memo
* @param sequence By default zero. Increased with each transaction replace.
* @return string Signed txhex ready for broadcast
*/
createTx(utxos, amount, fee, address, memo, sequence) {
// TODO: memo is not used here, get rid of it
if (sequence === undefined) {
sequence = 0;
}

227
models/signer.js

@ -6,128 +6,203 @@
* https://github.com/Overtorment/Cashier-BTC
*
**/
let bitcoinjs = require('bitcoinjs-lib')
exports.createSegwitTransaction = function (utxos, toAddress, amount, fixedFee, WIF, changeAddress, sequence) {
changeAddress = changeAddress || exports.WIF2segwitAddress(WIF)
let bitcoinjs = require('bitcoinjs-lib');
const toSatoshi = num => parseInt((num * 100000000).toFixed(0));
exports.createSegwitTransaction = function(
utxos,
toAddress,
amount,
fixedFee,
WIF,
changeAddress,
sequence,
) {
changeAddress = changeAddress || exports.WIF2segwitAddress(WIF);
if (sequence === undefined) {
sequence = bitcoinjs.Transaction.DEFAULT_SEQUENCE
sequence = bitcoinjs.Transaction.DEFAULT_SEQUENCE;
}
let feeInSatoshis = parseInt((fixedFee * 100000000).toFixed(0))
let keyPair = bitcoinjs.ECPair.fromWIF(WIF)
let pubKey = keyPair.getPublicKeyBuffer()
let pubKeyHash = bitcoinjs.crypto.hash160(pubKey)
let redeemScript = bitcoinjs.script.witnessPubKeyHash.output.encode(pubKeyHash)
let feeInSatoshis = parseInt((fixedFee * 100000000).toFixed(0));
let keyPair = bitcoinjs.ECPair.fromWIF(WIF);
let pubKey = keyPair.getPublicKeyBuffer();
let pubKeyHash = bitcoinjs.crypto.hash160(pubKey);
let redeemScript = bitcoinjs.script.witnessPubKeyHash.output.encode(
pubKeyHash,
);
let txb = new bitcoinjs.TransactionBuilder()
let unspentAmount = 0
let txb = new bitcoinjs.TransactionBuilder();
let unspentAmount = 0;
for (const unspent of utxos) {
if (unspent.confirmations < 2) { // using only confirmed outputs
continue
if (unspent.confirmations < 2) {
// using only confirmed outputs
continue;
}
txb.addInput(unspent.txid, unspent.vout, sequence)
unspentAmount += parseInt(((unspent.amount) * 100000000).toFixed(0))
txb.addInput(unspent.txid, unspent.vout, sequence);
unspentAmount += parseInt((unspent.amount * 100000000).toFixed(0));
}
let amountToOutput = parseInt(((amount - fixedFee) * 100000000).toFixed(0))
txb.addOutput(toAddress, amountToOutput)
let amountToOutput = parseInt(((amount - fixedFee) * 100000000).toFixed(0));
txb.addOutput(toAddress, amountToOutput);
if (amountToOutput + feeInSatoshis < unspentAmount) {
// sending less than we have, so the rest should go back
txb.addOutput(changeAddress, unspentAmount - amountToOutput - feeInSatoshis)
txb.addOutput(
changeAddress,
unspentAmount - amountToOutput - feeInSatoshis,
);
}
for (let c = 0; c < utxos.length; c++) {
txb.sign(c, keyPair, redeemScript, null, parseInt((utxos[c].amount * 100000000).toFixed(0)))
txb.sign(
c,
keyPair,
redeemScript,
null,
parseInt((utxos[c].amount * 100000000).toFixed(0)),
);
}
let tx = txb.build()
return tx.toHex()
}
exports.createRBFSegwitTransaction = function (txhex, addressReplaceMap, feeDelta, WIF, utxodata) {
let tx = txb.build();
return tx.toHex();
};
exports.createRBFSegwitTransaction = function(
txhex,
addressReplaceMap,
feeDelta,
WIF,
utxodata,
) {
if (feeDelta < 0) {
throw Error('replace-by-fee requires increased fee, not decreased')
throw Error('replace-by-fee requires increased fee, not decreased');
}
let tx = bitcoinjs.Transaction.fromHex(txhex)
let tx = bitcoinjs.Transaction.fromHex(txhex);
// looking for latest sequence number in inputs
let highestSequence = 0
let highestSequence = 0;
for (let i of tx.ins) {
if (i.sequence > highestSequence) {
highestSequence = i.sequence
highestSequence = i.sequence;
}
}
// creating TX
let txb = new bitcoinjs.TransactionBuilder()
let txb = new bitcoinjs.TransactionBuilder();
for (let unspent of tx.ins) {
txb.addInput(unspent.hash.reverse().toString('hex'), unspent.index, highestSequence + 1)
txb.addInput(
unspent.hash.reverse().toString('hex'),
unspent.index,
highestSequence + 1,
);
}
for (let o of tx.outs) {
let outAddress = bitcoinjs.address.fromOutputScript(o.script)
let outAddress = bitcoinjs.address.fromOutputScript(o.script);
if (addressReplaceMap[outAddress]) {
// means this is DESTINATION address, not messing with it's amount
// but replacing the address itseld
txb.addOutput(addressReplaceMap[outAddress], o.value)
txb.addOutput(addressReplaceMap[outAddress], o.value);
} else {
// CHANGE address, so we deduct increased fee from here
let feeDeltaInSatoshi = parseInt((feeDelta * 100000000).toFixed(0))
txb.addOutput(outAddress, o.value - feeDeltaInSatoshi)
let feeDeltaInSatoshi = parseInt((feeDelta * 100000000).toFixed(0));
txb.addOutput(outAddress, o.value - feeDeltaInSatoshi);
}
}
// signing
let keyPair = bitcoinjs.ECPair.fromWIF(WIF)
let pubKey = keyPair.getPublicKeyBuffer()
let pubKeyHash = bitcoinjs.crypto.hash160(pubKey)
let redeemScript = bitcoinjs.script.witnessPubKeyHash.output.encode(pubKeyHash)
let keyPair = bitcoinjs.ECPair.fromWIF(WIF);
let pubKey = keyPair.getPublicKeyBuffer();
let pubKeyHash = bitcoinjs.crypto.hash160(pubKey);
let redeemScript = bitcoinjs.script.witnessPubKeyHash.output.encode(
pubKeyHash,
);
for (let c = 0; c < tx.ins.length; c++) {
let txid = tx.ins[c].hash.reverse().toString('hex')
let index = tx.ins[c].index
let amount = utxodata[txid][index]
txb.sign(c, keyPair, redeemScript, null, amount)
let txid = tx.ins[c].hash.reverse().toString('hex');
let index = tx.ins[c].index;
let amount = utxodata[txid][index];
txb.sign(c, keyPair, redeemScript, null, amount);
}
let newTx = txb.build()
return newTx.toHex()
}
let newTx = txb.build();
return newTx.toHex();
};
exports.generateNewSegwitAddress = function () {
let keyPair = bitcoinjs.ECPair.makeRandom()
let pubKey = keyPair.getPublicKeyBuffer()
exports.generateNewSegwitAddress = function() {
let keyPair = bitcoinjs.ECPair.makeRandom();
let pubKey = keyPair.getPublicKeyBuffer();
let witnessScript = bitcoinjs.script.witnessPubKeyHash.output.encode(bitcoinjs.crypto.hash160(pubKey))
let scriptPubKey = bitcoinjs.script.scriptHash.output.encode(bitcoinjs.crypto.hash160(witnessScript))
let address = bitcoinjs.address.fromOutputScript(scriptPubKey)
let witnessScript = bitcoinjs.script.witnessPubKeyHash.output.encode(
bitcoinjs.crypto.hash160(pubKey),
);
let scriptPubKey = bitcoinjs.script.scriptHash.output.encode(
bitcoinjs.crypto.hash160(witnessScript),
);
let address = bitcoinjs.address.fromOutputScript(scriptPubKey);
return {
'address': address,
'WIF': keyPair.toWIF()
}
}
exports.URI = function (paymentInfo) {
let uri = 'bitcoin:'
uri += paymentInfo.address
uri += '?amount='
uri += parseFloat((paymentInfo.amount / 100000000))
uri += '&message='
uri += encodeURIComponent(paymentInfo.message)
address: address,
WIF: keyPair.toWIF(),
};
};
exports.URI = function(paymentInfo) {
let uri = 'bitcoin:';
uri += paymentInfo.address;
uri += '?amount=';
uri += parseFloat(paymentInfo.amount / 100000000);
uri += '&message=';
uri += encodeURIComponent(paymentInfo.message);
if (paymentInfo.label) {
uri += '&label='
uri += encodeURIComponent(paymentInfo.label)
uri += '&label=';
uri += encodeURIComponent(paymentInfo.label);
}
return uri
}
return uri;
};
exports.WIF2segwitAddress = function(WIF) {
let keyPair = bitcoinjs.ECPair.fromWIF(WIF);
let pubKey = keyPair.getPublicKeyBuffer();
let witnessScript = bitcoinjs.script.witnessPubKeyHash.output.encode(
bitcoinjs.crypto.hash160(pubKey),
);
let scriptPubKey = bitcoinjs.script.scriptHash.output.encode(
bitcoinjs.crypto.hash160(witnessScript),
);
return bitcoinjs.address.fromOutputScript(scriptPubKey);
};
exports.createTransaction = function(
utxos,
toAddress,
_amount,
_fixedFee,
WIF,
fromAddress,
) {
let fixedFee = toSatoshi(_fixedFee);
let amountToOutput = toSatoshi(_amount - _fixedFee);
let pk = bitcoinjs.ECPair.fromWIF(WIF); // eslint-disable-line new-cap
let txb = new bitcoinjs.TransactionBuilder();
let unspentAmount = 0;
for (const unspent of utxos) {
if (unspent.confirmations < 2) {
// using only confirmed outputs
continue;
}
txb.addInput(unspent.txid, unspent.vout);
unspentAmount += toSatoshi(unspent.amount);
}
txb.addOutput(toAddress, amountToOutput);
if (amountToOutput + fixedFee < unspentAmount) {
// sending less than we have, so the rest should go back
txb.addOutput(fromAddress, unspentAmount - amountToOutput - fixedFee);
}
for (let c = 0; c < utxos.length; c++) {
txb.sign(c, pk);
}
exports.WIF2segwitAddress = function (WIF) {
let keyPair = bitcoinjs.ECPair.fromWIF(WIF)
let pubKey = keyPair.getPublicKeyBuffer()
let witnessScript = bitcoinjs.script.witnessPubKeyHash.output.encode(bitcoinjs.crypto.hash160(pubKey))
let scriptPubKey = bitcoinjs.script.scriptHash.output.encode(bitcoinjs.crypto.hash160(witnessScript))
return bitcoinjs.address.fromOutputScript(scriptPubKey)
}
return txb.build().toHex();
};

2
package.json

@ -31,7 +31,7 @@
"postinstall": "./node_modules/.bin/rn-nodeify --install buffer,events,process,stream,util,inherits,fs,path --hack",
"test": "npm run unit && npm run jest && npm run lint",
"jest": "node node_modules/jest/bin/jest.js",
"lint": "./node_modules/.bin/eslint *.js screen/**/*.js screen/ class/ --fix",
"lint": "./node_modules/.bin/eslint *.js screen/**/*.js screen/ class/ models/ --fix",
"unit": "./node_modules/.bin/mocha tests/**/*.js"
},
"jest": {

88
screen/selftest.js

@ -62,6 +62,93 @@ export default class Selftest extends Component {
//
// utxos as received from blockcypher
let utxos = [
{
tx_hash:
'2f445cf016fa2772db7d473bff97515355b4e6148e1c980ce351d47cf54c517f',
block_height: 523186,
tx_input_n: -1,
tx_output_n: 1,
value: 100000,
ref_balance: 100000,
spent: false,
confirmations: 215,
confirmed: '2018-05-18T03:16:34Z',
double_spend: false,
},
];
let toAddr = '1GX36PGBUrF8XahZEGQqHqnJGW2vCZteoB';
let amount = 0.0009;
let fee = 0.0001;
let txHex = l.createTx(utxos, amount, fee, toAddr);
if (
txHex !==
'01000000017f514cf57cd451e30c981c8e14e6b455535197ff3b477ddb7227fa16f05c442f010000006b483045022100b9a6545847bd30418c133437c7660a6676afafe6e7e837a37ef2ead931ebd586022056bc43cbf71855d0719f54151c8fcaaaa03367ecafdd7296dbe39f042e432f4f012103aea0dfd576151cb399347aa6732f8fdf027b9ea3ea2e65fb754803f776e0a509ffffffff01905f0100000000001976a914aa381cd428a4e91327fd4434aa0a08ff131f1a5a88ac00000000'
) {
errorMessage += 'failed to create TX from legacy address; ';
isOk = false;
}
// now, several utxos
// utxos as received from blockcypher
utxos = [
{
amount: '0.002',
block_height: 523416,
confirmations: 6,
confirmed: '2018-05-19T15:46:43Z',
double_spend: false,
ref_balance: 300000,
spent: false,
tx_hash:
'dc3605040a03724bc584ed43bc22a559f5d32a1b0708ca05b20b9018fdd523ef',
tx_input_n: -1,
tx_output_n: 0,
txid:
'dc3605040a03724bc584ed43bc22a559f5d32a1b0708ca05b20b9018fdd523ef',
value: 200000,
vout: 0,
},
{
amount: '0.001',
block_height: 523186,
confirmations: 6,
confirmed: '2018-05-18T03:16:34Z',
double_spend: false,
ref_balance: 100000,
spent: false,
tx_hash:
'c473c104febfe6621804976d1082a1468c1198d0339e35f30a8ba1515d9eb017',
tx_input_n: -1,
tx_output_n: 0,
txid:
'c473c104febfe6621804976d1082a1468c1198d0339e35f30a8ba1515d9eb017',
value: 100000,
vout: 0,
},
];
toAddr = '1GX36PGBUrF8XahZEGQqHqnJGW2vCZteoB';
amount = 0.0009;
fee = 0.0001;
try {
txHex = l.createTx(utxos, amount, fee, toAddr);
console.log(txHex);
} catch (e) {
errorMessage += e.message + '; ';
isOk = false;
}
if (
txHex !==
'0100000002ef23d5fd18900bb205ca08071b2ad3f559a522bc43ed84c54b72030a040536dc000000006a47304402206b4f03e471d60dff19f4df1a8203ca97f6282658160034cea0f2b7d748c33d9802206058d23861dabdfb252c8df14249d1a2b00345dd90d32ab451cc3c6cfcb3b402012103aea0dfd576151cb399347aa6732f8fdf027b9ea3ea2e65fb754803f776e0a509ffffffff17b09e5d51a18b0af3359e33d098118c46a182106d97041862e6bffe04c173c4000000006b4830450221009785a61358a1ee7ab5885a98b111275226e0046a48b69980c4f53ecf99cdce0a02200503249e0b23d633ec1fbae5d41a0dcf9758dce3560066d1aee9ecfbfeefcfb7012103aea0dfd576151cb399347aa6732f8fdf027b9ea3ea2e65fb754803f776e0a509ffffffff02905f0100000000001976a914aa381cd428a4e91327fd4434aa0a08ff131f1a5a88ac400d0300000000001976a914597ce022baa887799951e0496c769d9cc0c759dc88ac00000000'
) {
errorMessage += 'failed to create TX from legacy address; ';
isOk = false;
}
//
l = new SegwitP2SHWallet();
l.setSecret('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct');
if (l.getAddress() !== '34AgLJhwXrvmkZS1o5TrcdeevMt22Nar53') {
@ -71,6 +158,7 @@ export default class Selftest extends Component {
//
// utxos as received from blockcypher
let utxo = [
{
tx_hash:

5
screen/transactions/RBF.js

@ -56,7 +56,8 @@ export default class RBF extends Component {
}
}
if (!destinationAddress) {
if (!destinationAddress || sourceWallet.type === 'legacy') {
// for now I'm too lazy to add RBF support for legacy addresses
this.state = {
isLoading: false,
nonReplaceable: true,
@ -76,7 +77,7 @@ export default class RBF extends Component {
async componentDidMount() {
let startTime = Date.now();
console.log('send/details - componentDidMount');
console.log('transactions/RBF - componentDidMount');
this.setState({
isLoading: false,
});

10
screen/wallets.js

@ -4,8 +4,7 @@ import WalletsList from './wallets/list';
import AddWallet from './wallets/add';
import WalletDetails from './wallets/details';
import WalletExport from './wallets/export';
import scanQrWifLegacyAddress from './wallets/scanQrWifLegacyAddress';
import scanQrWifSegwitP2SHAddress from './wallets/scanQrWifSegwitP2SHAddress';
import scanQrWif from './wallets/scanQrWif';
const WalletsNavigator = StackNavigator(
{
@ -15,11 +14,8 @@ const WalletsNavigator = StackNavigator(
AddWallet: {
screen: AddWallet,
},
ScanQrWifLegacyAddress: {
screen: scanQrWifLegacyAddress,
},
ScanQrWifSegwitP2SHAddress: {
screen: scanQrWifSegwitP2SHAddress,
ScanQrWif: {
screen: scanQrWif,
},
WalletDetails: {
screen: WalletDetails,

2
screen/wallets/add.js

@ -74,7 +74,7 @@ export default class WalletsAdd extends Component {
icon={{ name: 'qrcode', type: 'font-awesome' }}
title="Scan"
onPress={() => {
this.props.navigation.navigate('ScanQrWifSegwitP2SHAddress');
this.props.navigation.navigate('ScanQrWif');
}}
/>

57
screen/wallets/scanQrWifSegwitP2SHAddress.js → screen/wallets/scanQrWif.js

@ -9,16 +9,17 @@ import {
} from 'react-native';
import { BlueText, SafeBlueArea, BlueButton } from '../../BlueComponents';
import { Camera, Permissions } from 'expo';
import { SegwitP2SHWallet } from '../../class';
import { SegwitP2SHWallet, LegacyWallet } from '../../class';
import Ionicons from 'react-native-vector-icons/Ionicons';
import PropTypes from 'prop-types';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let EV = require('../../events');
let bip38 = require('../../bip38');
let wif = require('wif');
let prompt = require('../../prompt');
export default class CameraExample extends React.Component {
export default class ScanQrWif extends React.Component {
static navigationOptions = {
tabBarLabel: 'Wallets',
tabBarIcon: ({ tintColor, focused }) => (
@ -93,31 +94,40 @@ export default class CameraExample extends React.Component {
let newWallet = new SegwitP2SHWallet();
newWallet.setSecret(ret.data);
let newLegacyWallet = new LegacyWallet();
newLegacyWallet.setSecret(ret.data);
if (newWallet.getAddress() === false) {
// bad WIF
if (
newWallet.getAddress() === false ||
newLegacyWallet.getAddress() === false
) {
alert('Bad WIF');
return;
}
this.setState(
{
isLoading: true,
},
async () => {
newWallet.setLabel('New SegWit');
BlueApp.wallets.push(newWallet);
await BlueApp.saveToDisk();
this.props.navigation.navigate('WalletsList');
EV(EV.enum.WALLETS_COUNT_CHANGED);
alert(
'Imported WIF ' +
ret.data +
' with address ' +
newWallet.getAddress(),
);
},
);
this.setState({ isLoading: true });
await newLegacyWallet.fetchBalance();
console.log('newLegacyWallet == ', newLegacyWallet.getBalance());
if (newLegacyWallet.getBalance()) {
newLegacyWallet.setLabel('Imported Legacy');
BlueApp.wallets.push(newLegacyWallet);
alert(
'Imported WIF ' +
ret.data +
' with address ' +
newLegacyWallet.getAddress(),
);
} else {
newWallet.setLabel('Imported SegWit');
BlueApp.wallets.push(newWallet);
alert(
'Imported WIF ' + ret.data + ' with address ' + newWallet.getAddress(),
);
}
await BlueApp.saveToDisk();
this.props.navigation.navigate('WalletsList');
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
} // end
async componentWillMount() {
@ -218,8 +228,9 @@ export default class CameraExample extends React.Component {
}
}
CameraExample.propTypes = {
ScanQrWif.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.func,
navigate: PropTypes.func,
}),
};

141
screen/wallets/scanQrWifLegacyAddress.js

@ -1,141 +0,0 @@
/* global alert */
import React from 'react';
import {
Text,
ActivityIndicator,
Button,
View,
TouchableOpacity,
} from 'react-native';
import { Camera, Permissions } from 'expo';
import { LegacyWallet } from '../../class';
import Ionicons from 'react-native-vector-icons/Ionicons';
import PropTypes from 'prop-types';
let BlueApp = require('../../BlueApp');
let EV = require('../../events');
export default class CameraExample extends React.Component {
static navigationOptions = {
tabBarLabel: 'Wallets',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-briefcase' : 'ios-briefcase-outline'}
size={26}
style={{ color: tintColor }}
/>
),
};
state = {
isLoading: false,
hasCameraPermission: null,
type: Camera.Constants.Type.back,
};
async onBarCodeRead(ret) {
for (let w of BlueApp.wallets) {
// lookig for duplicates
if (w.getSecret() === ret.data) {
return; // duplicate, not adding
}
}
let newWallet = new LegacyWallet();
newWallet.setSecret(ret.data);
if (newWallet.getAddress() === false) return; // bad WIF
this.setState(
{
isLoading: true,
},
async () => {
newWallet.setLabel('NEW2');
BlueApp.wallets.push(newWallet);
await BlueApp.saveToDisk();
this.props.navigation.navigate('WalletsList');
EV(EV.enum.WALLETS_COUNT_CHANGED);
alert(
'Imported WIF ' +
ret.data +
' with address ' +
newWallet.getAddress(),
);
},
);
} // end
async componentWillMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({
hasCameraPermission: status === 'granted',
onCameraReady: function() {
alert('onCameraReady');
},
barCodeTypes: [Camera.Constants.BarCodeType.qr],
});
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 20 }}>
<ActivityIndicator />
</View>
);
}
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
} else {
return (
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1 }}
type={this.state.type}
onBarCodeRead={ret => this.onBarCodeRead(ret)}
>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}
>
<TouchableOpacity
style={{
flex: 0.2,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
this.setState({
type:
this.state.type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back,
});
}}
>
<Button
style={{ fontSize: 18, marginBottom: 10 }}
title="Go back"
onPress={() => this.props.navigation.goBack()}
/>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
}
}
CameraExample.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.func,
}),
};

26
tests/unit/signer.js

@ -3,7 +3,7 @@
let assert = require('assert')
describe('unit - signer', function () {
describe('createTransaction()', function () {
describe('createSegwitTransaction()', function () {
it('should return valid tx hex for segwit transactions', function (done) {
let signer = require('../../models/signer')
let utxos = [{ txid: '1e1a8cced5580eecd0ac15845fc3adfafbb0f5944a54950e4a16b8f6d1e9b715', vout: 1, address: '3Bsssbs4ANCGNETvGLJ3Fvri6SiVnH1fbi', account: '3Bsssbs4ANCGNETvGLJ3Fvri6SiVnH1fbi', scriptPubKey: 'a9146fbf1cee74734503297e46a0db3e3fbb06f2e9d387', amount: 0.001, confirmations: 108, spendable: false, solvable: false, safe: true }]
@ -109,4 +109,28 @@ describe('unit - signer', function () {
done()
})
})
describe('createTransaction()', () => {
const signer = require('../../models/signer')
it('should return valid TX hex for legacy transactions', () => {
let utxos = [{
txid: '2f445cf016fa2772db7d473bff97515355b4e6148e1c980ce351d47cf54c517f',
vout: 1,
address: '3Bsssbs4ANCGNETvGLJ3Fvri6SiVnH1fbi',
account: '3Bsssbs4ANCGNETvGLJ3Fvri6SiVnH1fbi',
scriptPubKey: 'a9146fbf1cee74734503297e46a0db3e3fbb06f2e9d387',
amount: 0.01,
confirmations: 108,
spendable: false,
solvable: false,
safe: true }]
let toAddr = '1GX36PGBUrF8XahZEGQqHqnJGW2vCZteoB'
let amount = 0.001
let fee = 0.0001
let WIF = 'KzbTHhzzZyVhkTYpuReMBkE7zUvvDEZtavq1DJV85MtBZyHK1TTF'
let fromAddr = '179JSjDc9Dh9pWWq9qv35sZsXQAV6VdE1E'
let txHex = signer.createTransaction(utxos, toAddr, amount, fee, WIF, fromAddr)
assert.equal(txHex, '01000000017f514cf57cd451e30c981c8e14e6b455535197ff3b477ddb7227fa16f05c442f010000006b483045022100c5d6b024db144aa1f0cb6d6212c326c9753f4144fd69947c1f38657944b92022022039214118b745afe6e031f96f3e98e705979f2b9f9cbbc6a91e11c89c811a3292012103f5438d524ad1cc288963466d6ef1a27d83183f7e9b7fe30879ecdae887692a31ffffffff02905f0100000000001976a914aa381cd428a4e91327fd4434aa0a08ff131f1a5a88aca0bb0d00000000001976a9144362a4c0dbf5102238164d1ec97f3b518bb651cd88ac00000000')
})
})
})

Loading…
Cancel
Save