From 884f3fd57d910637c7ae8ea8219d261571458cd9 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 15 May 2018 22:17:46 +1000 Subject: [PATCH] rm HDNode, use bip32 module --- README.md | 3 - package.json | 3 + src/hdnode.js | 316 ----------------------------- src/index.js | 9 +- test/fixtures/hdnode.json | 327 ------------------------------ test/hdnode.js | 397 ------------------------------------- test/integration/bip32.js | 47 +++-- test/integration/crypto.js | 28 ++- 8 files changed, 47 insertions(+), 1083 deletions(-) delete mode 100644 src/hdnode.js delete mode 100644 test/fixtures/hdnode.json delete mode 100644 test/hdnode.js diff --git a/README.md b/README.md index dfe8b7f..14faf3d 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,6 @@ npm install @types/bitcoinjs-lib ``` You can now use `bitcoinjs-lib` as a typescript compliant library. -``` javascript -import { HDNode, Transaction } from 'bitcoinjs-lib' -``` For VSCode (and other editors), users are advised to install the type declarations, as Intellisense uses that information to help you code (autocompletion, static analysis). diff --git a/package.json b/package.json index 0b73b4e..254289f 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "dependencies": { "bech32": "^1.1.2", "bigi": "^1.4.0", + "bip32": "0.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.3.0", "bs58check": "^2.0.0", @@ -42,11 +43,13 @@ "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", "safe-buffer": "^5.1.1", + "tiny-secp256k1": "0.0.5", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" }, "devDependencies": { + "bigi": "^1.4.2", "bip39": "^2.3.0", "bip65": "^1.0.1", "bs58": "^4.0.0", diff --git a/src/hdnode.js b/src/hdnode.js deleted file mode 100644 index a96b20c..0000000 --- a/src/hdnode.js +++ /dev/null @@ -1,316 +0,0 @@ -var Buffer = require('safe-buffer').Buffer -var base58check = require('bs58check') -var bcrypto = require('./crypto') -var createHmac = require('create-hmac') -var typeforce = require('typeforce') -var types = require('./types') -var NETWORKS = require('./networks') - -var BigInteger = require('bigi') -var ECPair = require('./ecpair') - -var ecurve = require('ecurve') -var curve = ecurve.getCurveByName('secp256k1') - -function HDNode (keyPair, chainCode) { - typeforce(types.tuple('ECPair', types.Buffer256bit), arguments) - - if (!keyPair.compressed) throw new TypeError('BIP32 only allows compressed keyPairs') - - this.keyPair = keyPair - this.chainCode = chainCode - this.depth = 0 - this.index = 0 - this.parentFingerprint = 0x00000000 -} - -HDNode.HIGHEST_BIT = 0x80000000 -HDNode.LENGTH = 78 -HDNode.MASTER_SECRET = Buffer.from('Bitcoin seed', 'utf8') - -HDNode.fromSeedBuffer = function (seed, network) { - typeforce(types.tuple(types.Buffer, types.maybe(types.Network)), arguments) - - if (seed.length < 16) throw new TypeError('Seed should be at least 128 bits') - if (seed.length > 64) throw new TypeError('Seed should be at most 512 bits') - - var I = createHmac('sha512', HDNode.MASTER_SECRET).update(seed).digest() - var IL = I.slice(0, 32) - var IR = I.slice(32) - - // In case IL is 0 or >= n, the master key is invalid - // This is handled by the ECPair constructor - var pIL = BigInteger.fromBuffer(IL) - var keyPair = new ECPair(pIL, null, { - network: network - }) - - return new HDNode(keyPair, IR) -} - -HDNode.fromSeedHex = function (hex, network) { - return HDNode.fromSeedBuffer(Buffer.from(hex, 'hex'), network) -} - -HDNode.fromBase58 = function (string, networks) { - var buffer = base58check.decode(string) - if (buffer.length !== 78) throw new Error('Invalid buffer length') - - // 4 bytes: version bytes - var version = buffer.readUInt32BE(0) - var network - - // list of networks? - if (Array.isArray(networks)) { - network = networks.filter(function (x) { - return version === x.bip32.private || - version === x.bip32.public - }).pop() - - if (!network) throw new Error('Unknown network version') - - // otherwise, assume a network object (or default to bitcoin) - } else { - network = networks || NETWORKS.bitcoin - } - - if (version !== network.bip32.private && - version !== network.bip32.public) throw new Error('Invalid network version') - - // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ... - var depth = buffer[4] - - // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) - var parentFingerprint = buffer.readUInt32BE(5) - if (depth === 0) { - if (parentFingerprint !== 0x00000000) throw new Error('Invalid parent fingerprint') - } - - // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. - // This is encoded in MSB order. (0x00000000 if master key) - var index = buffer.readUInt32BE(9) - if (depth === 0 && index !== 0) throw new Error('Invalid index') - - // 32 bytes: the chain code - var chainCode = buffer.slice(13, 45) - var keyPair - - // 33 bytes: private key data (0x00 + k) - if (version === network.bip32.private) { - if (buffer.readUInt8(45) !== 0x00) throw new Error('Invalid private key') - - var d = BigInteger.fromBuffer(buffer.slice(46, 78)) - keyPair = new ECPair(d, null, { network: network }) - - // 33 bytes: public key data (0x02 + X or 0x03 + X) - } else { - var Q = ecurve.Point.decodeFrom(curve, buffer.slice(45, 78)) - // Q.compressed is assumed, if somehow this assumption is broken, `new HDNode` will throw - - // Verify that the X coordinate in the public point corresponds to a point on the curve. - // If not, the extended public key is invalid. - curve.validate(Q) - - keyPair = new ECPair(null, Q, { network: network }) - } - - var hd = new HDNode(keyPair, chainCode) - hd.depth = depth - hd.index = index - hd.parentFingerprint = parentFingerprint - - return hd -} - -HDNode.prototype.getAddress = function () { - return this.keyPair.getAddress() -} - -HDNode.prototype.getIdentifier = function () { - return bcrypto.hash160(this.keyPair.getPublicKeyBuffer()) -} - -HDNode.prototype.getFingerprint = function () { - return this.getIdentifier().slice(0, 4) -} - -HDNode.prototype.getNetwork = function () { - return this.keyPair.getNetwork() -} - -HDNode.prototype.getPublicKeyBuffer = function () { - return this.keyPair.getPublicKeyBuffer() -} - -HDNode.prototype.neutered = function () { - var neuteredKeyPair = new ECPair(null, this.keyPair.Q, { - network: this.keyPair.network - }) - - var neutered = new HDNode(neuteredKeyPair, this.chainCode) - neutered.depth = this.depth - neutered.index = this.index - neutered.parentFingerprint = this.parentFingerprint - - return neutered -} - -HDNode.prototype.sign = function (hash) { - return this.keyPair.sign(hash) -} - -HDNode.prototype.verify = function (hash, signature) { - return this.keyPair.verify(hash, signature) -} - -HDNode.prototype.toBase58 = function (__isPrivate) { - if (__isPrivate !== undefined) throw new TypeError('Unsupported argument in 2.0.0') - - // Version - var network = this.keyPair.network - var version = (!this.isNeutered()) ? network.bip32.private : network.bip32.public - var buffer = Buffer.allocUnsafe(78) - - // 4 bytes: version bytes - buffer.writeUInt32BE(version, 0) - - // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, .... - buffer.writeUInt8(this.depth, 4) - - // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) - buffer.writeUInt32BE(this.parentFingerprint, 5) - - // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. - // This is encoded in big endian. (0x00000000 if master key) - buffer.writeUInt32BE(this.index, 9) - - // 32 bytes: the chain code - this.chainCode.copy(buffer, 13) - - // 33 bytes: the public key or private key data - if (!this.isNeutered()) { - // 0x00 + k for private keys - buffer.writeUInt8(0, 45) - this.keyPair.d.toBuffer(32).copy(buffer, 46) - - // 33 bytes: the public key - } else { - // X9.62 encoding for public keys - this.keyPair.getPublicKeyBuffer().copy(buffer, 45) - } - - return base58check.encode(buffer) -} - -// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions -HDNode.prototype.derive = function (index) { - typeforce(types.UInt32, index) - - var isHardened = index >= HDNode.HIGHEST_BIT - var data = Buffer.allocUnsafe(37) - - // Hardened child - if (isHardened) { - if (this.isNeutered()) throw new TypeError('Could not derive hardened child key') - - // data = 0x00 || ser256(kpar) || ser32(index) - data[0] = 0x00 - this.keyPair.d.toBuffer(32).copy(data, 1) - data.writeUInt32BE(index, 33) - - // Normal child - } else { - // data = serP(point(kpar)) || ser32(index) - // = serP(Kpar) || ser32(index) - this.keyPair.getPublicKeyBuffer().copy(data, 0) - data.writeUInt32BE(index, 33) - } - - var I = createHmac('sha512', this.chainCode).update(data).digest() - var IL = I.slice(0, 32) - var IR = I.slice(32) - - var pIL = BigInteger.fromBuffer(IL) - - // In case parse256(IL) >= n, proceed with the next value for i - if (pIL.compareTo(curve.n) >= 0) { - return this.derive(index + 1) - } - - // Private parent key -> private child key - var derivedKeyPair - if (!this.isNeutered()) { - // ki = parse256(IL) + kpar (mod n) - var ki = pIL.add(this.keyPair.d).mod(curve.n) - - // In case ki == 0, proceed with the next value for i - if (ki.signum() === 0) { - return this.derive(index + 1) - } - - derivedKeyPair = new ECPair(ki, null, { - network: this.keyPair.network - }) - - // Public parent key -> public child key - } else { - // Ki = point(parse256(IL)) + Kpar - // = G*IL + Kpar - var Ki = curve.G.multiply(pIL).add(this.keyPair.Q) - - // In case Ki is the point at infinity, proceed with the next value for i - if (curve.isInfinity(Ki)) { - return this.derive(index + 1) - } - - derivedKeyPair = new ECPair(null, Ki, { - network: this.keyPair.network - }) - } - - var hd = new HDNode(derivedKeyPair, IR) - hd.depth = this.depth + 1 - hd.index = index - hd.parentFingerprint = this.getFingerprint().readUInt32BE(0) - - return hd -} - -HDNode.prototype.deriveHardened = function (index) { - typeforce(types.UInt31, index) - - // Only derives hardened private keys by default - return this.derive(index + HDNode.HIGHEST_BIT) -} - -// Private === not neutered -// Public === neutered -HDNode.prototype.isNeutered = function () { - return !(this.keyPair.d) -} - -HDNode.prototype.derivePath = function (path) { - typeforce(types.BIP32Path, path) - - var splitPath = path.split('/') - if (splitPath[0] === 'm') { - if (this.parentFingerprint) { - throw new Error('Not a master node') - } - - splitPath = splitPath.slice(1) - } - - return splitPath.reduce(function (prevHd, indexStr) { - var index - if (indexStr.slice(-1) === "'") { - index = parseInt(indexStr.slice(0, -1), 10) - return prevHd.deriveHardened(index) - } else { - index = parseInt(indexStr, 10) - return prevHd.derive(index) - } - }, this) -} - -module.exports = HDNode diff --git a/src/index.js b/src/index.js index e0c58cb..e1876f0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,18 +1,17 @@ -var script = require('./script') - -var templates = require('./templates') -for (var key in templates) { +let script = require('./script') +let templates = require('./templates') +for (let key in templates) { script[key] = templates[key] } module.exports = { Block: require('./block'), ECPair: require('./ecpair'), - HDNode: require('./hdnode'), Transaction: require('./transaction'), TransactionBuilder: require('./transaction_builder'), address: require('./address'), + bip32: require('bip32'), crypto: require('./crypto'), networks: require('./networks'), opcodes: require('bitcoin-ops'), diff --git a/test/fixtures/hdnode.json b/test/fixtures/hdnode.json deleted file mode 100644 index 56e97d6..0000000 --- a/test/fixtures/hdnode.json +++ /dev/null @@ -1,327 +0,0 @@ -{ - "valid": [ - { - "network": "bitcoin", - "master": { - "seed": "000102030405060708090a0b0c0d0e0f", - "wif": "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW", - "pubKey": "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", - "chainCode": "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508", - "base58": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - "base58Priv": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - "identifier": "3442193e1bb70916e914552172cd4e2dbc9df811", - "fingerprint": "3442193e", - "address": "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma" - }, - "children": [ - { - "path": "m/0'", - "m": 0, - "hardened": true, - "wif": "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT", - "pubKey": "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56", - "chainCode": "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141", - "base58": "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", - "base58Priv": "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", - "identifier": "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7", - "fingerprint": "5c1bd648", - "address": "19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh", - "index": 2147483648, - "depth": 1 - }, - { - "path": "m/0'/1", - "m": 1, - "wif": "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM", - "pubKey": "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c", - "chainCode": "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19", - "base58": "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", - "base58Priv": "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", - "identifier": "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe", - "fingerprint": "bef5a2f9", - "address": "1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj", - "index": 1, - "depth": 2 - }, - { - "path": "m/0'/1/2'", - "m": 2, - "hardened": true, - "wif": "L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU", - "pubKey": "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2", - "chainCode": "04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f", - "base58": "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", - "base58Priv": "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", - "identifier": "ee7ab90cde56a8c0e2bb086ac49748b8db9dce72", - "fingerprint": "ee7ab90c", - "address": "1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x", - "index": 2147483650, - "depth": 3 - }, - { - "path": "m/0'/1/2'/2", - "m": 2, - "wif": "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR", - "pubKey": "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29", - "chainCode": "cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd", - "base58": "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV", - "base58Priv": "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", - "identifier": "d880d7d893848509a62d8fb74e32148dac68412f", - "fingerprint": "d880d7d8", - "address": "1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt", - "index": 2, - "depth": 4 - }, - { - "path": "m/0'/1/2'/2/1000000000", - "m": 1000000000, - "wif": "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs", - "pubKey": "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011", - "chainCode": "c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e", - "base58": "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", - "base58Priv": "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", - "identifier": "d69aa102255fed74378278c7812701ea641fdf32", - "fingerprint": "d69aa102", - "address": "1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam", - "index": 1000000000, - "depth": 5 - } - ] - }, - { - "network": "bitcoin", - "master": { - "seed": "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", - "wif": "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy", - "pubKey": "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7", - "chainCode": "60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689", - "base58": "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", - "base58Priv": "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", - "identifier": "bd16bee53961a47d6ad888e29545434a89bdfe95", - "fingerprint": "bd16bee5", - "address": "1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg" - }, - "children": [ - { - "path": "m/0", - "m": 0, - "wif": "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj", - "pubKey": "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea", - "chainCode": "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c", - "base58": "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", - "base58Priv": "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", - "identifier": "5a61ff8eb7aaca3010db97ebda76121610b78096", - "fingerprint": "5a61ff8e", - "address": "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ", - "index": 0, - "depth": 1 - }, - { - "path": "m/0/2147483647'", - "m": 2147483647, - "hardened": true, - "wif": "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr", - "pubKey": "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b", - "chainCode": "be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9", - "base58": "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", - "base58Priv": "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", - "identifier": "d8ab493736da02f11ed682f88339e720fb0379d1", - "fingerprint": "d8ab4937", - "address": "1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk", - "index": 4294967295, - "depth": 2 - }, - { - "path": "m/0/2147483647'/1", - "m": 1, - "wif": "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2", - "pubKey": "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9", - "chainCode": "f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb", - "base58": "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", - "base58Priv": "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", - "identifier": "78412e3a2296a40de124307b6485bd19833e2e34", - "fingerprint": "78412e3a", - "address": "1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW", - "index": 1, - "depth": 3 - }, - { - "path": "m/0/2147483647'/1/2147483646'", - "m": 2147483646, - "hardened": true, - "wif": "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF", - "pubKey": "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0", - "chainCode": "637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29", - "base58": "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", - "base58Priv": "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", - "identifier": "31a507b815593dfc51ffc7245ae7e5aee304246e", - "fingerprint": "31a507b8", - "address": "15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R", - "index": 4294967294, - "depth": 4 - }, - { - "path": "m/0/2147483647'/1/2147483646'/2", - "m": 2, - "wif": "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK", - "pubKey": "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c", - "chainCode": "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271", - "base58": "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", - "base58Priv": "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", - "identifier": "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220", - "fingerprint": "26132fdb", - "address": "14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt", - "index": 2, - "depth": 5 - } - ] - }, - { - "comment": "Private key has leading zeros, see PR #673", - "network": "bitcoin", - "master": { - "seed": "d13de7bd1e54422d1a3b3b699a27fb460de2849e7e66a005c647e8e4a54075cb", - "wif": "KwDiCU5bs8xQwsRgxjhkcJcVuR7NE4Mei8X9uSAVviVTE7JmMoS6", - "pubKey": "0298ccc720d5dea817c7077605263bae52bca083cf8888fee77ff4c1b4797ee180", - "chainCode": "c23ab32b36ddff49fae350a1bed8ec6b4d9fc252238dd789b7273ba4416054eb", - "base58": "xpub661MyMwAqRbcGUbHLLJ5n2DzFAt8mmaDxbmbdimh68m8EiXGEQPiJya4BJat5yMzy4e68VSUoLGCu5uvzf8dUoGvwuJsLE6F1cibmWsxFNn", - "base58Priv": "xprv9s21ZrQH143K3zWpEJm5QtHFh93eNJrNbNqzqLN5XoE9MvC7gs5TmBFaL2PpaXpDc8FBYVe5EChc73ApjSQ5fWsXS7auHy1MmG6hdpywE1q", - "identifier": "1a87677be6f73cc9655e8b4c5d2fd0aeeb1b23c7", - "fingerprint": "1a87677b", - "address": "KyDarNhq8WK8rSU36UY7bDv9MAwdpKFZYKPN89Geh2dUwHjTqVh5" - }, - "children": [ - { - "path": "m/44'/0'/0'/0/0'", - "wif": "L3z3MSqZtDQ1FPHKi7oWf1nc9rMEGFtZUDCoFa7n4F695g5qZiSu", - "pubKey": "027c3591221e28939e45f8ea297d62c3640ebb09d7058b01d09c963d984a40ad49", - "chainCode": "ca27553aa89617e982e621637d6478f564b32738f8bbe2e48d0a58a8e0f6da40", - "base58": "xpub6GcBnm7FfDg5ERWACCvtuotN6Tdoc37r3SZ1asBHvCWzPkqWn3MVKPWKzy6GsfmdMUGanR3D12dH1cp5tJauuubwc4FAJDn67SH2uUjwAT1", - "base58Priv": "xprvA3cqPFaMpr7n1wRh6BPtYfwdYRoKCaPzgDdQnUmgMrz1WxWNEW3EmbBr9ieh9BJAsRGKFPLvotb4p4Aq79jddUVKPVJt7exVzLHcv777JVf", - "identifier": "e371d69b5dae6eacee832a130ee9f55545275a09", - "fingerprint": "e371d69b", - "address": "1MjcmArHeqorgm9uJi4kPNQ6CbsrmCtASH", - "index": 2147483648, - "depth": 5 - } - ] - }, - { - "network": "litecoin", - "master": { - "seed": "000102030405060708090a0b0c0d0e0f", - "wif": "TAroS5Knm8GZcnpPycBgzjwwDLWMyQjDrcuGPPoArgrbW7Ln22qp", - "pubKey": "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", - "chainCode": "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508", - "base58": "Ltub2SSUS19CirucWFod2ZsYA2J4v4U76YiCXHdcQttnoiy5aGanFHCPDBX7utfG6f95u1cUbZJNafmvzNCzZZJTw1EmyFoL8u1gJbGM8ipu491", - "base58Priv": "Ltpv71G8qDifUiNetP6nmxPA5STrUVmv2J9YSmXajv8VsYBUyuPhvN9xCaQrfX2wo5xxJNtEazYCFRUu5FmokYMM79pcqz8pcdo4rNXAFPgyB4k", - "identifier": "3442193e1bb70916e914552172cd4e2dbc9df811", - "fingerprint": "3442193e", - "address": "LPzGaoLUtXFkmNo3u1chDxGxDnSaBQTTxm" - }, - "children": [ - { - "path": "m/0'", - "m": 0, - "hardened": true, - "wif": "TB22qU2V9EJCVKJ8cdYaTfvDhnYcCzthcWgFm1k6hbvbKM1NLxoL", - "pubKey": "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56", - "chainCode": "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141", - "base58": "Ltub2UhtRiSfp82berwLEKkB34QBEt2TUdCDCu4WNzGumvAMwYsxfWjULKsXhADxqy3cuDu3TnqoKJr1xmB8Wb2qzthWAtbb4CutpXPuSU1YMgG", - "base58Priv": "Ltpv73XYpw28ZyVe2zEVyiFnxUZxoKLGQNdZ8NxUi1WcqjNmMBgtLbh3KimGSnPHCoLv1RmvxHs4dnKmo1oXQ8dXuDu8uroxrbVxZPA1gXboYvx", - "identifier": "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7", - "fingerprint": "5c1bd648", - "address": "LTcyn1jun6g9hvxtsT7cqMRSyix7AULC76", - "index": 2147483648, - "depth": 1 - } - ] - } - ], - "invalid": { - "fromBase58": [ - { - "exception": "Invalid checksum", - "string": "xprvQQQQQQQQQQQQQQQQCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334" - }, - { - "exception": "Invalid buffer length", - "network": "bitcoin", - "string": "HAsbc6CgKmTYEQg2CTz7m5STEPAB" - }, - { - "exception": "Invalid parent fingerprint", - "network": "bitcoin", - "string": "xprv9tnJFvAXAXPfPnMTKfwpwnkty7MzJwELVgp4NTBquaKXy4RndyfJJCJJf7zNaVpBpzrwVRutZNLRCVLEcZHcvuCNG3zGbGBcZn57FbNnmSP" - }, - { - "exception": "Invalid private key", - "network": "bitcoin", - "string": "xprv9s21ZrQH143K3yLysFvsu3n1dMwhNusmNHr7xArzAeCc7MQYqDBBStmqnZq6WLi668siBBNs3SjiyaexduHu9sXT9ixTsqptL67ADqcaBdm" - }, - { - "exception": "Invalid index", - "network": "bitcoin", - "string": "xprv9s21ZrQYdgnodnKW4Drm1Qg7poU6Gf2WUDsjPxvYiK7iLBMrsjbnF1wsZZQgmXNeMSG3s7jmHk1b3JrzhG5w8mwXGxqFxfrweico7k8DtxR" - }, - { - "exception": "Unknown network version", - "string": "1111111111111adADjFaSNPxwXqLjHLj4mBfYxuewDPbw9hEj1uaXCzMxRPXDFF3cUoezTFYom4sEmEVSQmENPPR315cFk9YUFVek73wE9" - }, - { - "exception": "Invalid network version", - "network": "bitcoin", - "string": "Ltpv73XYpw28ZyVe2zEVyiFnxUZxoKLGQNdZ8NxUi1WcqjNmMBgtLbh3KimGSnPHCoLv1RmvxHs4dnKmo1oXQ8dXuDu8uroxrbVxZPA1gXboYvx" - }, - { - "exception": "Invalid buffer length", - "string": "9XpNiB4DberdMn4jZiMhNGtuZUd7xUrCEGw4MG967zsVNvUKBEC9XLrmVmFasanWGp15zXfTNw4vW4KdvUAynEwyKjdho9QdLMPA2H5uyt" - }, - { - "exception": "Invalid buffer length", - "string": "7JJikZQ2NUXjSAnAF2SjFYE3KXbnnVxzRBNddFE1DjbDEHVGEJzYC7zqSgPoauBJS3cWmZwsER94oYSFrW9vZ4Ch5FtGeifdzmtS3FGYDB1vxFZsYKgMc" - }, - { - "exception": "Invalid parent fingerprint", - "string": "xpub67tVq9SuNQCfm2PXBqjGRAtNZ935kx2uHJaURePth4JBpMfEy6jum7Euj7FTpbs7fnjhfZcNEktCucWHcJf74dbKLKNSTZCQozdDVwvkJhs" - }, - { - "exception": "Invalid index", - "string": "xpub661MyMwTWkfYZq6BEh3ywGVXFvNj5hhzmWMhFBHSqmub31B1LZ9wbJ3DEYXZ8bHXGqnHKfepTud5a2XxGdnnePzZa2m2DyzTnFGBUXtaf9M" - }, - { - "exception": "Unknown network version", - "string": "8FH81Rao5EgGmdScoN66TJAHsQP7phEMeyMTku9NBJd7hXgaj3HTvSNjqJjoqBpxdbuushwPEM5otvxXt2p9dcw33AqNKzZEPMqGHmz7Dpayi6Vb" - }, - { - "exception": "Point is not on the curve", - "string": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gYymDsxxRe3WWeZQ7TadaLSdKUffezzczTCpB8j3JP96UwE2n6w1" - } - ], - "deriveHardened": [ - 2147483648, - null, - "foo", - -1 - ], - "derive": [ - 4294967296, - null, - "foo", - -1 - ], - "derivePath": [ - 2, - [ - 2, - 3, - 4 - ], - "/", - "m/m/123", - "a/0/1/2", - "m/0/ 1 /2", - "m/0/1.5/2" - ] - } -} diff --git a/test/hdnode.js b/test/hdnode.js deleted file mode 100644 index 42a2d14..0000000 --- a/test/hdnode.js +++ /dev/null @@ -1,397 +0,0 @@ -/* global describe, it, beforeEach */ -/* eslint-disable no-new */ - -var assert = require('assert') -var ecdsa = require('../src/ecdsa') -var hoodwink = require('hoodwink') - -var BigInteger = require('bigi') -var ECPair = require('../src/ecpair') -var HDNode = require('../src/hdnode') - -var fixtures = require('./fixtures/hdnode.json') -var curve = ecdsa.__curve - -var NETWORKS = require('../src/networks') -var NETWORKS_LIST = [] // Object.values(NETWORKS) -for (var networkName in NETWORKS) { - NETWORKS_LIST.push(NETWORKS[networkName]) -} - -var validAll = [] -fixtures.valid.forEach(function (f) { - function addNetwork (n) { - n.network = f.network - return n - } - - validAll = validAll.concat(addNetwork(f.master), f.children.map(addNetwork)) -}) - -describe('HDNode', function () { - describe('Constructor', function () { - var keyPair, chainCode - - beforeEach(function () { - var d = BigInteger.ONE - - keyPair = new ECPair(d, null) - chainCode = Buffer.alloc(32, 1) - }) - - it('stores the keyPair/chainCode directly', function () { - var hd = new HDNode(keyPair, chainCode) - - assert.strictEqual(hd.keyPair, keyPair) - assert.strictEqual(hd.chainCode, chainCode) - }) - - it('has a default depth/index of 0', function () { - var hd = new HDNode(keyPair, chainCode) - - assert.strictEqual(hd.depth, 0) - assert.strictEqual(hd.index, 0) - }) - - it('throws on uncompressed keyPair', function () { - keyPair.compressed = false - - assert.throws(function () { - new HDNode(keyPair, chainCode) - }, /BIP32 only allows compressed keyPairs/) - }) - - it('throws when an invalid length chain code is given', function () { - assert.throws(function () { - new HDNode(keyPair, Buffer.alloc(20)) - }, /Expected property "1" of type Buffer\(Length: 32\), got Buffer\(Length: 20\)/) - }) - }) - - describe('fromSeed*', function () { - fixtures.valid.forEach(function (f) { - it('calculates privKey and chainCode for ' + f.master.fingerprint, function () { - var network = NETWORKS[f.network] - var hd = HDNode.fromSeedHex(f.master.seed, network) - - assert.strictEqual(hd.keyPair.toWIF(), f.master.wif) - assert.strictEqual(hd.chainCode.toString('hex'), f.master.chainCode) - }) - }) - - it('throws if IL is not within interval [1, n - 1] | IL === 0', hoodwink(function () { - this.mock(BigInteger, 'fromBuffer', function () { - return BigInteger.ZERO - }, 1) - - assert.throws(function () { - HDNode.fromSeedHex('ffffffffffffffffffffffffffffffff') - }, /Private key must be greater than 0/) - })) - - it('throws if IL is not within interval [1, n - 1] | IL === n', hoodwink(function () { - this.mock(BigInteger, 'fromBuffer', function () { - return curve.n - }, 1) - - assert.throws(function () { - HDNode.fromSeedHex('ffffffffffffffffffffffffffffffff') - }, /Private key must be less than the curve order/) - })) - - it('throws on low entropy seed', function () { - assert.throws(function () { - HDNode.fromSeedHex('ffffffffff') - }, /Seed should be at least 128 bits/) - }) - - it('throws on too high entropy seed', function () { - assert.throws(function () { - HDNode.fromSeedHex('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') - }, /Seed should be at most 512 bits/) - }) - }) - - describe('ECPair wrappers', function () { - var keyPair, hd, hash - - beforeEach(function () { - keyPair = ECPair.makeRandom() - hash = Buffer.alloc(32) - - var chainCode = Buffer.alloc(32) - hd = new HDNode(keyPair, chainCode) - }) - - describe('getAddress', function () { - it('wraps keyPair.getAddress', hoodwink(function () { - this.mock(hd.keyPair, 'getAddress', function () { - return 'foo' - }, 1) - - assert.strictEqual(hd.getAddress(), 'foo') - })) - }) - - describe('getNetwork', function () { - it('wraps keyPair.getNetwork', hoodwink(function () { - this.mock(hd.keyPair, 'getNetwork', function () { - return 'foo' - }, 1) - - assert.strictEqual(hd.getNetwork(), 'foo') - })) - }) - - describe('getPublicKeyBuffer', function () { - it('wraps keyPair.getPublicKeyBuffer', hoodwink(function () { - this.mock(hd.keyPair, 'getPublicKeyBuffer', function () { - return 'foo' - }, 1) - - assert.strictEqual(hd.getPublicKeyBuffer(), 'foo') - })) - }) - - describe('sign', function () { - it('wraps keyPair.sign', hoodwink(function () { - this.mock(hd.keyPair, 'sign', function (h) { - assert.strictEqual(hash, h) - return 'foo' - }, 1) - - assert.strictEqual(hd.sign(hash), 'foo') - })) - }) - - describe('verify', function () { - var signature - - beforeEach(function () { - signature = hd.sign(hash) - }) - - it('wraps keyPair.verify', hoodwink(function () { - this.mock(hd.keyPair, 'verify', function (h, s) { - assert.strictEqual(hash, h) - assert.strictEqual(signature, s) - return 'foo' - }, 1) - - assert.strictEqual(hd.verify(hash, signature), 'foo') - })) - }) - }) - - describe('fromBase58 / toBase58', function () { - validAll.forEach(function (f) { - it('exports ' + f.base58 + ' (public) correctly', function () { - var hd = HDNode.fromBase58(f.base58, NETWORKS_LIST) - - assert.strictEqual(hd.toBase58(), f.base58) - assert.throws(function () { hd.keyPair.toWIF() }, /Missing private key/) - }) - }) - - validAll.forEach(function (f) { - it('exports ' + f.base58Priv + ' (private) correctly', function () { - var hd = HDNode.fromBase58(f.base58Priv, NETWORKS_LIST) - - assert.strictEqual(hd.toBase58(), f.base58Priv) - assert.strictEqual(hd.keyPair.toWIF(), f.wif) - }) - }) - - fixtures.invalid.fromBase58.forEach(function (f) { - it('throws on ' + f.string, function () { - assert.throws(function () { - var networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST - - HDNode.fromBase58(f.string, networks) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('getIdentifier', function () { - validAll.forEach(function (f) { - it('returns the identifier for ' + f.fingerprint, function () { - var hd = HDNode.fromBase58(f.base58, NETWORKS_LIST) - - assert.strictEqual(hd.getIdentifier().toString('hex'), f.identifier) - }) - }) - }) - - describe('getFingerprint', function () { - validAll.forEach(function (f) { - it('returns the fingerprint for ' + f.fingerprint, function () { - var hd = HDNode.fromBase58(f.base58, NETWORKS_LIST) - - assert.strictEqual(hd.getFingerprint().toString('hex'), f.fingerprint) - }) - }) - }) - - describe('neutered / isNeutered', function () { - validAll.forEach(function (f) { - it('drops the private key for ' + f.fingerprint, function () { - var hd = HDNode.fromBase58(f.base58Priv, NETWORKS_LIST) - var hdn = hd.neutered() - - assert.notEqual(hdn.keyPair, hd.keyPair) - assert.throws(function () { hdn.keyPair.toWIF() }, /Missing private key/) - assert.strictEqual(hdn.toBase58(), f.base58) - assert.strictEqual(hdn.chainCode, hd.chainCode) - assert.strictEqual(hdn.depth, f.depth >>> 0) - assert.strictEqual(hdn.index, f.index >>> 0) - assert.strictEqual(hdn.isNeutered(), true) - - // does not modify the original - assert.strictEqual(hd.toBase58(), f.base58Priv) - assert.strictEqual(hd.isNeutered(), false) - }) - }) - }) - - describe('derive', function () { - function verifyVector (hd, v) { - if (hd.isNeutered()) { - assert.strictEqual(hd.toBase58(), v.base58) - } else { - assert.strictEqual(hd.neutered().toBase58(), v.base58) - assert.strictEqual(hd.toBase58(), v.base58Priv) - } - - assert.strictEqual(hd.getFingerprint().toString('hex'), v.fingerprint) - assert.strictEqual(hd.getIdentifier().toString('hex'), v.identifier) - assert.strictEqual(hd.getAddress(), v.address) - assert.strictEqual(hd.keyPair.toWIF(), v.wif) - assert.strictEqual(hd.keyPair.getPublicKeyBuffer().toString('hex'), v.pubKey) - assert.strictEqual(hd.chainCode.toString('hex'), v.chainCode) - assert.strictEqual(hd.depth, v.depth >>> 0) - assert.strictEqual(hd.index, v.index >>> 0) - } - - fixtures.valid.forEach(function (f) { - var network = NETWORKS[f.network] - var hd = HDNode.fromSeedHex(f.master.seed, network) - var master = hd - - // testing deriving path from master - f.children.forEach(function (c) { - it(c.path + ' from ' + f.master.fingerprint + ' by path', function () { - var child = master.derivePath(c.path) - var childNoM = master.derivePath(c.path.slice(2)) // no m/ on path - - verifyVector(child, c) - verifyVector(childNoM, c) - }) - }) - - // testing deriving path from children - f.children.forEach(function (c, i) { - var cn = master.derivePath(c.path) - - f.children.slice(i + 1).forEach(function (cc) { - it(cc.path + ' from ' + c.fingerprint + ' by path', function () { - var ipath = cc.path.slice(2).split('/').slice(i + 1).join('/') - var child = cn.derivePath(ipath) - verifyVector(child, cc) - - assert.throws(function () { - cn.derivePath('m/' + ipath) - }, /Not a master node/) - }) - }) - }) - - // FIXME: test data is only testing Private -> private for now - f.children.forEach(function (c) { - if (c.m === undefined) return - - it(c.path + ' from ' + f.master.fingerprint, function () { - if (c.hardened) { - hd = hd.deriveHardened(c.m) - } else { - hd = hd.derive(c.m) - } - - verifyVector(hd, c) - }) - }) - }) - - it('works for Private -> public (neutered)', function () { - var f = fixtures.valid[1] - var c = f.children[0] - - var master = HDNode.fromBase58(f.master.base58Priv, NETWORKS_LIST) - var child = master.derive(c.m).neutered() - - assert.strictEqual(child.toBase58(), c.base58) - }) - - it('works for Private -> public (neutered, hardened)', function () { - var f = fixtures.valid[0] - var c = f.children[0] - - var master = HDNode.fromBase58(f.master.base58Priv, NETWORKS_LIST) - var child = master.deriveHardened(c.m).neutered() - - assert.strictEqual(c.base58, child.toBase58()) - }) - - it('works for Public -> public', function () { - var f = fixtures.valid[1] - var c = f.children[0] - - var master = HDNode.fromBase58(f.master.base58, NETWORKS_LIST) - var child = master.derive(c.m) - - assert.strictEqual(c.base58, child.toBase58()) - }) - - it('throws on Public -> public (hardened)', function () { - var f = fixtures.valid[0] - var c = f.children[0] - - var master = HDNode.fromBase58(f.master.base58, NETWORKS_LIST) - - assert.throws(function () { - master.deriveHardened(c.m) - }, /Could not derive hardened child key/) - }) - - it('throws on wrong types', function () { - var f = fixtures.valid[0] - var master = HDNode.fromBase58(f.master.base58, NETWORKS_LIST) - - fixtures.invalid.derive.forEach(function (fx) { - assert.throws(function () { - master.derive(fx) - }, /Expected UInt32/) - }) - - fixtures.invalid.deriveHardened.forEach(function (fx) { - assert.throws(function () { - master.deriveHardened(fx) - }, /Expected UInt31/) - }) - - fixtures.invalid.derivePath.forEach(function (fx) { - assert.throws(function () { - master.derivePath(fx) - }, /Expected BIP32 derivation path/) - }) - }) - - it('works when private key has leading zeros', function () { - var key = 'xprv9s21ZrQH143K3ckY9DgU79uMTJkQRLdbCCVDh81SnxTgPzLLGax6uHeBULTtaEtcAvKjXfT7ZWtHzKjTpujMkUd9dDb8msDeAfnJxrgAYhr' - var hdkey = HDNode.fromBase58(key) - assert.strictEqual(hdkey.keyPair.d.toBuffer(32).toString('hex'), '00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd') - var child = hdkey.derivePath('m/44\'/0\'/0\'/0/0\'') - assert.strictEqual(child.keyPair.d.toBuffer().toString('hex'), '3348069561d2a0fb925e74bf198762acc47dce7db27372257d2d959a9e6f8aeb') - }) - }) -}) diff --git a/test/integration/bip32.js b/test/integration/bip32.js index 1908166..b7f0bb2 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -1,32 +1,39 @@ /* global describe, it */ var assert = require('assert') +let bip32 = require('bip32') var bip39 = require('bip39') var bitcoin = require('../../') +var baddress = bitcoin.address +var bcrypto = bitcoin.crypto +function getAddress (node) { + return baddress.toBase58Check(bcrypto.hash160(node.publicKey), bitcoin.networks.bitcoin.pubKeyHash) +} + describe('bitcoinjs-lib (BIP32)', function () { it('can import a BIP32 testnet xpriv and export to WIF', function () { var xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK' - var node = bitcoin.HDNode.fromBase58(xpriv, bitcoin.networks.testnet) + var node = bip32.fromBase58(xpriv, bitcoin.networks.testnet) - assert.equal(node.keyPair.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') + assert.equal(node.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') }) it('can export a BIP32 xpriv, then import it', function () { var mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' var seed = bip39.mnemonicToSeed(mnemonic) - var node = bitcoin.HDNode.fromSeedBuffer(seed) + var node = bip32.fromSeed(seed) var string = node.toBase58() - var restored = bitcoin.HDNode.fromBase58(string) + var restored = bip32.fromBase58(string) - assert.equal(node.getAddress(), restored.getAddress()) // same public key - assert.equal(node.keyPair.toWIF(), restored.keyPair.toWIF()) // same private key + assert.equal(getAddress(node), getAddress(restored)) // same public key + assert.equal(node.toWIF(), restored.toWIF()) // same private key }) it('can export a BIP32 xpub', function () { var mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' var seed = bip39.mnemonicToSeed(mnemonic) - var node = bitcoin.HDNode.fromSeedBuffer(seed) + var node = bip32.fromSeed(seed) var string = node.neutered().toBase58() assert.equal(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n') @@ -34,7 +41,7 @@ describe('bitcoinjs-lib (BIP32)', function () { it('can create a BIP32, bitcoin, account 0, external address', function () { var path = "m/0'/0/0" - var root = bitcoin.HDNode.fromSeedHex('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd') + var root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) var child1 = root.derivePath(path) @@ -43,12 +50,12 @@ describe('bitcoinjs-lib (BIP32)', function () { .derive(0) .derive(0) - assert.equal(child1.getAddress(), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') - assert.equal(child1b.getAddress(), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') + assert.equal(getAddress(child1), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') + assert.equal(getAddress(child1b), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') }) it('can create a BIP44, bitcoin, account 0, external address', function () { - var root = bitcoin.HDNode.fromSeedHex('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd') + var root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) var child1 = root.derivePath("m/44'/0'/0'/0/0") @@ -59,19 +66,19 @@ describe('bitcoinjs-lib (BIP32)', function () { .derive(0) .derive(0) - assert.equal(child1.getAddress(), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') - assert.equal(child1b.getAddress(), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') + assert.equal(getAddress(child1), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') + assert.equal(getAddress(child1b), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') }) it('can create a BIP49, bitcoin testnet, account 0, external address', function () { var mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' var seed = bip39.mnemonicToSeed(mnemonic) - var root = bitcoin.HDNode.fromSeedBuffer(seed) + var root = bip32.fromSeed(seed) var path = "m/49'/1'/0'/0/0" var child = root.derivePath(path) - var keyhash = bitcoin.crypto.hash160(child.getPublicKeyBuffer()) + var keyhash = bitcoin.crypto.hash160(child.publicKey) var scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash) var addressBytes = bitcoin.crypto.hash160(scriptSig) var outputScript = bitcoin.script.scriptHash.output.encode(addressBytes) @@ -86,14 +93,14 @@ describe('bitcoinjs-lib (BIP32)', function () { assert(bip39.validateMnemonic(mnemonic)) var seed = bip39.mnemonicToSeed(mnemonic) - var root = bitcoin.HDNode.fromSeedBuffer(seed) + var root = bip32.fromSeed(seed) // receive addresses - assert.strictEqual(root.derivePath("m/0'/0/0").getAddress(), '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC') - assert.strictEqual(root.derivePath("m/0'/0/1").getAddress(), '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL') + assert.strictEqual(getAddress(root.derivePath("m/0'/0/0")), '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC') + assert.strictEqual(getAddress(root.derivePath("m/0'/0/1")), '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL') // change addresses - assert.strictEqual(root.derivePath("m/0'/1/0").getAddress(), '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn') - assert.strictEqual(root.derivePath("m/0'/1/1").getAddress(), '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6') + assert.strictEqual(getAddress(root.derivePath("m/0'/1/0")), '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn') + assert.strictEqual(getAddress(root.derivePath("m/0'/1/1")), '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6') }) }) diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 183c50e..4b53769 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -3,7 +3,9 @@ var assert = require('assert') var bigi = require('bigi') var bitcoin = require('../../') +var bip32 = require('bip32') var crypto = require('crypto') +var tinysecp = require('tiny-secp256k1') var ecurve = require('ecurve') var secp256k1 = ecurve.getCurveByName('secp256k1') @@ -68,35 +70,31 @@ describe('bitcoinjs-lib (crypto)', function () { it('can recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key', function () { function recoverParent (master, child) { - assert(!master.keyPair.d, 'You already have the parent private key') - assert(child.keyPair.d, 'Missing child private key') + assert(master.isNeutered(), 'You already have the parent private key') + assert(!child.isNeutered(), 'Missing child private key') - var curve = secp256k1 - var QP = master.keyPair.Q - var serQP = master.keyPair.getPublicKeyBuffer() - - var d1 = child.keyPair.d + var serQP = master.publicKey + var d1 = child.privateKey var d2 var data = Buffer.alloc(37) serQP.copy(data, 0) // search index space until we find it - for (var i = 0; i < bitcoin.HDNode.HIGHEST_BIT; ++i) { + for (var i = 0; i < 0x80000000; ++i) { data.writeUInt32BE(i, 33) // calculate I var I = crypto.createHmac('sha512', master.chainCode).update(data).digest() var IL = I.slice(0, 32) - var pIL = bigi.fromBuffer(IL) - // See hdnode.js:273 to understand - d2 = d1.subtract(pIL).mod(curve.n) + // See bip32.js:273 to understand + d2 = tinysecp.privateSub(d1, IL) - var Qp = new bitcoin.ECPair(d2).Q - if (Qp.equals(QP)) break + var Qp = bip32.fromPrivateKey(d2, Buffer.alloc(32, 0)).publicKey + if (Qp.equals(serQP)) break } - var node = new bitcoin.HDNode(new bitcoin.ECPair(d2), master.chainCode, master.network) + var node = bip32.fromPrivateKey(d2, master.chainCode, master.network) node.depth = master.depth node.index = master.index node.masterFingerprint = master.masterFingerprint @@ -104,7 +102,7 @@ describe('bitcoinjs-lib (crypto)', function () { } var seed = crypto.randomBytes(32) - var master = bitcoin.HDNode.fromSeedBuffer(seed) + var master = bip32.fromSeed(seed) var child = master.derive(6) // m/6 // now for the recovery