4
4
* @prop {HDDeriveChild } deriveChild - get the next child xkey (in a path segment)
5
5
* @prop {HDDerivePath } derivePath - derive a full hd path from the given key
6
6
* @prop {HDFingerprint } fingerprint
7
- * @prop {HDFromSeed } fromMasterSeed
8
- * @prop {HDFromXKey } fromExtendedKey
7
+ * @prop {HDFromSeed } fromSeed
8
+ * @prop {HDFromXKey } fromXKey
9
9
* @prop {HDGetXPrv } getPrivateExtendedKey
10
10
* @prop {HDGetXPub } getPublicExtendedKey
11
11
* @prop {HDUtils } utils
12
12
* @prop {HDWipePrivates } wipePrivateData - randomizes private key buffer in-place
13
13
* @prop {Number } HARDENED_OFFSET - 0x80000000
14
+ * @prop {HDVersions } MAINNET - 'xprv' & 'xpub'
15
+ * @prop {HDVersions } TESTNET - 'tprv' & 'tpub'
14
16
* @prop {HDDeriveHelper } _derive - helper
15
17
*/
16
18
27
29
28
30
/**
29
31
* @callback HDCreate
30
- * @param {hdkey } opts
32
+ * @param {HDKey } opts
31
33
* @returns {HDKey }
32
34
*/
33
35
@@ -212,12 +214,14 @@ var DashHd = ("object" === typeof module && exports) || {};
212
214
let KEY_SIZE = 33 ;
213
215
let INDEXED_KEY_SIZE = 4 + KEY_SIZE ;
214
216
let XKEY_SIZE = 74 ;
217
+ let ACCOUNT_DEPTH = 3 ; // m/44'/5'/<0'[/0/0]
215
218
216
219
// Bitcoin hardcoded by default, can use package `coininfo` for others
217
- let BITCOIN_VERSIONS = { private : 0x0488ade4 , public : 0x0488b21e } ;
220
+ DashHd . MAINNET = { private : 0x0488ade4 , public : 0x0488b21e } ;
221
+ DashHd . TESTNET = { private : 0x043587cf , public : 0x04358394 } ;
218
222
219
223
DashHd . create = function ( {
220
- versions = BITCOIN_VERSIONS ,
224
+ versions = DashHd . MAINNET ,
221
225
depth = 0 ,
222
226
parentFingerprint = 0 ,
223
227
index,
@@ -400,7 +404,7 @@ var DashHd = ("object" === typeof module && exports) || {};
400
404
return n ;
401
405
}
402
406
403
- DashHd . fromMasterSeed = async function ( seed , versions = BITCOIN_VERSIONS ) {
407
+ DashHd . fromSeed = async function ( { seed, versions = DashHd . MAINNET } ) {
404
408
let chainAndKeys = await DashHd . _derive ( seed , {
405
409
chainCode : ROOT_CHAIN ,
406
410
} ) ;
@@ -416,27 +420,20 @@ var DashHd = ("object" === typeof module && exports) || {};
416
420
) ;
417
421
} ;
418
422
419
- DashHd . fromExtendedKey = async function (
420
- base58key ,
421
- versions ,
422
- skipVerification ,
423
- ) {
423
+ DashHd . fromXKey = async function ( {
424
+ xkey, // base58key,
425
+ versions = DashHd . MAINNET ,
426
+ normalizePublicKey = false ,
427
+ bip32 = false ,
428
+ } ) {
424
429
// => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
425
- versions = versions || BITCOIN_VERSIONS ;
426
- skipVerification = skipVerification || false ;
427
430
428
- //@ts -ignore - wth?
429
- let keyInfo = await Utils . decode ( base58key ) ;
431
+ let keyInfo = await Utils . decode ( xkey ) ;
430
432
let keyBytes = DashKeys . utils . hexToBytes ( keyInfo . xprv || keyInfo . xpub ) ;
431
433
let keyDv = new DataView ( keyBytes . buffer , 0 , keyBytes . byteLength ) ;
432
434
433
435
//let version = keyDv.getUint32(0, BUFFER_BE);
434
- // TODO tprv, tpub
435
436
let version = parseInt ( keyInfo . version , 16 ) ;
436
- assert (
437
- version === versions . private || version === versions . public ,
438
- "Version mismatch: does not match private or public" ,
439
- ) ;
440
437
441
438
let privateKey ;
442
439
let publicKey ;
@@ -448,35 +445,47 @@ var DashHd = ("object" === typeof module && exports) || {};
448
445
publicKey = key ;
449
446
}
450
447
448
+ let xprvHex = "0x0" + versions . private . toString ( 16 ) ;
449
+ let xpubHex = "0x0" + versions . public . toString ( 16 ) ;
451
450
if ( publicKey ) {
452
451
assert (
453
452
version === versions . public ,
454
- "Version mismatch: version does not match public" ,
455
- ) ;
456
- assert (
457
- key . length === 33 || key . length === 65 ,
458
- "Public key must be 33 or 65 bytes." ,
453
+ `Version mismatch: version does not match ${ xpubHex } (public)` ,
459
454
) ;
460
- if ( ! skipVerification ) {
455
+ // at one point xy pubs (1 + 64 bytes) were allowed (per spec)
456
+ // but nothing in the ecosystem actually works that way
457
+ assert ( key . length === 33 , "Public key must be 33 (1 + 32) bytes." ) ;
458
+ if ( normalizePublicKey ) {
461
459
publicKey = await Utils . publicKeyNormalize ( publicKey ) ;
462
460
}
463
461
} else {
464
462
assert (
465
463
version === versions . private ,
466
- " Version mismatch: version does not match private" ,
464
+ ` Version mismatch: version does not match ${ xprvHex } ( private)` ,
467
465
) ;
468
466
publicKey = await Utils . toPublicKey ( privateKey ) ;
469
467
}
470
468
471
- // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
469
+ let depth = keyDv . getUint8 ( 0 ) ;
470
+ if ( ! bip32 ) {
471
+ if ( depth !== ACCOUNT_DEPTH ) {
472
+ throw new Error (
473
+ `XKey with depth=${ depth } does not represent an account (depth=${ ACCOUNT_DEPTH } )` ,
474
+ ) ;
475
+ }
476
+ }
477
+
478
+ let parentFingerprint = keyDv . getUint32 ( 1 , BUFFER_BE ) ;
479
+ let index = keyDv . getUint32 ( 5 , BUFFER_BE ) ;
480
+ let chainCode = keyBytes . subarray ( 9 , 41 ) ;
472
481
let hdkey = DashHd . create ( {
473
- versions : versions ,
474
- depth : keyDv . getUint8 ( 0 ) ,
475
- parentFingerprint : keyDv . getUint32 ( 1 , BUFFER_BE ) ,
476
- index : keyDv . getUint32 ( 5 , BUFFER_BE ) ,
477
- chainCode : keyBytes . subarray ( 9 , 41 ) ,
478
- privateKey : privateKey ,
479
- publicKey : publicKey ,
482
+ versions,
483
+ depth,
484
+ parentFingerprint,
485
+ index,
486
+ chainCode,
487
+ privateKey,
488
+ publicKey,
480
489
} ) ;
481
490
482
491
return hdkey ;
@@ -539,7 +548,7 @@ if ("object" === typeof module) {
539
548
540
549
/**
541
550
* @callback HDDeriveChild
542
- * @param {hdkey } hdkey
551
+ * @param {HDKey } hdkey
543
552
* @param {Number } index - includes HARDENED_OFFSET, if applicable
544
553
* @param {Boolean } hardened
545
554
* returns {Promise<HDKey>}
@@ -561,7 +570,7 @@ if ("object" === typeof module) {
561
570
562
571
/**
563
572
* @callback HDDerivePath
564
- * @param {hdkey } hdkey
573
+ * @param {HDKey } hdkey
565
574
* @param {String } path
566
575
* returns {Promise<HDKey>}
567
576
*/
@@ -574,33 +583,45 @@ if ("object" === typeof module) {
574
583
575
584
/**
576
585
* @callback HDFromXKey
577
- * @param {String } base58key - base58check-encoded xkey
578
- * @param {HDVersions } [versions]
579
- * @param {Boolean } [skipVerification]
586
+ * @param {HDFromXKeyOptions } opts
587
+ * returns {Promise<HDKey>}
588
+ */
589
+
590
+ /**
591
+ * @typedef HDFromXKeyOptions
592
+ * @prop {HDVersions } [versions]
593
+ * @prop {String } xkey - base58check-encoded xkey
594
+ * @prop {Boolean } [bip32] - allow non-account depths
595
+ * @prop {Boolean } [normalizePublicKey]
580
596
* returns {Promise<HDKey>}
581
597
*/
582
598
583
599
/**
584
600
* @callback HDFromSeed
585
- * @param {Uint8Array } seedBuffer
586
- * @param {HDVersions } [versions]
601
+ * @param {HDFromSeedOptions } opts
587
602
* @returns {Promise<HDKey> }
588
603
*/
589
604
605
+ /**
606
+ * @typedef HDFromSeedOptions
607
+ * @prop {Uint8Array } seed
608
+ * @prop {HDVersions } [versions]
609
+ */
610
+
590
611
/**
591
612
* @callback HDGetBuffer
592
613
* @returns {Uint8Array }
593
614
*/
594
615
595
616
/**
596
617
* @callback HDGetXPrv
597
- * @param {hdkey } hdkey
618
+ * @param {HDKey } hdkey
598
619
* @returns {Promise<String> }
599
620
*/
600
621
601
622
/**
602
623
* @callback HDGetXPub
603
- * @param {hdkey } hdkey
624
+ * @param {HDKey } hdkey
604
625
* @returns {Promise<String> }
605
626
*/
606
627
@@ -643,5 +664,5 @@ if ("object" === typeof module) {
643
664
644
665
/**
645
666
* @callback HDWipePrivates
646
- * @param {hdkey } hdkey
667
+ * @param {HDKey } hdkey
647
668
*/
0 commit comments