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.
``` 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).

3
package.json

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

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

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

28
test/integration/crypto.js

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

Loading…
Cancel
Save