rm HDNode, use bip32 module
This commit is contained in:
parent
1bd10dc979
commit
884f3fd57d
8 changed files with 47 additions and 1083 deletions
|
@ -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).
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
316
src/hdnode.js
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
|
|
@ -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'),
|
||||
|
|
327
test/fixtures/hdnode.json
vendored
327
test/fixtures/hdnode.json
vendored
|
@ -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
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')
|
||||
})
|
||||
})
|
||||
})
|
|
@ -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')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue