UNPKG

21.4 kBJavaScriptView Raw
1'use strict';
2
3
4var assert = require('assert');
5var buffer = require('buffer');
6var _ = require('lodash');
7var $ = require('./util/preconditions');
8
9var BN = require('./crypto/bn');
10var Base58 = require('./encoding/base58');
11var Base58Check = require('./encoding/base58check');
12var Hash = require('./crypto/hash');
13var Network = require('./networks');
14var Point = require('./crypto/point');
15var PrivateKey = require('./privatekey');
16var Random = require('./crypto/random');
17
18var errors = require('./errors');
19var hdErrors = errors.HDPrivateKey;
20var BufferUtil = require('./util/buffer');
21var JSUtil = require('./util/js');
22
23var MINIMUM_ENTROPY_BITS = 128;
24var BITS_TO_BYTES = 1 / 8;
25var MAXIMUM_ENTROPY_BITS = 512;
26
27
28/**
29 * Represents an instance of an hierarchically derived private key.
30 *
31 * More info on https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
32 *
33 * @constructor
34 * @param {string|Buffer|Object} arg
35 */
36function HDPrivateKey(arg) {
37 /* jshint maxcomplexity: 10 */
38 if (arg instanceof HDPrivateKey) {
39 return arg;
40 }
41 if (!(this instanceof HDPrivateKey)) {
42 return new HDPrivateKey(arg);
43 }
44 if (!arg) {
45 return this._generateRandomly();
46 }
47
48 if (Network.get(arg)) {
49 return this._generateRandomly(arg);
50 } else if (_.isString(arg) || BufferUtil.isBuffer(arg)) {
51 if (HDPrivateKey.isValidSerialized(arg)) {
52 this._buildFromSerialized(arg);
53 } else if (JSUtil.isValidJSON(arg)) {
54 this._buildFromJSON(arg);
55 } else if (BufferUtil.isBuffer(arg) && HDPrivateKey.isValidSerialized(arg.toString())) {
56 this._buildFromSerialized(arg.toString());
57 } else {
58 throw HDPrivateKey.getSerializedError(arg);
59 }
60 } else if (_.isObject(arg)) {
61 this._buildFromObject(arg);
62 } else {
63 throw new hdErrors.UnrecognizedArgument(arg);
64 }
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(arg, hardened) {
156 return this.deriveNonCompliantChild(arg, hardened);
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 /* jshint maxstatements: 20 */
226 /* jshint maxcomplexity: 10 */
227 if (!HDPrivateKey.isValidPath(index, hardened)) {
228 throw new hdErrors.InvalidPath(index);
229 }
230
231 hardened = index >= HDPrivateKey.Hardened ? true : hardened;
232 if (index < HDPrivateKey.Hardened && hardened === true) {
233 index += HDPrivateKey.Hardened;
234 }
235
236 var indexBuffer = BufferUtil.integerAsBuffer(index);
237 var data;
238 if (hardened && nonCompliant) {
239 // The private key serialization in this case will not be exactly 32 bytes and can be
240 // any value less, and the value is not zero-padded.
241 var nonZeroPadded = this.privateKey.bn.toBuffer();
242 data = BufferUtil.concat([Buffer.from([0]), nonZeroPadded, indexBuffer]);
243 } else if (hardened) {
244 // This will use a 32 byte zero padded serialization of the private key
245 var privateKeyBuffer = this.privateKey.bn.toBuffer({size: 32});
246 assert(privateKeyBuffer.length === 32, 'length of private key buffer is expected to be 32 bytes');
247 data = BufferUtil.concat([Buffer.from([0]), privateKeyBuffer, indexBuffer]);
248 } else {
249 data = BufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]);
250 }
251 var hash = Hash.sha512hmac(data, this._buffers.chainCode);
252 var leftPart = BN.fromBuffer(hash.slice(0, 32), {
253 size: 32
254 });
255 var chainCode = hash.slice(32, 64);
256
257 var privateKey = leftPart.add(this.privateKey.toBigNumber()).umod(Point.getN()).toBuffer({
258 size: 32
259 });
260
261 if (!PrivateKey.isValid(privateKey)) {
262 // Index at this point is already hardened, we can pass null as the hardened arg
263 return this._deriveWithNumber(index + 1, null, nonCompliant);
264 }
265
266 var derived = new HDPrivateKey({
267 network: this.network,
268 depth: this.depth + 1,
269 parentFingerPrint: this.fingerPrint,
270 childIndex: index,
271 chainCode: chainCode,
272 privateKey: privateKey
273 });
274
275 return derived;
276};
277
278HDPrivateKey.prototype._deriveFromString = function(path, nonCompliant) {
279 if (!HDPrivateKey.isValidPath(path)) {
280 throw new hdErrors.InvalidPath(path);
281 }
282
283 var indexes = HDPrivateKey._getDerivationIndexes(path);
284 var derived = indexes.reduce(function(prev, index) {
285 return prev._deriveWithNumber(index, null, nonCompliant);
286 }, this);
287
288 return derived;
289};
290
291/**
292 * Verifies that a given serialized private key in base58 with checksum format
293 * is valid.
294 *
295 * @param {string|Buffer} data - the serialized private key
296 * @param {string|Network=} network - optional, if present, checks that the
297 * network provided matches the network serialized.
298 * @return {boolean}
299 */
300HDPrivateKey.isValidSerialized = function(data, network) {
301 return !HDPrivateKey.getSerializedError(data, network);
302};
303
304/**
305 * Checks what's the error that causes the validation of a serialized private key
306 * in base58 with checksum to fail.
307 *
308 * @param {string|Buffer} data - the serialized private key
309 * @param {string|Network=} network - optional, if present, checks that the
310 * network provided matches the network serialized.
311 * @return {errors.InvalidArgument|null}
312 */
313HDPrivateKey.getSerializedError = function(data, network) {
314 /* jshint maxcomplexity: 10 */
315 if (!(_.isString(data) || BufferUtil.isBuffer(data))) {
316 return new hdErrors.UnrecognizedArgument('Expected string or buffer');
317 }
318 if (!Base58.validCharacters(data)) {
319 return new errors.InvalidB58Char('(unknown)', data);
320 }
321 try {
322 data = Base58Check.decode(data);
323 } catch (e) {
324 return new errors.InvalidB58Checksum(data);
325 }
326 if (data.length !== HDPrivateKey.DataLength) {
327 return new hdErrors.InvalidLength(data);
328 }
329 if (!_.isUndefined(network)) {
330 var error = HDPrivateKey._validateNetwork(data, network);
331 if (error) {
332 return error;
333 }
334 }
335 return null;
336};
337
338HDPrivateKey._validateNetwork = function(data, networkArg) {
339 var network = Network.get(networkArg);
340 if (!network) {
341 return new errors.InvalidNetworkArgument(networkArg);
342 }
343 var version = data.slice(0, 4);
344 if (BufferUtil.integerFromBuffer(version) !== network.xprivkey) {
345 return new errors.InvalidNetwork(version);
346 }
347 return null;
348};
349
350HDPrivateKey.fromString = function(arg) {
351 $.checkArgument(_.isString(arg), 'No valid string was provided');
352 return new HDPrivateKey(arg);
353};
354
355HDPrivateKey.fromObject = function(arg) {
356 $.checkArgument(_.isObject(arg), 'No valid argument was provided');
357 return new HDPrivateKey(arg);
358};
359
360HDPrivateKey.prototype._buildFromJSON = function(arg) {
361 return this._buildFromObject(JSON.parse(arg));
362};
363
364HDPrivateKey.prototype._buildFromObject = function(arg) {
365 /* jshint maxcomplexity: 12 */
366 // TODO: Type validation
367 var buffers = {
368 version: arg.network ? BufferUtil.integerAsBuffer(Network.get(arg.network).xprivkey) : arg.version,
369 depth: _.isNumber(arg.depth) ? BufferUtil.integerAsSingleByteBuffer(arg.depth) : arg.depth,
370 parentFingerPrint: _.isNumber(arg.parentFingerPrint) ? BufferUtil.integerAsBuffer(arg.parentFingerPrint) : arg.parentFingerPrint,
371 childIndex: _.isNumber(arg.childIndex) ? BufferUtil.integerAsBuffer(arg.childIndex) : arg.childIndex,
372 chainCode: _.isString(arg.chainCode) ? Buffer.from(arg.chainCode,'hex') : arg.chainCode,
373 privateKey: (_.isString(arg.privateKey) && JSUtil.isHexa(arg.privateKey)) ? Buffer.from(arg.privateKey,'hex') : arg.privateKey,
374 checksum: arg.checksum ? (arg.checksum.length ? arg.checksum : BufferUtil.integerAsBuffer(arg.checksum)) : undefined
375 };
376 return this._buildFromBuffers(buffers);
377};
378
379HDPrivateKey.prototype._buildFromSerialized = function(arg) {
380 var decoded = Base58Check.decode(arg);
381 var buffers = {
382 version: decoded.slice(HDPrivateKey.VersionStart, HDPrivateKey.VersionEnd),
383 depth: decoded.slice(HDPrivateKey.DepthStart, HDPrivateKey.DepthEnd),
384 parentFingerPrint: decoded.slice(HDPrivateKey.ParentFingerPrintStart,
385 HDPrivateKey.ParentFingerPrintEnd),
386 childIndex: decoded.slice(HDPrivateKey.ChildIndexStart, HDPrivateKey.ChildIndexEnd),
387 chainCode: decoded.slice(HDPrivateKey.ChainCodeStart, HDPrivateKey.ChainCodeEnd),
388 privateKey: decoded.slice(HDPrivateKey.PrivateKeyStart, HDPrivateKey.PrivateKeyEnd),
389 checksum: decoded.slice(HDPrivateKey.ChecksumStart, HDPrivateKey.ChecksumEnd),
390 xprivkey: arg
391 };
392 return this._buildFromBuffers(buffers);
393};
394
395HDPrivateKey.prototype._generateRandomly = function(network) {
396 return HDPrivateKey.fromSeed(Random.getRandomBuffer(64), network);
397};
398
399/**
400 * Generate a private key from a seed, as described in BIP32
401 *
402 * @param {string|Buffer} hexa
403 * @param {*} network
404 * @return HDPrivateKey
405 */
406HDPrivateKey.fromSeed = function(hexa, network) {
407 /* jshint maxcomplexity: 8 */
408 if (JSUtil.isHexaString(hexa)) {
409 hexa = Buffer.from(hexa, 'hex');
410 }
411 if (!Buffer.isBuffer(hexa)) {
412 throw new hdErrors.InvalidEntropyArgument(hexa);
413 }
414 if (hexa.length < MINIMUM_ENTROPY_BITS * BITS_TO_BYTES) {
415 throw new hdErrors.InvalidEntropyArgument.NotEnoughEntropy(hexa);
416 }
417 if (hexa.length > MAXIMUM_ENTROPY_BITS * BITS_TO_BYTES) {
418 throw new hdErrors.InvalidEntropyArgument.TooMuchEntropy(hexa);
419 }
420 var hash = Hash.sha512hmac(hexa, Buffer.from('Bitcoin seed'));
421
422 return new HDPrivateKey({
423 network: Network.get(network) || Network.defaultNetwork,
424 depth: 0,
425 parentFingerPrint: 0,
426 childIndex: 0,
427 privateKey: hash.slice(0, 32),
428 chainCode: hash.slice(32, 64)
429 });
430};
431
432
433
434HDPrivateKey.prototype._calcHDPublicKey = function() {
435 if (!this._hdPublicKey) {
436 var HDPublicKey = require('./hdpublickey');
437 this._hdPublicKey = new HDPublicKey(this);
438 }
439};
440
441/**
442 * Receives a object with buffers in all the properties and populates the
443 * internal structure
444 *
445 * @param {Object} arg
446 * @param {buffer.Buffer} arg.version
447 * @param {buffer.Buffer} arg.depth
448 * @param {buffer.Buffer} arg.parentFingerPrint
449 * @param {buffer.Buffer} arg.childIndex
450 * @param {buffer.Buffer} arg.chainCode
451 * @param {buffer.Buffer} arg.privateKey
452 * @param {buffer.Buffer} arg.checksum
453 * @param {string=} arg.xprivkey - if set, don't recalculate the base58
454 * representation
455 * @return {HDPrivateKey} this
456 */
457HDPrivateKey.prototype._buildFromBuffers = function(arg) {
458 /* jshint maxcomplexity: 8 */
459 /* jshint maxstatements: 20 */
460
461 HDPrivateKey._validateBufferArguments(arg);
462
463 JSUtil.defineImmutable(this, {
464 _buffers: arg
465 });
466
467 var sequence = [
468 arg.version, arg.depth, arg.parentFingerPrint, arg.childIndex, arg.chainCode,
469 BufferUtil.emptyBuffer(1), arg.privateKey
470 ];
471 var concat = buffer.Buffer.concat(sequence);
472 if (!arg.checksum || !arg.checksum.length) {
473 arg.checksum = Base58Check.checksum(concat);
474 } else {
475 if (arg.checksum.toString() !== Base58Check.checksum(concat).toString()) {
476 throw new errors.InvalidB58Checksum(concat);
477 }
478 }
479
480 var network = Network.get(BufferUtil.integerFromBuffer(arg.version));
481 var xprivkey;
482 xprivkey = Base58Check.encode(buffer.Buffer.concat(sequence));
483 arg.xprivkey = Buffer.from(xprivkey);
484
485 var privateKey = new PrivateKey(BN.fromBuffer(arg.privateKey), network);
486 var publicKey = privateKey.toPublicKey();
487 var size = HDPrivateKey.ParentFingerPrintSize;
488 var fingerPrint = Hash.sha256ripemd160(publicKey.toBuffer()).slice(0, size);
489
490 JSUtil.defineImmutable(this, {
491 xprivkey: xprivkey,
492 network: network,
493 depth: BufferUtil.integerFromSingleByteBuffer(arg.depth),
494 privateKey: privateKey,
495 publicKey: publicKey,
496 fingerPrint: fingerPrint
497 });
498
499 this._hdPublicKey = null;
500
501 Object.defineProperty(this, 'hdPublicKey', {
502 configurable: false,
503 enumerable: true,
504 get: function() {
505 this._calcHDPublicKey();
506 return this._hdPublicKey;
507 }
508 });
509 Object.defineProperty(this, 'xpubkey', {
510 configurable: false,
511 enumerable: true,
512 get: function() {
513 this._calcHDPublicKey();
514 return this._hdPublicKey.xpubkey;
515 }
516 });
517 return this;
518};
519
520HDPrivateKey._validateBufferArguments = function(arg) {
521 var checkBuffer = function(name, size) {
522 var buff = arg[name];
523 assert(BufferUtil.isBuffer(buff), name + ' argument is not a buffer');
524 assert(
525 buff.length === size,
526 name + ' has not the expected size: found ' + buff.length + ', expected ' + size
527 );
528 };
529 checkBuffer('version', HDPrivateKey.VersionSize);
530 checkBuffer('depth', HDPrivateKey.DepthSize);
531 checkBuffer('parentFingerPrint', HDPrivateKey.ParentFingerPrintSize);
532 checkBuffer('childIndex', HDPrivateKey.ChildIndexSize);
533 checkBuffer('chainCode', HDPrivateKey.ChainCodeSize);
534 checkBuffer('privateKey', HDPrivateKey.PrivateKeySize);
535 if (arg.checksum && arg.checksum.length) {
536 checkBuffer('checksum', HDPrivateKey.CheckSumSize);
537 }
538};
539
540/**
541 * Returns the string representation of this private key (a string starting
542 * with "xprv..."
543 *
544 * @return string
545 */
546HDPrivateKey.prototype.toString = function() {
547 return this.xprivkey;
548};
549
550/**
551 * Returns the console representation of this extended private key.
552 * @return string
553 */
554HDPrivateKey.prototype.inspect = function() {
555 return '<HDPrivateKey: ' + this.xprivkey + '>';
556};
557
558/**
559 * Returns a plain object with a representation of this private key.
560 *
561 * Fields include:<ul>
562 * <li> network: either 'livenet' or 'testnet'
563 * <li> depth: a number ranging from 0 to 255
564 * <li> fingerPrint: a number ranging from 0 to 2^32-1, taken from the hash of the
565 * <li> associated public key
566 * <li> parentFingerPrint: a number ranging from 0 to 2^32-1, taken from the hash
567 * <li> of this parent's associated public key or zero.
568 * <li> childIndex: the index from which this child was derived (or zero)
569 * <li> chainCode: an hexa string representing a number used in the derivation
570 * <li> privateKey: the private key associated, in hexa representation
571 * <li> xprivkey: the representation of this extended private key in checksum
572 * <li> base58 format
573 * <li> checksum: the base58 checksum of xprivkey
574 * </ul>
575 * @return {Object}
576 */
577HDPrivateKey.prototype.toObject = HDPrivateKey.prototype.toJSON = function toObject() {
578 return {
579 network: Network.get(BufferUtil.integerFromBuffer(this._buffers.version), 'xprivkey').name,
580 depth: BufferUtil.integerFromSingleByteBuffer(this._buffers.depth),
581 fingerPrint: BufferUtil.integerFromBuffer(this.fingerPrint),
582 parentFingerPrint: BufferUtil.integerFromBuffer(this._buffers.parentFingerPrint),
583 childIndex: BufferUtil.integerFromBuffer(this._buffers.childIndex),
584 chainCode: BufferUtil.bufferToHex(this._buffers.chainCode),
585 privateKey: this.privateKey.toBuffer().toString('hex'),
586 checksum: BufferUtil.integerFromBuffer(this._buffers.checksum),
587 xprivkey: this.xprivkey
588 };
589};
590
591/**
592 * Build a HDPrivateKey from a buffer
593 *
594 * @param {Buffer} arg
595 * @return {HDPrivateKey}
596 */
597HDPrivateKey.fromBuffer = function(arg) {
598 return new HDPrivateKey(arg.toString());
599};
600
601/**
602 * Returns a buffer representation of the HDPrivateKey
603 *
604 * @return {string}
605 */
606HDPrivateKey.prototype.toBuffer = function() {
607 return BufferUtil.copy(this._buffers.xprivkey);
608};
609
610HDPrivateKey.DefaultDepth = 0;
611HDPrivateKey.DefaultFingerprint = 0;
612HDPrivateKey.DefaultChildIndex = 0;
613HDPrivateKey.Hardened = 0x80000000;
614HDPrivateKey.MaxIndex = 2 * HDPrivateKey.Hardened;
615
616HDPrivateKey.RootElementAlias = ['m', 'M', 'm\'', 'M\''];
617
618HDPrivateKey.VersionSize = 4;
619HDPrivateKey.DepthSize = 1;
620HDPrivateKey.ParentFingerPrintSize = 4;
621HDPrivateKey.ChildIndexSize = 4;
622HDPrivateKey.ChainCodeSize = 32;
623HDPrivateKey.PrivateKeySize = 32;
624HDPrivateKey.CheckSumSize = 4;
625
626HDPrivateKey.DataLength = 78;
627HDPrivateKey.SerializedByteSize = 82;
628
629HDPrivateKey.VersionStart = 0;
630HDPrivateKey.VersionEnd = HDPrivateKey.VersionStart + HDPrivateKey.VersionSize;
631HDPrivateKey.DepthStart = HDPrivateKey.VersionEnd;
632HDPrivateKey.DepthEnd = HDPrivateKey.DepthStart + HDPrivateKey.DepthSize;
633HDPrivateKey.ParentFingerPrintStart = HDPrivateKey.DepthEnd;
634HDPrivateKey.ParentFingerPrintEnd = HDPrivateKey.ParentFingerPrintStart + HDPrivateKey.ParentFingerPrintSize;
635HDPrivateKey.ChildIndexStart = HDPrivateKey.ParentFingerPrintEnd;
636HDPrivateKey.ChildIndexEnd = HDPrivateKey.ChildIndexStart + HDPrivateKey.ChildIndexSize;
637HDPrivateKey.ChainCodeStart = HDPrivateKey.ChildIndexEnd;
638HDPrivateKey.ChainCodeEnd = HDPrivateKey.ChainCodeStart + HDPrivateKey.ChainCodeSize;
639HDPrivateKey.PrivateKeyStart = HDPrivateKey.ChainCodeEnd + 1;
640HDPrivateKey.PrivateKeyEnd = HDPrivateKey.PrivateKeyStart + HDPrivateKey.PrivateKeySize;
641HDPrivateKey.ChecksumStart = HDPrivateKey.PrivateKeyEnd;
642HDPrivateKey.ChecksumEnd = HDPrivateKey.ChecksumStart + HDPrivateKey.CheckSumSize;
643
644assert(HDPrivateKey.ChecksumEnd === HDPrivateKey.SerializedByteSize);
645
646module.exports = HDPrivateKey;