UNPKG

21.2 kBJavaScriptView Raw
1'use strict'
2
3var assert = require('assert')
4var buffer = require('buffer')
5var _ = require('./util/_')
6var $ = require('./util/preconditions')
7
8var BN = require('./crypto/bn')
9var Base58 = require('./encoding/base58')
10var Base58Check = require('./encoding/base58check')
11var Hash = require('./crypto/hash')
12var Network = require('./networks')
13var Point = require('./crypto/point')
14var PrivateKey = require('./privatekey')
15var Random = require('./crypto/random')
16
17var errors = require('./errors')
18var hdErrors = errors.HDPrivateKey
19var JSUtil = require('./util/js')
20
21var MINIMUM_ENTROPY_BITS = 128
22var BITS_TO_BYTES = 1 / 8
23var MAXIMUM_ENTROPY_BITS = 512
24
25/**
26 * Represents an instance of an hierarchically derived private key.
27 *
28 * More info on https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
29 *
30 * @constructor
31 * @param {string|Buffer|Object} arg
32 */
33function HDPrivateKey (arg) {
34 if (arg instanceof HDPrivateKey) {
35 return arg
36 }
37 if (!(this instanceof HDPrivateKey)) {
38 return new HDPrivateKey(arg)
39 }
40 if (!arg) {
41 return this._generateRandomly()
42 }
43
44 if (Network.get(arg)) {
45 return this._generateRandomly(arg)
46 } else if (_.isString(arg) || Buffer.isBuffer(arg)) {
47 if (HDPrivateKey.isValidSerialized(arg)) {
48 this._buildFromSerialized(arg)
49 } else if (JSUtil.isValidJSON(arg)) {
50 this._buildFromJSON(arg)
51 } else if (Buffer.isBuffer(arg) && HDPrivateKey.isValidSerialized(arg.toString())) {
52 this._buildFromSerialized(arg.toString())
53 } else {
54 throw HDPrivateKey.getSerializedError(arg)
55 }
56 } else if (_.isObject(arg)) {
57 this._buildFromObject(arg)
58 } else {
59 throw new hdErrors.UnrecognizedArgument(arg)
60 }
61}
62
63HDPrivateKey.fromRandom = function () {
64 return new HDPrivateKey()
65}
66
67/**
68 * Verifies that a given path is valid.
69 *
70 * @param {string|number} arg
71 * @param {boolean?} hardened
72 * @return {boolean}
73 */
74HDPrivateKey.isValidPath = function (arg, hardened) {
75 if (_.isString(arg)) {
76 var indexes = HDPrivateKey._getDerivationIndexes(arg)
77 return indexes !== null && _.every(indexes, HDPrivateKey.isValidPath)
78 }
79
80 if (_.isNumber(arg)) {
81 if (arg < HDPrivateKey.Hardened && hardened === true) {
82 arg += HDPrivateKey.Hardened
83 }
84 return arg >= 0 && arg < HDPrivateKey.MaxIndex
85 }
86
87 return false
88}
89
90/**
91 * Internal function that splits a string path into a derivation index array.
92 * It will return null if the string path is malformed.
93 * It does not validate if indexes are in bounds.
94 *
95 * @param {string} path
96 * @return {Array}
97 */
98HDPrivateKey._getDerivationIndexes = function (path) {
99 var steps = path.split('/')
100
101 // Special cases:
102 if (_.includes(HDPrivateKey.RootElementAlias, path)) {
103 return []
104 }
105
106 if (!_.includes(HDPrivateKey.RootElementAlias, steps[0])) {
107 return null
108 }
109
110 var indexes = steps.slice(1).map(function (step) {
111 var isHardened = step.slice(-1) === '\''
112 if (isHardened) {
113 step = step.slice(0, -1)
114 }
115 if (!step || step[0] === '-') {
116 return NaN
117 }
118 var index = +step // cast to number
119 if (isHardened) {
120 index += HDPrivateKey.Hardened
121 }
122
123 return index
124 })
125
126 return _.some(indexes, isNaN) ? null : indexes
127}
128
129/**
130 * WARNING: This method is deprecated. Use deriveChild or deriveNonCompliantChild instead. This is not BIP32 compliant
131 *
132 *
133 * Get a derived child based on a string or number.
134 *
135 * If the first argument is a string, it's parsed as the full path of
136 * derivation. Valid values for this argument include "m" (which returns the
137 * same private key), "m/0/1/40/2'/1000", where the ' quote means a hardened
138 * derivation.
139 *
140 * If the first argument is a number, the child with that index will be
141 * derived. If the second argument is truthy, the hardened version will be
142 * derived. See the example usage for clarification.
143 *
144 * @example
145 * ```javascript
146 * var parent = new HDPrivateKey('xprv...');
147 * var child_0_1_2h = parent.derive(0).derive(1).derive(2, true);
148 * var copy_of_child_0_1_2h = parent.derive("m/0/1/2'");
149 * assert(child_0_1_2h.xprivkey === copy_of_child_0_1_2h);
150 * ```
151 *
152 * @param {string|number} arg
153 * @param {boolean?} hardened
154 */
155HDPrivateKey.prototype.derive = function () {
156 throw new Error('derive has been deprecated. use deriveChild or, for the old way, deriveNonCompliantChild.')
157}
158
159/**
160 * WARNING: This method will not be officially supported until v1.0.0.
161 *
162 *
163 * Get a derived child based on a string or number.
164 *
165 * If the first argument is a string, it's parsed as the full path of
166 * derivation. Valid values for this argument include "m" (which returns the
167 * same private key), "m/0/1/40/2'/1000", where the ' quote means a hardened
168 * derivation.
169 *
170 * If the first argument is a number, the child with that index will be
171 * derived. If the second argument is truthy, the hardened version will be
172 * derived. See the example usage for clarification.
173 *
174 * WARNING: The `nonCompliant` option should NOT be used, except for older implementation
175 * that used a derivation strategy that used a non-zero padded private key.
176 *
177 * @example
178 * ```javascript
179 * var parent = new HDPrivateKey('xprv...');
180 * var child_0_1_2h = parent.deriveChild(0).deriveChild(1).deriveChild(2, true);
181 * var copy_of_child_0_1_2h = parent.deriveChild("m/0/1/2'");
182 * assert(child_0_1_2h.xprivkey === copy_of_child_0_1_2h);
183 * ```
184 *
185 * @param {string|number} arg
186 * @param {boolean?} hardened
187 */
188HDPrivateKey.prototype.deriveChild = function (arg, hardened) {
189 if (_.isNumber(arg)) {
190 return this._deriveWithNumber(arg, hardened)
191 } else if (_.isString(arg)) {
192 return this._deriveFromString(arg)
193 } else {
194 throw new hdErrors.InvalidDerivationArgument(arg)
195 }
196}
197
198/**
199 * WARNING: This method will not be officially supported until v1.0.0
200 *
201 *
202 * WARNING: If this is a new implementation you should NOT use this method, you should be using
203 * `derive` instead.
204 *
205 * This method is explicitly for use and compatibility with an implementation that
206 * was not compliant with BIP32 regarding the derivation algorithm. The private key
207 * must be 32 bytes hashing, and this implementation will use the non-zero padded
208 * serialization of a private key, such that it's still possible to derive the privateKey
209 * to recover those funds.
210 *
211 * @param {string|number} arg
212 * @param {boolean?} hardened
213 */
214HDPrivateKey.prototype.deriveNonCompliantChild = function (arg, hardened) {
215 if (_.isNumber(arg)) {
216 return this._deriveWithNumber(arg, hardened, true)
217 } else if (_.isString(arg)) {
218 return this._deriveFromString(arg, true)
219 } else {
220 throw new hdErrors.InvalidDerivationArgument(arg)
221 }
222}
223
224HDPrivateKey.prototype._deriveWithNumber = function (index, hardened, nonCompliant) {
225 if (!HDPrivateKey.isValidPath(index, hardened)) {
226 throw new hdErrors.InvalidPath(index)
227 }
228
229 hardened = index >= HDPrivateKey.Hardened ? true : hardened
230 if (index < HDPrivateKey.Hardened && hardened === true) {
231 index += HDPrivateKey.Hardened
232 }
233
234 var indexBuffer = JSUtil.integerAsBuffer(index)
235 var data
236 if (hardened && nonCompliant) {
237 // The private key serialization in this case will not be exactly 32 bytes and can be
238 // any value less, and the value is not zero-padded.
239 var nonZeroPadded = this.privateKey.bn.toBuffer()
240 data = Buffer.concat([buffer.Buffer.from([0]), nonZeroPadded, indexBuffer])
241 } else if (hardened) {
242 // This will use a 32 byte zero padded serialization of the private key
243 var privateKeyBuffer = this.privateKey.bn.toBuffer({ size: 32 })
244 assert(privateKeyBuffer.length === 32, 'length of private key buffer is expected to be 32 bytes')
245 data = Buffer.concat([buffer.Buffer.from([0]), privateKeyBuffer, indexBuffer])
246 } else {
247 data = Buffer.concat([this.publicKey.toBuffer(), indexBuffer])
248 }
249 var hash = Hash.sha512hmac(data, this._buffers.chainCode)
250 var leftPart = BN.fromBuffer(hash.slice(0, 32), {
251 size: 32
252 })
253 var chainCode = hash.slice(32, 64)
254
255 var privateKey = leftPart.add(this.privateKey.toBigNumber()).umod(Point.getN()).toBuffer({
256 size: 32
257 })
258
259 if (!PrivateKey.isValid(privateKey)) {
260 // Index at this point is already hardened, we can pass null as the hardened arg
261 return this._deriveWithNumber(index + 1, null, nonCompliant)
262 }
263
264 var derived = new HDPrivateKey({
265 network: this.network,
266 depth: this.depth + 1,
267 parentFingerPrint: this.fingerPrint,
268 childIndex: index,
269 chainCode: chainCode,
270 privateKey: privateKey
271 })
272
273 return derived
274}
275
276HDPrivateKey.prototype._deriveFromString = function (path, nonCompliant) {
277 if (!HDPrivateKey.isValidPath(path)) {
278 throw new hdErrors.InvalidPath(path)
279 }
280
281 var indexes = HDPrivateKey._getDerivationIndexes(path)
282 var derived = indexes.reduce(function (prev, index) {
283 return prev._deriveWithNumber(index, null, nonCompliant)
284 }, this)
285
286 return derived
287}
288
289/**
290 * Verifies that a given serialized private key in base58 with checksum format
291 * is valid.
292 *
293 * @param {string|Buffer} data - the serialized private key
294 * @param {string|Network=} network - optional, if present, checks that the
295 * network provided matches the network serialized.
296 * @return {boolean}
297 */
298HDPrivateKey.isValidSerialized = function (data, network) {
299 return !HDPrivateKey.getSerializedError(data, network)
300}
301
302/**
303 * Checks what's the error that causes the validation of a serialized private key
304 * in base58 with checksum to fail.
305 *
306 * @param {string|Buffer} data - the serialized private key
307 * @param {string|Network=} network - optional, if present, checks that the
308 * network provided matches the network serialized.
309 * @return {errors.InvalidArgument|null}
310 */
311HDPrivateKey.getSerializedError = function (data, network) {
312 if (!(_.isString(data) || Buffer.isBuffer(data))) {
313 return new hdErrors.UnrecognizedArgument('Expected string or buffer')
314 }
315 if (!Base58.validCharacters(data)) {
316 return new errors.InvalidB58Char('(unknown)', data)
317 }
318 try {
319 data = Base58Check.decode(data)
320 } catch (e) {
321 return new errors.InvalidB58Checksum(data)
322 }
323 if (data.length !== HDPrivateKey.DataLength) {
324 return new hdErrors.InvalidLength(data)
325 }
326 if (!_.isUndefined(network)) {
327 var error = HDPrivateKey._validateNetwork(data, network)
328 if (error) {
329 return error
330 }
331 }
332 return null
333}
334
335HDPrivateKey._validateNetwork = function (data, networkArg) {
336 var network = Network.get(networkArg)
337 if (!network) {
338 return new errors.InvalidNetworkArgument(networkArg)
339 }
340 var version = data.slice(0, 4)
341 if (version.readUInt32BE(0) !== network.xprivkey) {
342 return new errors.InvalidNetwork(version)
343 }
344 return null
345}
346
347HDPrivateKey.fromString = function (arg) {
348 $.checkArgument(_.isString(arg), 'No valid string was provided')
349 return new HDPrivateKey(arg)
350}
351
352HDPrivateKey.fromObject = function (arg) {
353 $.checkArgument(_.isObject(arg), 'No valid argument was provided')
354 return new HDPrivateKey(arg)
355}
356
357HDPrivateKey.prototype._buildFromJSON = function (arg) {
358 return this._buildFromObject(JSON.parse(arg))
359}
360
361HDPrivateKey.prototype._buildFromObject = function (arg) {
362 // TODO: Type validation
363 var buffers = {
364 version: arg.network ? JSUtil.integerAsBuffer(Network.get(arg.network).xprivkey) : arg.version,
365 depth: _.isNumber(arg.depth) ? Buffer.from([arg.depth & 0xff]) : arg.depth,
366 parentFingerPrint: _.isNumber(arg.parentFingerPrint) ? JSUtil.integerAsBuffer(arg.parentFingerPrint) : arg.parentFingerPrint,
367 childIndex: _.isNumber(arg.childIndex) ? JSUtil.integerAsBuffer(arg.childIndex) : arg.childIndex,
368 chainCode: _.isString(arg.chainCode) ? Buffer.from(arg.chainCode, 'hex') : arg.chainCode,
369 privateKey: (_.isString(arg.privateKey) && JSUtil.isHexa(arg.privateKey)) ? Buffer.from(arg.privateKey, 'hex') : arg.privateKey,
370 checksum: arg.checksum ? (arg.checksum.length ? arg.checksum : JSUtil.integerAsBuffer(arg.checksum)) : undefined
371 }
372 return this._buildFromBuffers(buffers)
373}
374
375HDPrivateKey.prototype._buildFromSerialized = function (arg) {
376 var decoded = Base58Check.decode(arg)
377 var buffers = {
378 version: decoded.slice(HDPrivateKey.VersionStart, HDPrivateKey.VersionEnd),
379 depth: decoded.slice(HDPrivateKey.DepthStart, HDPrivateKey.DepthEnd),
380 parentFingerPrint: decoded.slice(HDPrivateKey.ParentFingerPrintStart,
381 HDPrivateKey.ParentFingerPrintEnd),
382 childIndex: decoded.slice(HDPrivateKey.ChildIndexStart, HDPrivateKey.ChildIndexEnd),
383 chainCode: decoded.slice(HDPrivateKey.ChainCodeStart, HDPrivateKey.ChainCodeEnd),
384 privateKey: decoded.slice(HDPrivateKey.PrivateKeyStart, HDPrivateKey.PrivateKeyEnd),
385 checksum: decoded.slice(HDPrivateKey.ChecksumStart, HDPrivateKey.ChecksumEnd),
386 xprivkey: arg
387 }
388 return this._buildFromBuffers(buffers)
389}
390
391HDPrivateKey.prototype._generateRandomly = function (network) {
392 return HDPrivateKey.fromSeed(Random.getRandomBuffer(64), network)
393}
394
395/**
396 * Generate a private key from a seed, as described in BIP32
397 *
398 * @param {string|Buffer} hexa
399 * @param {*} network
400 * @return HDPrivateKey
401 */
402HDPrivateKey.fromSeed = function (hexa, network) {
403 if (JSUtil.isHexaString(hexa)) {
404 hexa = Buffer.from(hexa, 'hex')
405 }
406 if (!Buffer.isBuffer(hexa)) {
407 throw new hdErrors.InvalidEntropyArgument(hexa)
408 }
409 if (hexa.length < MINIMUM_ENTROPY_BITS * BITS_TO_BYTES) {
410 throw new hdErrors.InvalidEntropyArgument.NotEnoughEntropy(hexa)
411 }
412 if (hexa.length > MAXIMUM_ENTROPY_BITS * BITS_TO_BYTES) {
413 throw new hdErrors.InvalidEntropyArgument.TooMuchEntropy(hexa)
414 }
415 var hash = Hash.sha512hmac(hexa, buffer.Buffer.from('Bitcoin seed'))
416
417 return new HDPrivateKey({
418 network: Network.get(network) || Network.defaultNetwork,
419 depth: 0,
420 parentFingerPrint: 0,
421 childIndex: 0,
422 privateKey: hash.slice(0, 32),
423 chainCode: hash.slice(32, 64)
424 })
425}
426
427HDPrivateKey.prototype._calcHDPublicKey = function () {
428 if (!this._hdPublicKey) {
429 var HDPublicKey = require('./hdpublickey')
430 this._hdPublicKey = new HDPublicKey(this)
431 }
432}
433
434/**
435 * Receives a object with buffers in all the properties and populates the
436 * internal structure
437 *
438 * @param {Object} arg
439 * @param {buffer.Buffer} arg.version
440 * @param {buffer.Buffer} arg.depth
441 * @param {buffer.Buffer} arg.parentFingerPrint
442 * @param {buffer.Buffer} arg.childIndex
443 * @param {buffer.Buffer} arg.chainCode
444 * @param {buffer.Buffer} arg.privateKey
445 * @param {buffer.Buffer} arg.checksum
446 * @param {string=} arg.xprivkey - if set, don't recalculate the base58
447 * representation
448 * @return {HDPrivateKey} this
449 */
450HDPrivateKey.prototype._buildFromBuffers = function (arg) {
451 HDPrivateKey._validateBufferArguments(arg)
452
453 JSUtil.defineImmutable(this, {
454 _buffers: arg
455 })
456
457 var sequence = [
458 arg.version, arg.depth, arg.parentFingerPrint, arg.childIndex, arg.chainCode,
459 Buffer.alloc(1), arg.privateKey
460 ]
461 var concat = buffer.Buffer.concat(sequence)
462 if (!arg.checksum || !arg.checksum.length) {
463 arg.checksum = Base58Check.checksum(concat)
464 } else {
465 if (arg.checksum.toString() !== Base58Check.checksum(concat).toString()) {
466 throw new errors.InvalidB58Checksum(concat)
467 }
468 }
469
470 var network = Network.get(arg.version.readUInt32BE(0))
471 var xprivkey
472 xprivkey = Base58Check.encode(buffer.Buffer.concat(sequence))
473 arg.xprivkey = Buffer.from(xprivkey)
474
475 var privateKey = new PrivateKey(BN.fromBuffer(arg.privateKey), network)
476 var publicKey = privateKey.toPublicKey()
477 var size = HDPrivateKey.ParentFingerPrintSize
478 var fingerPrint = Hash.sha256ripemd160(publicKey.toBuffer()).slice(0, size)
479
480 JSUtil.defineImmutable(this, {
481 xprivkey: xprivkey,
482 network: network,
483 depth: arg.depth[0],
484 privateKey: privateKey,
485 publicKey: publicKey,
486 fingerPrint: fingerPrint
487 })
488
489 this._hdPublicKey = null
490
491 Object.defineProperty(this, 'hdPublicKey', {
492 configurable: false,
493 enumerable: true,
494 get: function () {
495 this._calcHDPublicKey()
496 return this._hdPublicKey
497 }
498 })
499 Object.defineProperty(this, 'xpubkey', {
500 configurable: false,
501 enumerable: true,
502 get: function () {
503 this._calcHDPublicKey()
504 return this._hdPublicKey.xpubkey
505 }
506 })
507 return this
508}
509
510HDPrivateKey._validateBufferArguments = function (arg) {
511 var checkBuffer = function (name, size) {
512 var buff = arg[name]
513 assert(Buffer.isBuffer(buff), name + ' argument is not a buffer')
514 assert(
515 buff.length === size,
516 name + ' has not the expected size: found ' + buff.length + ', expected ' + size
517 )
518 }
519 checkBuffer('version', HDPrivateKey.VersionSize)
520 checkBuffer('depth', HDPrivateKey.DepthSize)
521 checkBuffer('parentFingerPrint', HDPrivateKey.ParentFingerPrintSize)
522 checkBuffer('childIndex', HDPrivateKey.ChildIndexSize)
523 checkBuffer('chainCode', HDPrivateKey.ChainCodeSize)
524 checkBuffer('privateKey', HDPrivateKey.PrivateKeySize)
525 if (arg.checksum && arg.checksum.length) {
526 checkBuffer('checksum', HDPrivateKey.CheckSumSize)
527 }
528}
529
530/**
531 * Returns the string representation of this private key (a string starting
532 * with "xprv..."
533 *
534 * @return string
535 */
536HDPrivateKey.prototype.toString = function () {
537 return this.xprivkey
538}
539
540/**
541 * Returns the console representation of this extended private key.
542 * @return string
543 */
544HDPrivateKey.prototype.inspect = function () {
545 return '<HDPrivateKey: ' + this.xprivkey + '>'
546}
547
548/**
549 * Returns a plain object with a representation of this private key.
550 *
551 * Fields include:<ul>
552 * <li> network: either 'livenet' or 'testnet'
553 * <li> depth: a number ranging from 0 to 255
554 * <li> fingerPrint: a number ranging from 0 to 2^32-1, taken from the hash of the
555 * <li> associated public key
556 * <li> parentFingerPrint: a number ranging from 0 to 2^32-1, taken from the hash
557 * <li> of this parent's associated public key or zero.
558 * <li> childIndex: the index from which this child was derived (or zero)
559 * <li> chainCode: an hexa string representing a number used in the derivation
560 * <li> privateKey: the private key associated, in hexa representation
561 * <li> xprivkey: the representation of this extended private key in checksum
562 * <li> base58 format
563 * <li> checksum: the base58 checksum of xprivkey
564 * </ul>
565 * @return {Object}
566 */
567HDPrivateKey.prototype.toObject = HDPrivateKey.prototype.toJSON = function toObject () {
568 return {
569 network: Network.get(this._buffers.version.readUInt32BE(0), 'xprivkey').name,
570 depth: this._buffers.depth[0],
571 fingerPrint: this.fingerPrint.readUInt32BE(0),
572 parentFingerPrint: this._buffers.parentFingerPrint.readUInt32BE(0),
573 childIndex: this._buffers.childIndex.readUInt32BE(0),
574 chainCode: this._buffers.chainCode.toString('hex'),
575 privateKey: this.privateKey.toBuffer().toString('hex'),
576 checksum: this._buffers.checksum.readUInt32BE(0),
577 xprivkey: this.xprivkey
578 }
579}
580
581/**
582 * Build a HDPrivateKey from a buffer
583 *
584 * @param {Buffer} arg
585 * @return {HDPrivateKey}
586 */
587HDPrivateKey.fromBuffer = function (buf) {
588 return new HDPrivateKey(buf.toString())
589}
590
591/**
592 * Build a HDPrivateKey from a hex string
593 *
594 * @param {string} hex
595 * @return {HDPrivateKey}
596 */
597HDPrivateKey.fromHex = function (hex) {
598 return HDPrivateKey.fromBuffer(Buffer.from(hex, 'hex'))
599}
600
601/**
602 * Returns a buffer representation of the HDPrivateKey
603 *
604 * @return {string}
605 */
606HDPrivateKey.prototype.toBuffer = function () {
607 return Buffer.from(this.toString())
608}
609
610/**
611 * Returns a hex string representation of the HDPrivateKey
612 *
613 * @return {string}
614 */
615HDPrivateKey.prototype.toHex = function () {
616 return this.toBuffer().toString('hex')
617}
618
619HDPrivateKey.DefaultDepth = 0
620HDPrivateKey.DefaultFingerprint = 0
621HDPrivateKey.DefaultChildIndex = 0
622HDPrivateKey.Hardened = 0x80000000
623HDPrivateKey.MaxIndex = 2 * HDPrivateKey.Hardened
624
625HDPrivateKey.RootElementAlias = ['m', 'M', 'm\'', 'M\'']
626
627HDPrivateKey.VersionSize = 4
628HDPrivateKey.DepthSize = 1
629HDPrivateKey.ParentFingerPrintSize = 4
630HDPrivateKey.ChildIndexSize = 4
631HDPrivateKey.ChainCodeSize = 32
632HDPrivateKey.PrivateKeySize = 32
633HDPrivateKey.CheckSumSize = 4
634
635HDPrivateKey.DataLength = 78
636HDPrivateKey.SerializedByteSize = 82
637
638HDPrivateKey.VersionStart = 0
639HDPrivateKey.VersionEnd = HDPrivateKey.VersionStart + HDPrivateKey.VersionSize
640HDPrivateKey.DepthStart = HDPrivateKey.VersionEnd
641HDPrivateKey.DepthEnd = HDPrivateKey.DepthStart + HDPrivateKey.DepthSize
642HDPrivateKey.ParentFingerPrintStart = HDPrivateKey.DepthEnd
643HDPrivateKey.ParentFingerPrintEnd = HDPrivateKey.ParentFingerPrintStart + HDPrivateKey.ParentFingerPrintSize
644HDPrivateKey.ChildIndexStart = HDPrivateKey.ParentFingerPrintEnd
645HDPrivateKey.ChildIndexEnd = HDPrivateKey.ChildIndexStart + HDPrivateKey.ChildIndexSize
646HDPrivateKey.ChainCodeStart = HDPrivateKey.ChildIndexEnd
647HDPrivateKey.ChainCodeEnd = HDPrivateKey.ChainCodeStart + HDPrivateKey.ChainCodeSize
648HDPrivateKey.PrivateKeyStart = HDPrivateKey.ChainCodeEnd + 1
649HDPrivateKey.PrivateKeyEnd = HDPrivateKey.PrivateKeyStart + HDPrivateKey.PrivateKeySize
650HDPrivateKey.ChecksumStart = HDPrivateKey.PrivateKeyEnd
651HDPrivateKey.ChecksumEnd = HDPrivateKey.ChecksumStart + HDPrivateKey.CheckSumSize
652
653assert(HDPrivateKey.ChecksumEnd === HDPrivateKey.SerializedByteSize)
654
655module.exports = HDPrivateKey