Browse Source

rm HDNode, use bip32 module

addLowRGrinding
Daniel Cousens 7 years ago
parent
commit
884f3fd57d
  1. 3
      README.md
  2. 3
      package.json
  3. 316
      src/hdnode.js
  4. 9
      src/index.js
  5. 327
      test/fixtures/hdnode.json
  6. 397
      test/hdnode.js
  7. 47
      test/integration/bip32.js
  8. 28
      test/integration/crypto.js

3
README.md

@ -86,9 +86,6 @@ npm install @types/bitcoinjs-lib
``` ```
You can now use `bitcoinjs-lib` as a typescript compliant library. 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). 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).

3
package.json

@ -32,6 +32,7 @@
"dependencies": { "dependencies": {
"bech32": "^1.1.2", "bech32": "^1.1.2",
"bigi": "^1.4.0", "bigi": "^1.4.0",
"bip32": "0.0.3",
"bip66": "^1.1.0", "bip66": "^1.1.0",
"bitcoin-ops": "^1.3.0", "bitcoin-ops": "^1.3.0",
"bs58check": "^2.0.0", "bs58check": "^2.0.0",
@ -42,11 +43,13 @@
"pushdata-bitcoin": "^1.0.1", "pushdata-bitcoin": "^1.0.1",
"randombytes": "^2.0.1", "randombytes": "^2.0.1",
"safe-buffer": "^5.1.1", "safe-buffer": "^5.1.1",
"tiny-secp256k1": "0.0.5",
"typeforce": "^1.11.3", "typeforce": "^1.11.3",
"varuint-bitcoin": "^1.0.4", "varuint-bitcoin": "^1.0.4",
"wif": "^2.0.1" "wif": "^2.0.1"
}, },
"devDependencies": { "devDependencies": {
"bigi": "^1.4.2",
"bip39": "^2.3.0", "bip39": "^2.3.0",
"bip65": "^1.0.1", "bip65": "^1.0.1",
"bs58": "^4.0.0", "bs58": "^4.0.0",

316
src/hdnode.js

@ -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

9
src/index.js

@ -1,18 +1,17 @@
var script = require('./script') let script = require('./script')
let templates = require('./templates')
var templates = require('./templates') for (let key in templates) {
for (var key in templates) {
script[key] = templates[key] script[key] = templates[key]
} }
module.exports = { module.exports = {
Block: require('./block'), Block: require('./block'),
ECPair: require('./ecpair'), ECPair: require('./ecpair'),
HDNode: require('./hdnode'),
Transaction: require('./transaction'), Transaction: require('./transaction'),
TransactionBuilder: require('./transaction_builder'), TransactionBuilder: require('./transaction_builder'),
address: require('./address'), address: require('./address'),
bip32: require('bip32'),
crypto: require('./crypto'), crypto: require('./crypto'),
networks: require('./networks'), networks: require('./networks'),
opcodes: require('bitcoin-ops'), opcodes: require('bitcoin-ops'),

327
test/fixtures/hdnode.json

@ -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"
]
}
}

397
test/hdnode.js

@ -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')
})
})
})

47
test/integration/bip32.js

@ -1,32 +1,39 @@
/* global describe, it */ /* global describe, it */
var assert = require('assert') var assert = require('assert')
let bip32 = require('bip32')
var bip39 = require('bip39') var bip39 = require('bip39')
var bitcoin = require('../../') 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 () { describe('bitcoinjs-lib (BIP32)', function () {
it('can import a BIP32 testnet xpriv and export to WIF', function () { it('can import a BIP32 testnet xpriv and export to WIF', function () {
var xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK' 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 () { 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 mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost'
var seed = bip39.mnemonicToSeed(mnemonic) var seed = bip39.mnemonicToSeed(mnemonic)
var node = bitcoin.HDNode.fromSeedBuffer(seed) var node = bip32.fromSeed(seed)
var string = node.toBase58() 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(getAddress(node), getAddress(restored)) // same public key
assert.equal(node.keyPair.toWIF(), restored.keyPair.toWIF()) // same private key assert.equal(node.toWIF(), restored.toWIF()) // same private key
}) })
it('can export a BIP32 xpub', function () { it('can export a BIP32 xpub', function () {
var mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' var mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost'
var seed = bip39.mnemonicToSeed(mnemonic) var seed = bip39.mnemonicToSeed(mnemonic)
var node = bitcoin.HDNode.fromSeedBuffer(seed) var node = bip32.fromSeed(seed)
var string = node.neutered().toBase58() var string = node.neutered().toBase58()
assert.equal(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n') assert.equal(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n')
@ -34,7 +41,7 @@ describe('bitcoinjs-lib (BIP32)', function () {
it('can create a BIP32, bitcoin, account 0, external address', function () { it('can create a BIP32, bitcoin, account 0, external address', function () {
var path = "m/0'/0/0" 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) var child1 = root.derivePath(path)
@ -43,12 +50,12 @@ describe('bitcoinjs-lib (BIP32)', function () {
.derive(0) .derive(0)
.derive(0) .derive(0)
assert.equal(child1.getAddress(), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') assert.equal(getAddress(child1), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7')
assert.equal(child1b.getAddress(), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') assert.equal(getAddress(child1b), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7')
}) })
it('can create a BIP44, bitcoin, account 0, external address', function () { 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") var child1 = root.derivePath("m/44'/0'/0'/0/0")
@ -59,19 +66,19 @@ describe('bitcoinjs-lib (BIP32)', function () {
.derive(0) .derive(0)
.derive(0) .derive(0)
assert.equal(child1.getAddress(), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') assert.equal(getAddress(child1), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au')
assert.equal(child1b.getAddress(), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') assert.equal(getAddress(child1b), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au')
}) })
it('can create a BIP49, bitcoin testnet, account 0, external address', function () { 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 mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
var seed = bip39.mnemonicToSeed(mnemonic) 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 path = "m/49'/1'/0'/0/0"
var child = root.derivePath(path) 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 scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash)
var addressBytes = bitcoin.crypto.hash160(scriptSig) var addressBytes = bitcoin.crypto.hash160(scriptSig)
var outputScript = bitcoin.script.scriptHash.output.encode(addressBytes) var outputScript = bitcoin.script.scriptHash.output.encode(addressBytes)
@ -86,14 +93,14 @@ describe('bitcoinjs-lib (BIP32)', function () {
assert(bip39.validateMnemonic(mnemonic)) assert(bip39.validateMnemonic(mnemonic))
var seed = bip39.mnemonicToSeed(mnemonic) var seed = bip39.mnemonicToSeed(mnemonic)
var root = bitcoin.HDNode.fromSeedBuffer(seed) var root = bip32.fromSeed(seed)
// receive addresses // receive addresses
assert.strictEqual(root.derivePath("m/0'/0/0").getAddress(), '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC') assert.strictEqual(getAddress(root.derivePath("m/0'/0/0")), '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC')
assert.strictEqual(root.derivePath("m/0'/0/1").getAddress(), '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL') assert.strictEqual(getAddress(root.derivePath("m/0'/0/1")), '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL')
// change addresses // change addresses
assert.strictEqual(root.derivePath("m/0'/1/0").getAddress(), '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn') assert.strictEqual(getAddress(root.derivePath("m/0'/1/0")), '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn')
assert.strictEqual(root.derivePath("m/0'/1/1").getAddress(), '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6') assert.strictEqual(getAddress(root.derivePath("m/0'/1/1")), '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6')
}) })
}) })

28
test/integration/crypto.js

@ -3,7 +3,9 @@
var assert = require('assert') var assert = require('assert')
var bigi = require('bigi') var bigi = require('bigi')
var bitcoin = require('../../') var bitcoin = require('../../')
var bip32 = require('bip32')
var crypto = require('crypto') var crypto = require('crypto')
var tinysecp = require('tiny-secp256k1')
var ecurve = require('ecurve') var ecurve = require('ecurve')
var secp256k1 = ecurve.getCurveByName('secp256k1') 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 () { 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) { function recoverParent (master, child) {
assert(!master.keyPair.d, 'You already have the parent private key') assert(master.isNeutered(), 'You already have the parent private key')
assert(child.keyPair.d, 'Missing child private key') assert(!child.isNeutered(), 'Missing child private key')
var curve = secp256k1 var serQP = master.publicKey
var QP = master.keyPair.Q var d1 = child.privateKey
var serQP = master.keyPair.getPublicKeyBuffer()
var d1 = child.keyPair.d
var d2 var d2
var data = Buffer.alloc(37) var data = Buffer.alloc(37)
serQP.copy(data, 0) serQP.copy(data, 0)
// search index space until we find it // 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) data.writeUInt32BE(i, 33)
// calculate I // calculate I
var I = crypto.createHmac('sha512', master.chainCode).update(data).digest() var I = crypto.createHmac('sha512', master.chainCode).update(data).digest()
var IL = I.slice(0, 32) var IL = I.slice(0, 32)
var pIL = bigi.fromBuffer(IL)
// See hdnode.js:273 to understand // See bip32.js:273 to understand
d2 = d1.subtract(pIL).mod(curve.n) d2 = tinysecp.privateSub(d1, IL)
var Qp = new bitcoin.ECPair(d2).Q var Qp = bip32.fromPrivateKey(d2, Buffer.alloc(32, 0)).publicKey
if (Qp.equals(QP)) break 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.depth = master.depth
node.index = master.index node.index = master.index
node.masterFingerprint = master.masterFingerprint node.masterFingerprint = master.masterFingerprint
@ -104,7 +102,7 @@ describe('bitcoinjs-lib (crypto)', function () {
} }
var seed = crypto.randomBytes(32) var seed = crypto.randomBytes(32)
var master = bitcoin.HDNode.fromSeedBuffer(seed) var master = bip32.fromSeed(seed)
var child = master.derive(6) // m/6 var child = master.derive(6) // m/6
// now for the recovery // now for the recovery

Loading…
Cancel
Save