Skip to content

Commit 8576356

Browse files
committed
fix: pass 'version' properly, for 'tprv' and 'testnet'
1 parent 32dc6cb commit 8576356

File tree

1 file changed

+110
-23
lines changed

1 file changed

+110
-23
lines changed

dashhd.js

Lines changed: 110 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* @prop {HDToXKeyBytes} toXPrvBytes
1515
* @prop {HDToXPub} toXPub
1616
* @prop {HDToXKeyBytes} toXPubBytes
17-
* @prop {HDUtils} utils
17+
* @prop {HDUtils} _utils
1818
* @prop {HDWipePrivates} wipePrivateData - randomizes private key buffer in-place
1919
* @prop {HDToPublic} toPublic - returns public key
2020
* @prop {Number} HARDENED_OFFSET - 0x80000000
@@ -61,7 +61,7 @@
6161

6262
/**
6363
* @typedef HDKeyOptions
64-
* @prop {HDVersions?} [versions]
64+
* @prop {HDVersionsOption?} [versions]
6565
* @prop {Number?} [depth]
6666
* @prop {Number?} [parentFingerprint]
6767
* @prop {Number} index
@@ -106,25 +106,41 @@ var DashHd = ("object" === typeof module && exports) || {};
106106
/**
107107
* @param {Uint8Array} keyBytes
108108
* @param {Object} opts
109-
* @param {Number} [opts.version]
109+
* @param {String|Uint32} [opts.version]
110110
*/
111111
Utils.encodeXPrv = async function (keyBytes, opts) {
112112
let version = "xprv";
113113
if (opts?.version) {
114-
version = opts?.version.toString(16);
115-
version = version.padStart(8, "0");
114+
if (opts.version === "tprv") {
115+
version = opts.version;
116+
} else {
117+
// intended for numbers, but won't break hex strings
118+
version = opts.version.toString(16);
119+
version = version.padStart(8, "0");
120+
}
116121
}
117122
//@ts-ignore
118123
return await DashKeys.encodeKey(keyBytes, { version });
119124
};
120125

121126
/**
122127
* @param {Uint8Array} keyBytes
123-
* TODO - pass tpub
128+
* @param {Object} opts
129+
* @param {String|Uint32} [opts.version]
124130
*/
125-
Utils.encodeXPub = async function (keyBytes) {
131+
Utils.encodeXPub = async function (keyBytes, opts) {
132+
let version = "xpub";
133+
if (opts?.version) {
134+
if (opts.version === "tpub") {
135+
version = opts.version;
136+
} else {
137+
// intended for numbers, but won't break hex strings
138+
version = opts.version.toString(16);
139+
version = version.padStart(8, "0");
140+
}
141+
}
126142
//@ts-ignore
127-
return await DashKeys.encodeKey(keyBytes, { version: "xpub" });
143+
return await DashKeys.encodeKey(keyBytes, { version: version });
128144
};
129145
/** @type {HDKeyTweak} */
130146
Utils.privateKeyTweakAdd = async function (privateKeyCopy, tweak) {
@@ -247,6 +263,7 @@ var DashHd = ("object" === typeof module && exports) || {};
247263
let XKEY_SIZE = 74;
248264
let XKEY_DEPTH = 4; // m/44'/5'/0'/<0>[/0]
249265

266+
//@ts-ignore
250267
DashHd._utils = Utils;
251268

252269
// Bitcoin defaults hard-coded by default.
@@ -296,19 +313,21 @@ var DashHd = ("object" === typeof module && exports) || {};
296313
if (privBytes.length !== 32) {
297314
throw new Error("expected a private key (size 32)");
298315
}
299-
return await DashKeys.encodeKey(privBytes);
316+
return await DashKeys.encodeKey(privBytes, opts);
300317
};
301318

302-
DashHd.toXPrv = async function (hdkey) {
319+
DashHd.toXPrv = async function (hdkey, opts) {
303320
//@ts-ignore - will throw if null
304321
let xprvBytes = DashHd._toXBytes(hdkey, hdkey.privateKey);
305322
//@ts-ignore - wth?
306-
let xprv = await Utils.encodeXPrv(xprvBytes);
323+
let xprv = await Utils.encodeXPrv(xprvBytes, opts);
307324
return xprv;
308325
};
309326

310327
// TODO - missing custom version
311328
DashHd.toXPrvBytes = function (hdkey, opts) {
329+
/** @type {Number} */
330+
//@ts-ignore
312331
let version = opts?.version || DashHd.MAINNET.private;
313332

314333
//@ts-ignore - will throw if null
@@ -320,14 +339,28 @@ var DashHd = ("object" === typeof module && exports) || {};
320339
return xprvBytes;
321340
};
322341

323-
DashHd.toXPub = async function (hdkey) {
342+
DashHd.toXPub = async function (hdkey, opts) {
324343
let xpubBytes = DashHd._toXBytes(hdkey, hdkey.publicKey);
325-
let xpub = await Utils.encodeXPub(xpubBytes);
344+
let xpub = await Utils.encodeXPub(xpubBytes, opts);
326345
return xpub;
327346
};
328347

329348
DashHd.toXPubBytes = function (hdkey, opts) {
330-
let version = opts?.version || DashHd.MAINNET.public;
349+
/** @type {Uint32} */
350+
//@ts-ignore - it's a number, I promise
351+
let version = DashHd.MAINNET.public;
352+
if (opts?.version) {
353+
let [_, versionUint32] = // jshint ignore:line
354+
_versionToTuple(
355+
opts.version,
356+
"xpub",
357+
//@ts-ignore - it's a number, I promise
358+
DashHd.MAINNET.public,
359+
"tpub",
360+
DashHd.TESTNET.public,
361+
);
362+
version = versionUint32;
363+
}
331364

332365
let xpubPart = DashHd._toXBytes(hdkey, hdkey.publicKey);
333366
let xpubBytes = new Uint8Array(xpubPart.length + 4);
@@ -605,11 +638,17 @@ var DashHd = ("object" === typeof module && exports) || {};
605638
publicKey = key;
606639
}
607640

608-
let xprvHex = "0x0" + versions.private.toString(16);
609-
let xpubHex = "0x0" + versions.public.toString(16);
610641
if (publicKey) {
642+
let [xpubHex, xpubUint32] = _versionToTuple(
643+
versions.public,
644+
"xpub",
645+
//@ts-ignore - it's a number, I promise
646+
DashHd.MAINNET.public,
647+
"tpub",
648+
DashHd.TESTNET.public,
649+
);
611650
assert(
612-
version === versions.public,
651+
version === xpubUint32,
613652
`Version mismatch: version does not match ${xpubHex} (public)`,
614653
);
615654
// at one point xy pubs (1 + 64 bytes) were allowed (per spec)
@@ -619,8 +658,16 @@ var DashHd = ("object" === typeof module && exports) || {};
619658
publicKey = await Utils.publicKeyNormalize(publicKey);
620659
}
621660
} else {
661+
let [xprvHex, xprvUint32] = _versionToTuple(
662+
versions.private,
663+
"xprv",
664+
//@ts-ignore - it's a number, I promise
665+
DashHd.MAINNET.private,
666+
"tprv",
667+
DashHd.TESTNET.private,
668+
);
622669
assert(
623-
version === versions.private,
670+
version === xprvUint32,
624671
`Version mismatch: version does not match ${xprvHex} (private)`,
625672
);
626673
publicKey = await Utils.toPublicKey(privateKey);
@@ -652,6 +699,33 @@ var DashHd = ("object" === typeof module && exports) || {};
652699
return hdkey;
653700
};
654701

702+
/**
703+
*
704+
* @param {String|Number} version
705+
* @param {String} mainStr - 'xprv'|'xpub'
706+
* @param {Number} mainInt - DashHd.MAINNET.private|DashHd.MAINNET.public
707+
* @param {String} testStr - 'tprv'|'tpub'
708+
* @param {Number} testInt - DashHd.TESTNET.private|DashHd.TESTNET.publi c
709+
* @returns {[String, Number]}
710+
*/
711+
function _versionToTuple(version, mainStr, mainInt, testInt, testStr) {
712+
let isMainnet = version === mainStr || version === "mainnet";
713+
let isTestnet = version === testStr || version === "testnet";
714+
if (isMainnet) {
715+
version = mainInt;
716+
} else if (isTestnet) {
717+
version = testInt;
718+
}
719+
// intended for uint32, but doesn't break hex
720+
let xkeyHex = version.toString(16);
721+
xkeyHex = xkeyHex.padStart(8, "0");
722+
let xkeyUint32 = parseInt(xkeyHex, 16);
723+
xkeyUint32 = xkeyUint32 >>> 0; /* jshint ignore:line */ // coerce uint32
724+
xkeyHex = `0x${xkeyHex}`;
725+
726+
return [xkeyHex, xkeyUint32];
727+
}
728+
655729
DashHd.toId = async function (hdkey) {
656730
let idBytes = await DashHd.toIdBytes(hdkey);
657731
let id = Utils.bytesToBase64Url(idBytes);
@@ -721,8 +795,14 @@ if ("object" === typeof module) {
721795

722796
/**
723797
* @typedef HDVersions
724-
* @prop {Number} private - 32-bit (4-byte) int (encodes to 'xprv' in base58)
725-
* @prop {Number} public - 32-bit (4-byte) int (encodes to 'xpub' in base58)
798+
* @prop {Uint32} private - 'mainnet', 'testnet', 'xprv' or 'tprv', or 4-byte hex or uint32
799+
* @prop {Uint32} public - 'mainnet', 'testnet', 'xpub' or 'tpub', or 4-byte hex or uint32
800+
*/
801+
802+
/**
803+
* @typedef HDVersionsOption
804+
* @prop {Uint32|String} [private] - 'mainnet', 'testnet', 'xprv' or 'tprv', or 4-byte hex or uint32
805+
* @prop {Uint32|String} [public] - 'mainnet', 'testnet', 'xpub' or 'tpub', or 4-byte hex or uint32
726806
*/
727807

728808
/**
@@ -825,8 +905,7 @@ if ("object" === typeof module) {
825905

826906
/**
827907
* @typedef HDFromXKeyOptions
828-
* @prop {HDVersions} [versions]
829-
* @prop {String} xkey - base58check-encoded xkey
908+
* @prop {HDVersionsOption} [versions]
830909
* @prop {Boolean} [bip32] - allow non-account depths
831910
* @prop {Boolean} [normalizePublicKey]
832911
* returns {Promise<HDKey>}
@@ -843,7 +922,7 @@ if ("object" === typeof module) {
843922
* @typedef HDFromSeedOptions
844923
* @prop {Number} [purpose] - 44 (BIP-44) by default
845924
* @prop {Number} [coinType] - 5 (DASH) by default
846-
* @prop {HDVersions} [versions] - mainnet ('xprv', 'xpub') by default
925+
* @prop {HDVersionsOption} [versions] - mainnet ('xprv', 'xpub') by default
847926
*/
848927

849928
/**
@@ -904,12 +983,16 @@ if ("object" === typeof module) {
904983
/**
905984
* @callback HDToXPrv
906985
* @param {HDKey} hdkey
986+
* @param {Object} [opts]
987+
* @param {String|Uint32} [opts.version]
907988
* @returns {Promise<String>}
908989
*/
909990

910991
/**
911992
* @callback HDToXPub
912993
* @param {HDKey} hdkey
994+
* @param {Object} [opts]
995+
* @param {String|Uint32} [opts.version]
913996
* @returns {Promise<String>}
914997
*/
915998

@@ -966,3 +1049,7 @@ if ("object" === typeof module) {
9661049
* @callback HDWipePrivates
9671050
* @param {HDKey} hdkey
9681051
*/
1052+
1053+
/**
1054+
* @typedef {Number} Uint32
1055+
*/

0 commit comments

Comments
 (0)