diff --git a/App.test.js b/App.test.js index b150b91e..ee68c0a5 100644 --- a/App.test.js +++ b/App.test.js @@ -223,6 +223,12 @@ it('Wallet can fetch UTXO', async () => { assert.ok(w.utxo.length > 0, 'unexpected empty UTXO'); }); +it('SegwitP2SHWallet can generate segwit P2SH address from WIF', () => { + let l = new SegwitP2SHWallet(); + l.setSecret('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct'); + assert.ok(l.getAddress() === '34AgLJhwXrvmkZS1o5TrcdeevMt22Nar53', 'expected ' + l.getAddress()); +}); + it('Wallet can fetch balance', async () => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; let w = new LegacyWallet(); diff --git a/Electrum.test.js b/Electrum.test.js index 9f638bce..7e107cfb 100644 --- a/Electrum.test.js +++ b/Electrum.test.js @@ -24,7 +24,6 @@ beforeAll(async () => { describe('Electrum', () => { it('ElectrumClient can connect and query', async () => { const ElectrumClient = require('electrum-client'); - let bitcoin = require('bitcoinjs-lib'); for (let peer of BlueElectrum.hardcodedPeers) { let mainClient = new ElectrumClient(peer.tcp, peer.host, 'tcp'); diff --git a/class/abstract-hd-wallet.js b/class/abstract-hd-wallet.js index 47f43929..227b0bb9 100644 --- a/class/abstract-hd-wallet.js +++ b/class/abstract-hd-wallet.js @@ -419,8 +419,8 @@ export class AbstractHDWallet extends LegacyWallet { // wrong guess. will have to rescan if (!completelyEmptyWallet) { // so doing binary search for last used address: - this.next_free_change_address_index = await binarySearchIterationForInternalAddress(100); - this.next_free_address_index = await binarySearchIterationForExternalAddress(100); + this.next_free_change_address_index = await binarySearchIterationForInternalAddress(1000); + this.next_free_address_index = await binarySearchIterationForExternalAddress(1000); } } // end rescanning fresh wallet diff --git a/class/hd-segwit-bech32-wallet.js b/class/hd-segwit-bech32-wallet.js index 4601e23a..8a6d261d 100644 --- a/class/hd-segwit-bech32-wallet.js +++ b/class/hd-segwit-bech32-wallet.js @@ -1,11 +1,12 @@ import { AbstractHDWallet } from './abstract-hd-wallet'; import { NativeModules } from 'react-native'; -import bitcoin from 'bitcoinjs-lib'; import bip39 from 'bip39'; import BigNumber from 'bignumber.js'; import b58 from 'bs58check'; import signer from '../models/signer'; const BlueElectrum = require('../BlueElectrum'); +const bitcoin5 = require('bitcoinjs5'); +const HDNode = require('bip32'); const { RNRandomBytes } = NativeModules; @@ -30,10 +31,9 @@ function _zpubToXpub(zpub) { * @returns {String} */ function _nodeToBech32SegwitAddress(hdNode) { - const pubkeyBuf = hdNode.keyPair.getPublicKeyBuffer(); - var scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(pubkeyBuf)); - var address = bitcoin.address.fromOutputScript(scriptPubKey); - return address; + return bitcoin5.payments.p2wpkh({ + pubkey: hdNode.publicKey, + }).address; } /** @@ -126,11 +126,11 @@ export class HDSegwitBech32Wallet extends AbstractHDWallet { _getWIFByIndex(internal, index) { const mnemonic = this.secret; const seed = bip39.mnemonicToSeed(mnemonic); - const root = bitcoin.HDNode.fromSeedBuffer(seed); + const root = HDNode.fromSeed(seed); const path = `m/84'/0'/0'/${internal ? 1 : 0}/${index}`; const child = root.derivePath(path); - return child.keyPair.toWIF(); + return child.toWIF(); } _getNodeAddressByIndex(node, index) { @@ -145,13 +145,13 @@ export class HDSegwitBech32Wallet extends AbstractHDWallet { if (node === 0 && !this._node0) { const xpub = _zpubToXpub(this.getXpub()); - const hdNode = bitcoin.HDNode.fromBase58(xpub); + const hdNode = HDNode.fromBase58(xpub); this._node0 = hdNode.derive(node); } if (node === 1 && !this._node1) { const xpub = _zpubToXpub(this.getXpub()); - const hdNode = bitcoin.HDNode.fromBase58(xpub); + const hdNode = HDNode.fromBase58(xpub); this._node1 = hdNode.derive(node); } @@ -194,7 +194,7 @@ export class HDSegwitBech32Wallet extends AbstractHDWallet { // first, getting xpub const mnemonic = this.secret; const seed = bip39.mnemonicToSeed(mnemonic); - const root = bitcoin.HDNode.fromSeedBuffer(seed); + const root = HDNode.fromSeed(seed); const path = "m/84'/0'/0'"; const child = root.derivePath(path).neutered(); diff --git a/class/hd-segwit-p2sh-wallet.js b/class/hd-segwit-p2sh-wallet.js index 2dc377e2..1e0a9c29 100644 --- a/class/hd-segwit-p2sh-wallet.js +++ b/class/hd-segwit-p2sh-wallet.js @@ -1,11 +1,13 @@ import { AbstractHDWallet } from './abstract-hd-wallet'; import Frisbee from 'frisbee'; import { NativeModules } from 'react-native'; -import bitcoin from 'bitcoinjs-lib'; import bip39 from 'bip39'; import BigNumber from 'bignumber.js'; import b58 from 'bs58check'; import signer from '../models/signer'; +const bitcoin = require('bitcoinjs-lib'); +const bitcoin5 = require('bitcoinjs5'); +const HDNode = require('bip32'); const { RNRandomBytes } = NativeModules; @@ -28,13 +30,10 @@ function ypubToXpub(ypub) { * @returns {String} */ function nodeToP2shSegwitAddress(hdNode) { - const pubkeyBuf = hdNode.keyPair.getPublicKeyBuffer(); - const hash = bitcoin.crypto.hash160(pubkeyBuf); - const redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(hash); - const hash2 = bitcoin.crypto.hash160(redeemScript); - const scriptPubkey = bitcoin.script.scriptHash.output.encode(hash2); - - return bitcoin.address.fromOutputScript(scriptPubkey); + const { address } = bitcoin5.payments.p2sh({ + redeem: bitcoin5.payments.p2wpkh({ pubkey: hdNode.publicKey }), + }); + return address; } /** @@ -104,7 +103,7 @@ export class HDSegwitP2SHWallet extends AbstractHDWallet { if (!this._node0) { const xpub = ypubToXpub(this.getXpub()); - const hdNode = bitcoin.HDNode.fromBase58(xpub); + const hdNode = HDNode.fromBase58(xpub); this._node0 = hdNode.derive(0); } const address = nodeToP2shSegwitAddress(this._node0.derive(index)); @@ -118,7 +117,7 @@ export class HDSegwitP2SHWallet extends AbstractHDWallet { if (!this._node1) { const xpub = ypubToXpub(this.getXpub()); - const hdNode = bitcoin.HDNode.fromBase58(xpub); + const hdNode = HDNode.fromBase58(xpub); this._node1 = hdNode.derive(1); } const address = nodeToP2shSegwitAddress(this._node1.derive(index)); @@ -139,7 +138,7 @@ export class HDSegwitP2SHWallet extends AbstractHDWallet { // first, getting xpub const mnemonic = this.secret; const seed = bip39.mnemonicToSeed(mnemonic); - const root = bitcoin.HDNode.fromSeedBuffer(seed); + const root = HDNode.fromSeed(seed); const path = "m/49'/0'/0'"; const child = root.derivePath(path).neutered(); diff --git a/class/segwit-bech-wallet.js b/class/segwit-bech-wallet.js index e8293d20..e3d6b19a 100644 --- a/class/segwit-bech-wallet.js +++ b/class/segwit-bech-wallet.js @@ -29,7 +29,6 @@ export class SegwitBech32Wallet extends LegacyWallet { } static scriptPubKeyToAddress(scriptPubKey) { - const bitcoin = require('bitcoinjs-lib'); const scriptPubKey2 = Buffer.from(scriptPubKey, 'hex'); return bitcoin.address.fromOutputScript(scriptPubKey2, bitcoin.networks.bitcoin); } diff --git a/package-lock.json b/package-lock.json index 931128c6..e6a34f16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3405,6 +3405,11 @@ "integrity": "sha512-eAtOAFZefEnfJiRFQBGw1eYqa5GTLCZ1y86N0XSI/D6EB+E8z6VPV/UL7Gi5UEclFqoQk+6NRqEDsfmDLXn8sg==", "dev": true }, + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -4718,6 +4723,14 @@ "dev": true, "optional": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bip21": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bip21/-/bip21-2.0.2.tgz", @@ -4726,6 +4739,30 @@ "qs": "^6.3.0" } }, + "bip32": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.2.tgz", + "integrity": "sha512-Wws0wBYp+KViCTJLLalmOUhefd1puJslme6ks1HPRuEEXGA28HNRVcx6aIGnH0VNYr/NqoUMYf/QakCjbXcElQ==", + "requires": { + "@types/node": "10.12.18", + "bs58check": "^2.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "tiny-secp256k1": "^1.0.0", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + }, + "dependencies": { + "wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=", + "requires": { + "bs58check": "<3.0.0" + } + } + } + }, "bip39": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/bip39/-/bip39-2.5.0.tgz", @@ -4773,6 +4810,53 @@ "wif": "^2.0.1" } }, + "bitcoinjs5": { + "version": "git+https://github.com/Overtorment/bitcoinjs5.git#846c0185a6693f86540d58a7a5fffe8173dc8b34", + "from": "git+https://github.com/Overtorment/bitcoinjs5.git", + "requires": { + "@types/node": "10.12.18", + "bech32": "^1.1.2", + "bip32": "^2.0.3", + "bip66": "^1.1.0", + "bitcoin-ops": "^1.4.0", + "bs58check": "^2.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.3", + "merkle-lib": "^2.0.10", + "pushdata-bitcoin": "^1.0.1", + "randombytes": "^2.0.1", + "tiny-secp256k1": "^1.1.1", + "typeforce": "^1.11.3", + "varuint-bitcoin": "^1.0.4", + "wif": "^2.0.1" + }, + "dependencies": { + "bip32": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.3.tgz", + "integrity": "sha512-Tg4dHUXiYBkJyCQq4g++C2PqKcZRveVqy7cKxyl88Uai7MmmknFGaF88odYrXcXk5EMyrlXLuAMC3yEiLxRnNA==", + "requires": { + "@types/node": "10.12.18", + "bs58check": "^2.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "tiny-secp256k1": "^1.1.0", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + }, + "dependencies": { + "wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=", + "requires": { + "bs58check": "<3.0.0" + } + } + } + } + } + }, "bl": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/bl/-/bl-0.8.2.tgz", @@ -6753,6 +6837,11 @@ "flat-cache": "^2.0.1" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -6969,8 +7058,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", @@ -6988,13 +7076,11 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7007,18 +7093,15 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -7121,8 +7204,7 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -7132,7 +7214,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7145,20 +7226,17 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.3.5", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7175,7 +7253,6 @@ "mkdirp": { "version": "0.5.1", "bundled": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -7248,8 +7325,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -7259,7 +7335,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -7335,8 +7410,7 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -7366,7 +7440,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -7384,7 +7457,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -7423,13 +7495,11 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.0.3", - "bundled": true, - "optional": true + "bundled": true } } }, @@ -11893,8 +11963,7 @@ "nan": { "version": "2.13.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", - "optional": true + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==" }, "nanomatch": { "version": "1.2.13", @@ -16439,6 +16508,18 @@ "setimmediate": "^1.0.4" } }, + "tiny-secp256k1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.1.tgz", + "integrity": "sha512-jA9WalQuhKun1svJrAVi9Vu8aUWKMfR7nMV903kHjrHTTY/IFa0petSq+Jk/Mv447dGD9LC8fGsmGRubBbcNng==", + "requires": { + "bindings": "^1.3.0", + "bn.js": "^4.11.8", + "create-hmac": "^1.1.7", + "elliptic": "^6.4.0", + "nan": "^2.13.2" + } + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", diff --git a/package.json b/package.json index 21cfefaf..c0da1130 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@remobile/react-native-qrcode-local-image": "1.0.4", "bignumber.js": "8.1.1", "bip21": "2.0.2", + "bip32": "2.0.2", "bip39": "2.5.0", "bitcoinjs-lib": "3.3.2", "buffer": "5.2.1", @@ -53,6 +54,7 @@ "crypto-js": "3.1.9-1", "dayjs": "1.8.13", "electrum-client": "git+https://github.com/Overtorment/rn-electrum-client.git", + "bitcoinjs5": "git+https://github.com/Overtorment/bitcoinjs5.git", "eslint-config-prettier": "4.2.0", "eslint-config-standard": "12.0.0", "eslint-config-standard-react": "7.0.2", diff --git a/screen/transactions/RBF-create.js b/screen/transactions/RBF-create.js index af18884c..cd0668b8 100644 --- a/screen/transactions/RBF-create.js +++ b/screen/transactions/RBF-create.js @@ -117,7 +117,7 @@ export default class SendCreate extends Component { BlueApp.tx_metadata[this.state.txid]['last_sequence'] = lastSequence; // in case new TX get confirmed, we must save metadata under new txid - let bitcoin = require('bitcoinjs-lib'); + let bitcoin = bitcoinjs; let txDecoded = bitcoin.Transaction.fromHex(tx); let txid = txDecoded.getId(); BlueApp.tx_metadata[txid] = BlueApp.tx_metadata[this.state.txid]; diff --git a/tests/unit/signer.js b/tests/unit/signer.js index 38db4064..775fe6af 100644 --- a/tests/unit/signer.js +++ b/tests/unit/signer.js @@ -1,5 +1,5 @@ /* global describe, it */ - +let bitcoinjs = require('bitcoinjs-lib') let assert = require('assert') describe('unit - signer', function () { @@ -18,7 +18,6 @@ describe('unit - signer', function () { let txhex = signer.createSegwitTransaction(utxos, '1Pb81K1xJnMjUfFgKUbva6gr1HCHXxHVnr', 0.001, 0.0001, 'KyWpryAKPiXXbipxWhtprZjSLVjp22sxbVnJssq2TCNQxs1SuMeD', '3Bsssbs4ANCGNETvGLJ3Fvri6SiVnH1fbi', 0) assert.equal(txhex, '0100000000010115b7e9d1f6b8164a0e95544a94f5b0fbfaadc35f8415acd0ec0e58d5ce8c1a1e0100000017160014f90e5bca5635b84bd828064586bd7eb117fee9a90000000002905f0100000000001976a914f7c6c1f9f6142107ed293c8fbf85fbc49eb5f1b988ace00f97000000000017a9146fbf1cee74734503297e46a0db3e3fbb06f2e9d38702483045022100bd687693e57161282a80affb82f18386cbf319bca72ca2c16320b0f3b087bee802205e22a9a16b86628ea08eab83aebec1348c476e9d0c90cd41aa73c47f50d86aab0121039425479ea581ebc7f55959da8c2e1a1063491768860386335dd4630b5eeacfc500000000') // now, testing change addess, destination address, amounts & fees... - let bitcoinjs = require('bitcoinjs-lib') let tx = bitcoinjs.Transaction.fromHex(txhex) assert.equal(bitcoinjs.address.fromOutputScript(tx.outs[0].script), '1Pb81K1xJnMjUfFgKUbva6gr1HCHXxHVnr') assert.equal(bitcoinjs.address.fromOutputScript(tx.outs[1].script), '3Bsssbs4ANCGNETvGLJ3Fvri6SiVnH1fbi') @@ -30,7 +29,6 @@ describe('unit - signer', function () { it('should create Replace-By-Fee tx, given txhex', () => { let txhex = '0100000000010115b7e9d1f6b8164a0e95544a94f5b0fbfaadc35f8415acd0ec0e58d5ce8c1a1e0100000017160014f90e5bca5635b84bd828064586bd7eb117fee9a90000000002905f0100000000001976a914f7c6c1f9f6142107ed293c8fbf85fbc49eb5f1b988ace00f97000000000017a9146fbf1cee74734503297e46a0db3e3fbb06f2e9d38702483045022100bd687693e57161282a80affb82f18386cbf319bca72ca2c16320b0f3b087bee802205e22a9a16b86628ea08eab83aebec1348c476e9d0c90cd41aa73c47f50d86aab0121039425479ea581ebc7f55959da8c2e1a1063491768860386335dd4630b5eeacfc500000000' let signer = require('../../models/signer') - let bitcoinjs = require('bitcoinjs-lib') let dummyUtxodata = { '15b7e9d1f6b8164a0e95544a94f5b0fbfaadc35f8415acd0ec0e58d5ce8c1a1e': { // txid we use output from 1: 666 // output index and it's value in satoshi @@ -75,7 +73,7 @@ describe('unit - signer', function () { let signer = require('../../models/signer') let utxos = [ { 'txid': '160559030484800a77f9b38717bb0217e87bfeb47b92e2e5bad6316ad9d8d360', 'vout': 1, 'address': '3NBtBset4qPD8DZeLw4QbFi6SNjNL8hg7x', 'account': '3NBtBset4qPD8DZeLw4QbFi6SNjNL8hg7x', 'scriptPubKey': 'a914e0d81f03546ab8f29392b488ec62ab355ee7c57387', 'amount': 0.00400000, 'confirmations': 271, 'spendable': false, 'solvable': false, 'safe': true } ] let txhex = signer.createSegwitTransaction(utxos, '1Pb81K1xJnMjUfFgKUbva6gr1HCHXxHVnr', 0.003998, 0.000001, 'L4iRvejJG9gRhKVc3rZm5haoyd4EuCi77G91DnXRrvNDqiXktkXh') - let bitcoin = require('bitcoinjs-lib') + let bitcoin = bitcoinjs let tx = bitcoin.Transaction.fromHex(txhex); assert.equal(tx.ins.length, 1); assert.equal(tx.outs.length, 1); // only 1 output, which means change is neglected