UNPKG

@dashevo/dashcore-lib

Version:

A pure and powerful JavaScript Dash library.

1,011 lines (918 loc) 32.1 kB
/* eslint-disable */ // TODO: Remove previous line and work through linting issues at next edit 'use strict'; var assert = require('assert'); var _ = require('lodash'); var $ = require('./util/preconditions'); var BN = require('./crypto/bn'); var Base58 = require('./encoding/base58'); var Base58Check = require('./encoding/base58check'); var Hash = require('./crypto/hash'); var Network = require('./networks'); var Point = require('./crypto/point'); var PrivateKey = require('./privatekey'); var Random = require('./crypto/random'); var errors = require('./errors'); var hdErrors = errors.HDPrivateKey; var BufferUtil = require('./util/buffer'); var JSUtil = require('./util/js'); var MINIMUM_ENTROPY_BITS = 128; var BITS_TO_BYTES = 1 / 8; var MAXIMUM_ENTROPY_BITS = 512; /** * Represents an instance of an hierarchically derived private key. * * More info on https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki * * @constructor * @param {string|Buffer|Object} arg */ function HDPrivateKey(arg) { /* jshint maxcomplexity: 10 */ if (arg instanceof HDPrivateKey) { return arg; } if (!(this instanceof HDPrivateKey)) { return new HDPrivateKey(arg); } if (!arg) { return this._generateRandomly(); } if (Network.get(arg)) { return this._generateRandomly(arg); } else if (_.isString(arg) || BufferUtil.isBuffer(arg)) { if (HDPrivateKey.isValidSerialized(arg)) { this._buildFromSerialized(arg); } else if (JSUtil.isValidJSON(arg)) { this._buildFromJSON(arg); } else if ( BufferUtil.isBuffer(arg) && HDPrivateKey.isValidSerialized(arg.toString()) ) { this._buildFromSerialized(arg.toString()); } else { throw HDPrivateKey.getSerializedError(arg); } } else if (_.isObject(arg)) { this._buildFromObject(arg); } else { throw new hdErrors.UnrecognizedArgument(arg); } } /** * Verifies that a given path is valid. * * @param {string|number} arg * @param {boolean?} hardened * @return {boolean} */ HDPrivateKey.isValidPath = function (arg, hardened) { if (_.isString(arg)) { var indexes = HDPrivateKey._getDerivationIndexes(arg); return indexes !== null && _.every(indexes.map(index=>index.value), HDPrivateKey.isValidPath); } if (_.isNumber(arg)) { if (arg < HDPrivateKey.Hardened && hardened === true) { arg += HDPrivateKey.Hardened; } return arg >= 0 && arg < HDPrivateKey.MaxIndex; } if (BN.isBN(arg)) { return arg.gte(HDPrivateKey.MaxIndex32bit) && arg.lt(HDPrivateKey.MaxIndex256bit) } return false; }; /** * Internal function that splits a string path into a derivation index array. * It will return null if the string path is malformed. * It does not validate if indexes are in bounds. * * @param {string} path * @return {Array} */ HDPrivateKey._getDerivationIndexes = function (path) { var steps = path.split('/'); // Special cases: if (_.includes(HDPrivateKey.RootElementAlias, path)) { return []; } if (!_.includes(HDPrivateKey.RootElementAlias, steps[0])) { return null; } var indexes = steps.slice(1).map(function (step) { var isHardened = step.slice(-1) === '\''; if (isHardened) { step = step.slice(0, -1); } if (!step || step[0] === '-') { return {value: NaN, hardened: undefined}; } var nIndexValue // DIP 14 256bit Mode if (HDPrivateKey._isIndex256bit(step)) { nIndexValue = HDPrivateKey._IndexArgToBN(step) return { value: nIndexValue, hardened: isHardened } } // BIP32 Compatibility Mode else { var indexValue = HDPrivateKey._IndexArgToNumber(step, {castStringToNumber: true}) if (isHardened) { indexValue += HDPrivateKey.Hardened; } return {value: indexValue, hardened: null} // index is already hardened according to BIP32 } }); return _.some(indexes.map(index=>index.value), isNaN) ? null : indexes; }; /** * WARNING: This method is deprecated. Use deriveChild or deriveNonCompliantChild instead. This is not BIP32 compliant * * * Get a derived child based on a string or number. * * If the first argument is a string, it's parsed as the full path of * derivation. Valid values for this argument include "m" (which returns the * same private key), "m/0/1/40/2'/1000", where the ' quote means a hardened * derivation. * * If the first argument is a number, the child with that index will be * derived. If the second argument is truthy, the hardened version will be * derived. See the example usage for clarification. * * @example * ```javascript * var parent = new HDPrivateKey('xprv...'); * var child_0_1_2h = parent.derive(0).derive(1).derive(2, true); * var copy_of_child_0_1_2h = parent.derive("m/0/1/2'"); * assert(child_0_1_2h.xprivkey === copy_of_child_0_1_2h); * ``` * * @param {string|number} arg * @param {boolean?} hardened * @return HDPrivateKey */ HDPrivateKey.prototype.derive = function (arg, hardened) { return this.deriveNonCompliantChild(arg, hardened); }; /** * WARNING: This method will not be officially supported until v1.0.0. * * * Get a derived child based on a string or number. * * If the first argument is a string, it's parsed as the full path of * derivation. Valid values for this argument include "m" (which returns the * same private key), "m/0/1/40/2'/1000", where the ' quote means a hardened * derivation. * * If the first argument is a number, the child with that index will be * derived. If the second argument is truthy, the hardened version will be * derived. See the example usage for clarification. * * WARNING: The `nonCompliant` option should NOT be used, except for older implementation * that used a derivation strategy that used a non-zero padded private key. * * @example * ```javascript * var parent = new HDPrivateKey('xprv...'); * var child_0_1_2h = parent.deriveChild(0).deriveChild(1).deriveChild(2, true); * var copy_of_child_0_1_2h = parent.deriveChild("m/0/1/2'"); * assert(child_0_1_2h.xprivkey === copy_of_child_0_1_2h); * ``` * * @param {string|number} arg - string can be a path or a DIP14 256-bit hex, number is only used for BIP32 compatibility * @param {boolean?} hardened * @return HDPrivateKey */ HDPrivateKey.prototype.deriveChild = function (arg, hardened) { // DIP14 256-bit Mode if (HDPrivateKey._isIndex256bit(arg)) { return this._deriveWithBigNumber(HDPrivateKey._IndexArgToBN(arg), hardened); } // BIP32 Compatibility Mode else if (!_.isNaN(HDPrivateKey._IndexArgToNumber(arg, {castStringToNumber: false}))) { return this._deriveWithNumber(HDPrivateKey._IndexArgToNumber(arg, {castStringToNumber: false}), hardened); } // Mixed BIP32 and DIP14 path else if (_.isString(arg)) { return this._deriveFromString(arg); } else { throw new hdErrors.InvalidDerivationArgument(arg); } }; /** * WARNING: This method will not be officially supported until v1.0.0 * * * WARNING: If this is a new implementation you should NOT use this method, you should be using * `derive` instead. * * This method is explicitly for use and compatibility with an implementation that * was not compliant with BIP32 regarding the derivation algorithm. The private key * must be 32 bytes hashing, and this implementation will use the non-zero padded * serialization of a private key, such that it's still possible to derive the privateKey * to recover those funds. * * @param {string|number} arg * @param {boolean?} hardened */ HDPrivateKey.prototype.deriveNonCompliantChild = function (arg, hardened) { if (_.isNumber(arg)) { return this._deriveWithNumber(arg, hardened, true); } else if (_.isString(arg)) { return this._deriveFromString(arg, true); } else { throw new hdErrors.InvalidDerivationArgument(arg); } }; HDPrivateKey.prototype._deriveWithNumber = function ( index, hardened, nonCompliant ) { /* jshint maxstatements: 20 */ /* jshint maxcomplexity: 10 */ if (!HDPrivateKey.isValidPath(index, hardened)) { throw new hdErrors.InvalidPath(index); } hardened = index >= HDPrivateKey.Hardened ? true : hardened; if (index < HDPrivateKey.Hardened && hardened === true) { index += HDPrivateKey.Hardened; } var indexBuffer = BufferUtil.integerAsBuffer(index); var data; if (hardened && nonCompliant) { // The private key serialization in this case will not be exactly 32 bytes and can be // any value less, and the value is not zero-padded. var nonZeroPadded = this.privateKey.bn.toBuffer(); data = BufferUtil.concat([Buffer.from([0]), nonZeroPadded, indexBuffer]); } else if (hardened) { // This will use a 32 byte zero padded serialization of the private key var privateKeyBuffer = this.privateKey.bn.toBuffer({ size: 32 }); assert( privateKeyBuffer.length === 32, 'length of private key buffer is expected to be 32 bytes' ); data = BufferUtil.concat([Buffer.from([0]), privateKeyBuffer, indexBuffer]); } else { data = BufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]); } var hash = Hash.sha512hmac(data, this._buffers.chainCode); var leftPart = BN.fromBuffer(hash.slice(0, 32), { size: 32, }); var chainCode = hash.slice(32, 64); var privateKey = leftPart .add(this.privateKey.toBigNumber()) .umod(Point.getN()) .toBuffer({ size: 32, }); if (!PrivateKey.isValid(privateKey)) { // Index at this point is already hardened, we can pass null as the hardened arg return this._deriveWithNumber(index + 1, null, nonCompliant); } var derived = new HDPrivateKey({ network: this.network, depth: this.depth + 1, parentFingerPrint: this.fingerPrint, childIndex: index, chainCode: chainCode, privateKey: privateKey, }); return derived; }; HDPrivateKey.prototype._deriveWithBigNumber = function(index, hardened) { /* jshint maxstatements: 20 */ /* jshint maxcomplexity: 10 */ if (!HDPrivateKey.isValidPath(index, hardened)) { throw new hdErrors.InvalidPath(index); } var indexBuffer = index.toBuffer({size:32}) var data; if (hardened) { // This will use a 32 byte zero padded serialization of the private key var privateKeyBuffer = this.privateKey.bn.toBuffer({size: 32}); assert(privateKeyBuffer.length === 32, 'length of private key buffer is expected to be 32 bytes'); data = BufferUtil.concat([Buffer.from([0]), privateKeyBuffer, indexBuffer]); } else { data = BufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]); } var hash = Hash.sha512hmac(data, this._buffers.chainCode); var leftPart = BN.fromBuffer(hash.slice(0, 32), { size: 32 }); var chainCode = hash.slice(32, 64); var privateKey = leftPart.add(this.privateKey.toBigNumber()).umod(Point.getN()).toBuffer({ size: 32 }); if (!PrivateKey.isValid(privateKey)) { // Always pass hardened arg return this._deriveWithBigNumber(index.iaddn(1), hardened); } var derived = new HDPrivateKey({ network: this.network, depth: this.depth + 1, parentFingerPrint: this.fingerPrint, hardened: hardened, childIndex: index, chainCode: chainCode, privateKey: privateKey }); return derived; }; HDPrivateKey.prototype._deriveFromString = function (path, nonCompliant) { if (!HDPrivateKey.isValidPath(path)) { throw new hdErrors.InvalidPath(path); } var indexes = HDPrivateKey._getDerivationIndexes(path); var derived = indexes.reduce(function (prev, index) { return _.isNumber(index.value) ? prev._deriveWithNumber(index.value, null, nonCompliant) : prev._deriveWithBigNumber(index.value, index.hardened); }, this); return derived; }; /** * Verifies that a given serialized private key in base58 with checksum format * is valid. * * @param {string|Buffer} data - the serialized private key * @param {string|Network=} network - optional, if present, checks that the * network provided matches the network serialized. * @return {boolean} */ HDPrivateKey.isValidSerialized = function (data, network) { return !HDPrivateKey.getSerializedError(data, network); }; /** * Checks what's the error that causes the validation of a serialized private key * in base58 with checksum to fail. * * @param {string|Buffer} data - the serialized private key * @param {string|Network=} network - optional, if present, checks that the * network provided matches the network serialized. * @return {InvalidArgument|null} */ HDPrivateKey.getSerializedError = function (data, network) { /* jshint maxcomplexity: 10 */ if (!(_.isString(data) || BufferUtil.isBuffer(data))) { return new hdErrors.UnrecognizedArgument('Expected string or buffer'); } if (!Base58.validCharacters(data)) { return new errors.InvalidB58Char('(unknown)', data); } try { data = Base58Check.decode(data); } catch (e) { return new errors.InvalidB58Checksum(data); } if (![HDPrivateKey.DataLength, HDPrivateKey.DataSize256bit].includes(data.length)) { return new hdErrors.InvalidLength(data); } if (!_.isUndefined(network)) { var error = HDPrivateKey._validateNetwork(data, network); if (error) { return error; } } return null; }; HDPrivateKey._validateNetwork = function (data, networkArg) { var network = Network.get(networkArg); if (!network) { return new errors.InvalidNetworkArgument(networkArg); } var version = data.slice(0, 4); var versionInt = BufferUtil.integerFromBuffer(version) if (data.length === HDPrivateKey.DataLength && versionInt !== network.xprivkey) return new errors.InvalidNetwork(version); if (data.length === HDPrivateKey.DataSize256bit && versionInt !== network.xprivkey256bit) return new errors.InvalidNetwork(version); return null; }; HDPrivateKey._IndexArgToBN = function (arg) { if (_.isString(arg) && arg.slice(0, 2) === '0x') { return new BN(arg.slice(2, arg.length), 16) } else if (BufferUtil.isBuffer(arg)) { return new BN(arg) } else if (BN.isBN(arg)) { return arg } else { return null } } HDPrivateKey._IndexArgToNumber = function (arg, {castStringToNumber}) { if (_.isNumber(arg)) { return arg } else if (_.isString(arg) && arg.slice(0, 2) === '0x') { return new BN(arg.slice(2, arg.length), 16).toNumber() } else if (_.isString(arg) && castStringToNumber) { return +arg // cast to number compatible to BIP32 Mode } else if (BufferUtil.isBuffer(arg)) { return new BN(arg).toNumber() } else if (BN.isBN(arg)) { return arg.toNumber() } else { return NaN } } HDPrivateKey._isIndex256bit = function (index) { if (BufferUtil.isBuffer(index) && index.length === HDPrivateKey.ChildIndexSize256bit) { return true } else if (_.isString(index) && index.slice(0, 2) === '0x') { var bnIndex = new BN(index.slice(2, index.length), 16) assert(bnIndex.lt(HDPrivateKey.MaxIndex256bit), "Index is too large, should be < 2^256") return bnIndex.gte(HDPrivateKey.MaxIndex32bit) ? true : false } else if (BN.isBN(index)) { assert(index.gte(HDPrivateKey.MaxIndex32bit), "Index is too small, 'hex' index should be >= 2^32") assert(index.lt(HDPrivateKey.MaxIndex256bit), "Index is too large, should be < 2^256") return true } else { return false } } HDPrivateKey._index256bitToBuffer = function (index) { if (BufferUtil.isBuffer(index)) { assert(index.length === HDPrivateKey.ChildIndexSize256bit, `Index buffer-length is illegal, should be ${HDPrivateKey.ChildIndexSize256bit}`) return index } else if (_.isString(index) && index.slice(0, 2) === '0x') { var bnIndex = new BN(index.slice(2, index.length), 16) assert(bnIndex.gte(HDPrivateKey.MaxIndex32bit), "Index is too small, 'hex' index should be >= 2^32") assert(bnIndex.lt(HDPrivateKey.MaxIndex256bit), "Index is too large, should be < 2^256") return bnIndex.toBuffer({size: 32}) } else if (BN.isBN(index)) { assert(index.gte(HDPrivateKey.MaxIndex32bit), "Index is too small, 'hex' index should be >= 2^32") assert(index.lt(HDPrivateKey.MaxIndex256bit), "Index is too large, should be < 2^256") return index.toBuffer({size: 32}) } } HDPrivateKey._index32bitToBuffer = function (index) { if (BufferUtil.isBuffer(index)) { assert(index.length === HDPrivateKey.ChildIndexSize, `Index buffer-length is illegal, should be ${HDPrivateKey.ChildIndexSize}`) return index } else if (_.isString(index) && index.slice(0, 2) === '0x') { var bnIndex = new BN(index.slice(2, index.length), 16) return bnIndex.toBuffer({size: 4}) } else if (_.isNumber(index)) { return BufferUtil.integerAsBuffer(index) } else if (BN.isBN(index)) { return index.toBuffer({size: 4}) } } /** * Creates an HDPrivateKey from a string representation * * @param {String} arg * @return {HDPrivateKey} */ HDPrivateKey.fromString = function (arg) { $.checkArgument(_.isString(arg), 'No valid string was provided'); return new HDPrivateKey(arg); }; /** * Creates an HDPrivateKey from an object * * @param {Object} arg * @return {HDPrivateKey} */ HDPrivateKey.fromObject = function (arg) { $.checkArgument(_.isObject(arg), 'No valid argument was provided'); return new HDPrivateKey(arg); }; HDPrivateKey.prototype._buildFromJSON = function (arg) { return this._buildFromObject(JSON.parse(arg)); }; HDPrivateKey.prototype._buildFromObject = function (arg) { /* jshint maxcomplexity: 12 */ // TODO: Type validation var versionKey, childIndex, hardened // DIP14 256-bit Mode if (HDPrivateKey._isIndex256bit(arg.childIndex)) { versionKey = 'xprivkey256bit' childIndex = HDPrivateKey._index256bitToBuffer(arg.childIndex) hardened = _.isBoolean(arg.hardened) ? BufferUtil.integerAsSingleByteBuffer(arg.hardened ? 1 : 0) : BufferUtil.emptyBuffer(1) } // BIP32 Compatibility Mode else { versionKey = 'xprivkey' childIndex = _.isNumber(arg.childIndex) ? BufferUtil.integerAsBuffer(arg.childIndex) : arg.childIndex hardened = undefined } var buffers = { version: arg.network ? BufferUtil.integerAsBuffer(Network.get(arg.network)[versionKey]) : arg.version, depth: _.isNumber(arg.depth) ? BufferUtil.integerAsSingleByteBuffer(arg.depth) : arg.depth, parentFingerPrint: _.isNumber(arg.parentFingerPrint) ? BufferUtil.integerAsBuffer(arg.parentFingerPrint) : arg.parentFingerPrint, hardened: hardened, childIndex: childIndex, chainCode: _.isString(arg.chainCode) ? BufferUtil.hexToBuffer(arg.chainCode) : arg.chainCode, privateKey: (_.isString(arg.privateKey) && JSUtil.isHexa(arg.privateKey)) ? BufferUtil.hexToBuffer(arg.privateKey) : arg.privateKey, checksum: arg.checksum ? (arg.checksum.length ? arg.checksum : BufferUtil.integerAsBuffer(arg.checksum)) : undefined }; return this._buildFromBuffers(buffers); }; HDPrivateKey.prototype._buildFromSerialized = function(arg) { var decoded = Base58Check.decode(arg); var buffers if (decoded.length === HDPrivateKey.DataLength) { buffers = { version: decoded.slice( HDPrivateKey.VersionStart, HDPrivateKey.VersionEnd ), depth: decoded.slice( HDPrivateKey.DepthStart, HDPrivateKey.DepthEnd ), parentFingerPrint: decoded.slice( HDPrivateKey.ParentFingerPrintStart, HDPrivateKey.ParentFingerPrintEnd ), childIndex: decoded.slice( HDPrivateKey.ChildIndexStart, HDPrivateKey.ChildIndexEnd ), chainCode: decoded.slice( HDPrivateKey.ChainCodeStart, HDPrivateKey.ChainCodeEnd ), privateKey: decoded.slice( HDPrivateKey.PrivateKeyStart, HDPrivateKey.PrivateKeyEnd ), checksum: decoded.slice( HDPrivateKey.ChecksumStart, HDPrivateKey.ChecksumEnd ), xprivkey: arg }; } else if (decoded.length === HDPrivateKey.DataSize256bit) { buffers = { version: decoded.slice( HDPrivateKey.VersionStart, HDPrivateKey.VersionEnd ), depth: decoded.slice( HDPrivateKey.DepthStart, HDPrivateKey.DepthEnd ), parentFingerPrint: decoded.slice( HDPrivateKey.ParentFingerPrintStart, HDPrivateKey.ParentFingerPrintEnd ), hardened: decoded.slice( HDPrivateKey.HardenedStart, HDPrivateKey.HardenedEnd ), childIndex: decoded.slice( HDPrivateKey.ChildIndexStart256bit, HDPrivateKey.ChildIndexEnd256bit ), chainCode: decoded.slice( HDPrivateKey.ChainCodeStart256bit, HDPrivateKey.ChainCodeEnd256bit ), privateKey: decoded.slice( HDPrivateKey.PrivateKeyStart256bit, HDPrivateKey.PrivateKeyEnd256bit ), checksum: decoded.slice( HDPrivateKey.ChecksumStart256bit, HDPrivateKey.ChecksumEnd256bit ), xprivkey: arg }; } return this._buildFromBuffers(buffers); }; HDPrivateKey.prototype._generateRandomly = function (network) { return HDPrivateKey.fromSeed(Random.getRandomBuffer(64), network); }; /** * Generate a private key from a seed, as described in BIP32 * * @param {string|Buffer} hexa * @param {*} network * @return {HDPrivateKey} */ HDPrivateKey.fromSeed = function (hexa, network) { /* jshint maxcomplexity: 8 */ if (JSUtil.isHexaString(hexa)) { hexa = BufferUtil.hexToBuffer(hexa); } if (!Buffer.isBuffer(hexa)) { throw new hdErrors.InvalidEntropyArgument(hexa); } if (hexa.length < MINIMUM_ENTROPY_BITS * BITS_TO_BYTES) { throw new hdErrors.InvalidEntropyArgument.NotEnoughEntropy(hexa); } if (hexa.length > MAXIMUM_ENTROPY_BITS * BITS_TO_BYTES) { throw new hdErrors.InvalidEntropyArgument.TooMuchEntropy(hexa); } var hash = Hash.sha512hmac(hexa, Buffer.from('Bitcoin seed')); return new HDPrivateKey({ network: Network.get(network) || Network.defaultNetwork, depth: 0, parentFingerPrint: 0, childIndex: 0, privateKey: hash.slice(0, 32), chainCode: hash.slice(32, 64), }); }; HDPrivateKey.prototype._calcHDPublicKey = function () { if (!this._hdPublicKey) { var HDPublicKey = require('./hdpublickey'); this._hdPublicKey = new HDPublicKey(this); } }; /** * Receives a object with buffers in all the properties and populates the * internal structure * * @param {Object} arg * @param {Buffer} arg.version * @param {Buffer} arg.depth * @param {Buffer} arg.parentFingerPrint * @param {Buffer} arg.hardened - only used for DIP14 256-bit derivation paths * @param {Buffer} arg.childIndex * @param {Buffer} arg.chainCode * @param {Buffer} arg.privateKey * @param {Buffer} arg.checksum * @param {string=} arg.xprivkey - if set, don't recalculate the base58 * representation * @return {HDPrivateKey} this */ HDPrivateKey.prototype._buildFromBuffers = function (arg) { /* jshint maxcomplexity: 8 */ /* jshint maxstatements: 20 */ HDPrivateKey._validateBufferArguments(arg); JSUtil.defineImmutable(this, { _buffers: arg, }); var sequence // DIP14 256-bit Mode if (arg.childIndex.length === HDPrivateKey.ChildIndexSize256bit) { sequence = [ arg.version, arg.depth, arg.parentFingerPrint, arg.hardened, arg.childIndex, arg.chainCode, BufferUtil.emptyBuffer(1), arg.privateKey ]; } // BIP32 Compatibility Mode else { sequence = [ arg.version, arg.depth, arg.parentFingerPrint, arg.childIndex, arg.chainCode, BufferUtil.emptyBuffer(1), arg.privateKey ]; } var concat = Buffer.concat(sequence); if (!arg.checksum || !arg.checksum.length) { arg.checksum = Base58Check.checksum(concat); } else { if (arg.checksum.toString() !== Base58Check.checksum(concat).toString()) { throw new errors.InvalidB58Checksum(concat); } } var network = Network.get(BufferUtil.integerFromBuffer(arg.version)); var xprivkey; xprivkey = Base58Check.encode(Buffer.concat(sequence)); arg.xprivkey = Buffer.from(xprivkey); var privateKey = new PrivateKey(BN.fromBuffer(arg.privateKey), network); var publicKey = privateKey.toPublicKey(); var size = HDPrivateKey.ParentFingerPrintSize; var fingerPrint = Hash.sha256ripemd160(publicKey.toBuffer()).slice(0, size); JSUtil.defineImmutable(this, { xprivkey: xprivkey, network: network, depth: BufferUtil.integerFromSingleByteBuffer(arg.depth), privateKey: privateKey, publicKey: publicKey, fingerPrint: fingerPrint, }); this._hdPublicKey = null; Object.defineProperty(this, 'hdPublicKey', { configurable: false, enumerable: true, get: function () { this._calcHDPublicKey(); return this._hdPublicKey; }, }); Object.defineProperty(this, 'xpubkey', { configurable: false, enumerable: true, get: function () { this._calcHDPublicKey(); return this._hdPublicKey.xpubkey; }, }); return this; }; HDPrivateKey._validateBufferArguments = function (arg) { var checkBuffer = function (name, size) { var buff = arg[name]; assert(BufferUtil.isBuffer(buff), name + ' argument is not a buffer'); assert( buff.length === size, name + ' has not the expected size: found ' + buff.length + ', expected ' + size ); }; checkBuffer('version', HDPrivateKey.VersionSize); checkBuffer('depth', HDPrivateKey.DepthSize); checkBuffer('parentFingerPrint', HDPrivateKey.ParentFingerPrintSize); checkBuffer('chainCode', HDPrivateKey.ChainCodeSize); checkBuffer('privateKey', HDPrivateKey.PrivateKeySize); if (arg.checksum && arg.checksum.length) { checkBuffer('checksum', HDPrivateKey.CheckSumSize); } assert(BufferUtil.isBuffer(arg.childIndex), 'childIndex argument is not a buffer'); if (arg['childIndex'].length === HDPrivateKey.ChildIndexSize256bit) { checkBuffer('hardened', HDPrivateKey.HardenedSize) } else { checkBuffer('childIndex', HDPrivateKey.ChildIndexSize) } }; /** * Returns the string representation of this private key (a string starting * with "xprv..." * * @return {string} */ HDPrivateKey.prototype.toString = function () { return this.xprivkey; }; /** * Returns the console representation of this extended private key. * @return {string} */ HDPrivateKey.prototype.inspect = function () { return '<HDPrivateKey: ' + this.xprivkey + '>'; }; /** * Returns a plain object with a representation of this private key. * * Fields include:<ul> * <li> network: either 'livenet' or 'testnet' * <li> depth: a number ranging from 0 to 255 * <li> fingerPrint: a number ranging from 0 to 2^32-1, taken from the hash of the * <li> associated public key * <li> parentFingerPrint: a number ranging from 0 to 2^32-1, taken from the hash * <li> of this parent's associated public key or zero. * <li> childIndex: the index from which this child was derived (or zero) * <li> chainCode: an hexa string representing a number used in the derivation * <li> privateKey: the private key associated, in hexa representation * <li> xprivkey: the representation of this extended private key in checksum * <li> base58 format * <li> checksum: the base58 checksum of xprivkey * </ul> * @function * @return {Object} */ HDPrivateKey.prototype.toObject = HDPrivateKey.prototype.toJSON = function toObject() { var versionKey, hardened, childIndex // DIP14 256-bit Mode if (this._buffers.childIndex.length === HDPrivateKey.ChildIndexSize256bit) { versionKey = 'xprivkey256bit' hardened = BufferUtil.integerFromBuffer(this._buffers.hardened) ? true : false childIndex = '0x' + Buffer.from(this._buffers.childIndex).toString('hex') // debugger } // BIP32 Compatibility Mode else { versionKey = 'xprivkey' hardened = undefined childIndex = BufferUtil.integerFromBuffer(this._buffers.childIndex) } return { network: Network.get(BufferUtil.integerFromBuffer(this._buffers.version), versionKey).name, depth: BufferUtil.integerFromSingleByteBuffer(this._buffers.depth), fingerPrint: BufferUtil.integerFromBuffer(this.fingerPrint), parentFingerPrint: BufferUtil.integerFromBuffer(this._buffers.parentFingerPrint), hardened: hardened, childIndex: childIndex, chainCode: BufferUtil.bufferToHex(this._buffers.chainCode), privateKey: this.privateKey.toBuffer().toString('hex'), checksum: BufferUtil.integerFromBuffer(this._buffers.checksum), xprivkey: this.xprivkey }; }; /** * Build a HDPrivateKey from a buffer * * @param {Buffer} arg * @return {HDPrivateKey} */ HDPrivateKey.fromBuffer = function (arg) { return new HDPrivateKey(arg.toString()); }; /** * Returns a buffer representation of the HDPrivateKey * * @return {Buffer} */ HDPrivateKey.prototype.toBuffer = function () { return BufferUtil.copy(this._buffers.xprivkey); }; HDPrivateKey.DefaultDepth = 0; HDPrivateKey.DefaultFingerprint = 0; HDPrivateKey.DefaultChildIndex = 0; HDPrivateKey.Hardened = 0x80000000; HDPrivateKey.MaxIndex = 2 * HDPrivateKey.Hardened; HDPrivateKey.MaxIndex32bit = new BN(HDPrivateKey.MaxIndex) HDPrivateKey.MaxIndex256bit = new BN('10000000000000000000000000000000000000000000000000000000000000000', 16) // n2.pow(n256).toString('hex') HDPrivateKey.RootElementAlias = ['m', 'M', "m'", "M'"]; HDPrivateKey.VersionSize = 4; HDPrivateKey.DepthSize = 1; HDPrivateKey.ParentFingerPrintSize = 4; HDPrivateKey.HardenedSize = 1; HDPrivateKey.ChildIndexSize = 4; HDPrivateKey.ChildIndexSize256bit = 32; HDPrivateKey.ChainCodeSize = 32; HDPrivateKey.PrivateKeySize = 32; HDPrivateKey.CheckSumSize = 4; HDPrivateKey.DataLength = 78; HDPrivateKey.DataSize256bit = 107; HDPrivateKey.SerializedByteSize = 82; HDPrivateKey.SerializedByteSize256bit = 111; HDPrivateKey.VersionStart = 0; HDPrivateKey.VersionEnd = HDPrivateKey.VersionStart + HDPrivateKey.VersionSize; HDPrivateKey.DepthStart = HDPrivateKey.VersionEnd; HDPrivateKey.DepthEnd = HDPrivateKey.DepthStart + HDPrivateKey.DepthSize; HDPrivateKey.ParentFingerPrintStart = HDPrivateKey.DepthEnd; HDPrivateKey.ParentFingerPrintEnd = HDPrivateKey.ParentFingerPrintStart + HDPrivateKey.ParentFingerPrintSize; HDPrivateKey.ChildIndexStart = HDPrivateKey.ParentFingerPrintEnd; HDPrivateKey.ChildIndexEnd = HDPrivateKey.ChildIndexStart + HDPrivateKey.ChildIndexSize; HDPrivateKey.ChainCodeStart = HDPrivateKey.ChildIndexEnd; HDPrivateKey.ChainCodeEnd = HDPrivateKey.ChainCodeStart + HDPrivateKey.ChainCodeSize; HDPrivateKey.PrivateKeyStart = HDPrivateKey.ChainCodeEnd + 1; HDPrivateKey.PrivateKeyEnd = HDPrivateKey.PrivateKeyStart + HDPrivateKey.PrivateKeySize; HDPrivateKey.ChecksumStart = HDPrivateKey.PrivateKeyEnd; HDPrivateKey.ChecksumEnd = HDPrivateKey.ChecksumStart + HDPrivateKey.CheckSumSize; HDPrivateKey.HardenedStart = HDPrivateKey.ParentFingerPrintEnd; HDPrivateKey.HardenedEnd = HDPrivateKey.HardenedStart+HDPrivateKey.HardenedSize; HDPrivateKey.ChildIndexStart256bit = HDPrivateKey.HardenedEnd HDPrivateKey.ChildIndexEnd256bit = HDPrivateKey.ChildIndexStart256bit + HDPrivateKey.ChildIndexSize256bit; HDPrivateKey.ChainCodeStart256bit = HDPrivateKey.ChildIndexEnd256bit; HDPrivateKey.ChainCodeEnd256bit = HDPrivateKey.ChainCodeStart256bit + HDPrivateKey.ChainCodeSize; HDPrivateKey.PrivateKeyStart256bit = HDPrivateKey.ChainCodeEnd256bit + 1; HDPrivateKey.PrivateKeyEnd256bit = HDPrivateKey.PrivateKeyStart256bit + HDPrivateKey.PrivateKeySize; HDPrivateKey.ChecksumStart256bit = HDPrivateKey.PrivateKeyEnd256bit; HDPrivateKey.ChecksumEnd256bit = HDPrivateKey.ChecksumStart256bit + HDPrivateKey.CheckSumSize; assert(HDPrivateKey.ChecksumEnd === HDPrivateKey.SerializedByteSize); assert(HDPrivateKey.ChecksumEnd256bit === HDPrivateKey.SerializedByteSize256bit); module.exports = HDPrivateKey;