Skip to content

Commit 86b788a

Browse files
author
AJ ONeal
committed
feat: export raw (serialized) XKey bytes
1 parent 298286e commit 86b788a

File tree

2 files changed

+84
-46
lines changed

2 files changed

+84
-46
lines changed

dashhd.js

Lines changed: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
* @prop {HDToAddr} toAddr
1010
* @prop {HDToWif} toWif
1111
* @prop {HDToXPrv} toXPrv
12+
* @prop {HDToXKeyBytes} toXPrvBytes
1213
* @prop {HDToXPub} toXPub
14+
* @prop {HDToXKeyBytes} toXPubBytes
1315
* @prop {HDUtils} utils
1416
* @prop {HDWipePrivates} wipePrivateData - randomizes private key buffer in-place
1517
* @prop {Number} HARDENED_OFFSET - 0x80000000
@@ -22,6 +24,7 @@
2224
* @prop {HDCreateAccountKey} _createAccount - helper
2325
* @prop {HDCreateXKey} _createXKey - helper
2426
* @prop {HDDeriveHelper} _derive - helper
27+
* @prop {HDToXBytes} _toXBytes - helper
2528
*/
2629

2730
/**
@@ -295,20 +298,69 @@ var DashHd = ("object" === typeof module && exports) || {};
295298
};
296299

297300
DashHd.toXPrv = async function (hdkey) {
298-
if (!hdkey.privateKey) {
299-
return null;
300-
}
301-
301+
//@ts-ignore - will throw if null
302+
let xprvBytes = DashHd._toXBytes(hdkey, hdkey.privateKey);
302303
//@ts-ignore - wth?
303-
return await Utils.encodeXPrv(serialize(hdkey, hdkey.privateKey));
304+
let xprv = await Utils.encodeXPrv(xprvBytes);
305+
return xprv;
306+
};
307+
308+
// TODO - missing custom version
309+
DashHd.toXPrvBytes = function (hdkey) {
310+
//@ts-ignore - will throw if null
311+
let xprvPart = DashHd._toXBytes(hdkey, hdkey.privateKey);
312+
let xprvBytes = new Uint8Array(xprvPart.length + 4);
313+
let xkeyDv = new DataView(xprvBytes.buffer);
314+
xkeyDv.setUint32(0, DashHd.MAINNET.private, BUFFER_BE);
315+
xprvBytes.set(xprvPart, 4);
316+
return xprvBytes;
304317
};
305318

306319
DashHd.toXPub = async function (hdkey) {
307-
if (!hdkey.publicKey) {
308-
throw new Error("Missing public key");
320+
let xpubBytes = DashHd._toXBytes(hdkey, hdkey.publicKey);
321+
let xpub = await Utils.encodeXPub(xpubBytes);
322+
return xpub;
323+
};
324+
325+
// TODO - missing custom version
326+
DashHd.toXPubBytes = function (hdkey) {
327+
let xpubPart = DashHd._toXBytes(hdkey, hdkey.publicKey);
328+
let xpubBytes = new Uint8Array(xpubPart.length + 4);
329+
let xkeyDv = new DataView(xpubBytes.buffer);
330+
xkeyDv.setUint32(0, DashHd.MAINNET.private, BUFFER_BE);
331+
xpubBytes.set(xpubPart, 4);
332+
return xpubBytes;
333+
};
334+
335+
DashHd._toXBytes = function (hdkey, keyBytes) {
336+
if (!keyBytes) {
337+
throw new Error("missing key bytes");
309338
}
339+
// version(4) is part of Base58Check (perhaps mistakenly)
340+
// depth(1) + fingerprint(4) + index(4) + chain(32) + key(1 + 32)
341+
let xkey = new Uint8Array(XKEY_SIZE);
342+
let xkeyDv = new DataView(xkey.buffer);
343+
344+
xkeyDv.setUint8(0, hdkey.depth);
310345

311-
return await Utils.encodeXPub(serialize(hdkey, hdkey.publicKey));
346+
let fingerprint = 0x00000000;
347+
if (hdkey.depth > 0) {
348+
fingerprint = hdkey.parentFingerprint;
349+
}
350+
xkeyDv.setUint32(1, fingerprint, BUFFER_BE);
351+
xkeyDv.setUint32(5, hdkey.index, BUFFER_BE);
352+
353+
xkey.set(hdkey.chainCode, 9);
354+
355+
let keyStart = 41;
356+
let isPrivate = 32 === keyBytes.length;
357+
if (isPrivate) {
358+
xkey[keyStart] = 0x00;
359+
keyStart += 1;
360+
}
361+
xkey.set(keyBytes, keyStart);
362+
363+
return xkey;
312364
};
313365

314366
// IMPORTANT: never allow `await` (or other async) between writing to
@@ -607,37 +659,6 @@ var DashHd = ("object" === typeof module && exports) || {};
607659
}
608660
}
609661

610-
/**
611-
* @param {HDKey} hdkey - TODO attach to hdkey
612-
* @param {Uint8Array} keyBytes
613-
*/
614-
function serialize(hdkey, keyBytes) {
615-
// version(4) + depth(1) + fingerprint(4) + index(4) + chain(32) + key(1 + 32)
616-
let xkey = new Uint8Array(XKEY_SIZE);
617-
let xkeyDv = new DataView(xkey.buffer);
618-
619-
xkeyDv.setUint8(0, hdkey.depth);
620-
621-
let fingerprint = 0x00000000;
622-
if (hdkey.depth > 0) {
623-
fingerprint = hdkey.parentFingerprint;
624-
}
625-
xkeyDv.setUint32(1, fingerprint, BUFFER_BE);
626-
xkeyDv.setUint32(5, hdkey.index, BUFFER_BE);
627-
628-
xkey.set(hdkey.chainCode, 9);
629-
630-
let keyStart = 41;
631-
let isPrivate = 32 === keyBytes.length;
632-
if (isPrivate) {
633-
xkey[keyStart] = 0x00;
634-
keyStart += 1;
635-
}
636-
xkey.set(keyBytes, keyStart);
637-
638-
return xkey;
639-
}
640-
641662
DashHd.HARDENED_OFFSET = HARDENED_OFFSET;
642663
})(("object" === typeof window && window) || {}, DashHd);
643664
if ("object" === typeof module) {
@@ -794,6 +815,19 @@ if ("object" === typeof module) {
794815
* @returns {Promise<String>}
795816
*/
796817

818+
/**
819+
* @callback HDToXKeyBytes
820+
* @param {HDKey} hdkey
821+
* @returns {Uint8Array}
822+
*/
823+
824+
/**
825+
* @callback HDToXBytes
826+
* @param {HDKey} hdkey
827+
* @param {Uint8Array?} keyBytes
828+
* @returns {Uint8Array}
829+
*/
830+
797831
/**
798832
* @callback HDToWif
799833
* @param {Uint8Array} privBytes

test/hdkey.test.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -274,18 +274,18 @@ describe("hdkey", function () {
274274
});
275275

276276
describe("> when private key is null", function () {
277-
it("privateExtendedKey should return null and not throw", async function () {
277+
it("toXPrv should throw", async function () {
278278
var seed = "000102030405060708090a0b0c0d0e0f";
279279
var masterKey = await DashHd.fromSeed(hexToU8(seed));
280280

281281
assert.ok(await DashHd.toXPrv(masterKey), "xpriv is truthy");
282282
DashHd.wipePrivateData(masterKey);
283283

284-
assert.doesNotThrow(async function () {
285-
await masterKey.toXPrv();
284+
await assert.rejects(async function () {
285+
await DashHd.toXPrv(masterKey);
286286
});
287287

288-
assert.ok(!(await DashHd.toXPrv(masterKey)), "xpriv is falsy");
288+
//assert.ok(!(await DashHd.toXPrv(masterKey)), "xpriv is falsy");
289289
});
290290
});
291291

@@ -314,14 +314,18 @@ describe("hdkey", function () {
314314
const hdkey = await DashHd.fromSeed(hexToU8(fixtures.valid[6].seed));
315315
DashHd.wipePrivateData(hdkey);
316316
assert.equal(hdkey.privateKey, null);
317-
assert.equal(await DashHd.toXPrv(hdkey), null);
318-
assert.rejects(async function () {
317+
await assert.rejects(async function () {
318+
await DashHd.toXPrv(hdkey);
319+
});
320+
await assert.rejects(async function () {
319321
await sign(hdkey, new Uint8Array(32));
320322
}, "shouldn't be able to sign");
321323
const childKey = await DashHd.derivePath(hdkey, "m/0");
322324
assert.equal(await DashHd.toXPub(childKey), fixtures.valid[7].public);
323325
assert.equal(childKey.privateKey, null);
324-
assert.equal(await DashHd.toXPrv(childKey), null);
326+
await assert.rejects(async function () {
327+
await DashHd.toXPrv(childKey);
328+
});
325329
});
326330

327331
it("should have correct data", async function () {

0 commit comments

Comments
 (0)