Daniel Cousens
7 years ago
committed by
GitHub
8 changed files with 47 additions and 1083 deletions
@ -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 |
@ -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" |
|||
] |
|||
} |
|||
} |
@ -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') |
|||
}) |
|||
}) |
|||
}) |
Loading…
Reference in new issue