Skip to content

Commit

Permalink
chore: add types and minor changes for type checking
Browse files Browse the repository at this point in the history
  • Loading branch information
AJ ONeal committed Feb 3, 2023
1 parent 92dccc8 commit 7472aad
Showing 1 changed file with 177 additions and 7 deletions.
184 changes: 177 additions & 7 deletions lib/hdkey.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,44 @@
/**
* @typedef HDKey
* @prop {HDCreate} create
* @prop {HDFromSeed} fromMasterSeed
* @prop {HDFromXKey} fromExtendedKey
* @prop {HDFromJSON} fromJSON
* @prop {Number} HARDENED_OFFSET - 0x80000000
*/

/**
* @callback HDCreate
* @param {HDVersions} [versions]
* @returns {hdkey}
*/

/**
* @typedef hdkey
* @prop {Buffer} chainCode - extra 32-bytes of shared entropy for xkeys
* @prop {Number} depth - of hd path - typically 0 is seed, 1-3 hardened, 4-5 are not
* @prop {Buffer} identifier - same bytes as pubKeyHash, but used for id
* @prop {Number} index - the final segment of an HD Path, the index of the wif/addr
* @prop {Number} parentFingerprint - 32-bit int, slice of id, stored in child xkeys
* @prop {Buffer} publicKey
* @prop {HDVersions} versions - magic bytes for base58 prefix
* @prop {HDDerivePath} derive - derive a full hd path from the given root
* @prop {HDDeriveChild} deriveChild - get the next child xkey (in a path segment)
* @prop {HDFingerprint} getFingerprint
* @prop {HDMaybeGetString} getPrivateExtendedKey
* @prop {HDMaybeGetBuffer} getPrivateKey
* @prop {HDGetString} getPublicExtendedKey
* @prop {HDSetBuffer} setPublicKey
* @prop {HDSetBuffer} setPrivateKey
* @prop {HDSign} sign
* @prop {HDVerify} verify
* @prop {HDToJSON} toJSON
* @prop {HDWipePrivates} wipePrivateData - randomizes private key buffer in-place
* @prop {Function} _setPublicKey
*/

/** @type {HDKey} */
//@ts-ignore
var HDKey = ("object" === typeof module && exports) || {};
(function (window, HDKey) {
"use strict";
Expand All @@ -15,21 +56,23 @@ var HDKey = ("object" === typeof module && exports) || {};
var BITCOIN_VERSIONS = { private: 0x0488ade4, public: 0x0488b21e };

HDKey.create = function (versions) {
/** @type {hdkey} */
var hdkey = {};
/** @type {Buffer?} */
var _privateKey = null;

hdkey.versions = versions || BITCOIN_VERSIONS;
hdkey.depth = 0;
hdkey.index = 0;
hdkey.publicKey = null;
// note: BIP-32 "Key Identifier" is the same as "Pub Key Hash"
// but used for a different purpose, hence the semantic name
hdkey.identifier = null;
// note: chainCode is public
hdkey.chainCode = null;
//hdkey.publicKey = null;
//hdkey.identifier = null;
//hdkey.chainCode = null;
hdkey.parentFingerprint = 0;

hdkey.getFingerprint = function () {
if (!hdkey.identifier) {
throw new Error("Public key has not been set");
}
return hdkey.identifier.slice(0, 4).readUInt32BE(0);
};

Expand Down Expand Up @@ -57,6 +100,9 @@ var HDKey = ("object" === typeof module && exports) || {};
hdkey._setPublicKey(publicKey);
};

/**
* @param {Buffer} publicKey
*/
hdkey._setPublicKey = function (publicKey) {
hdkey.publicKey = Buffer.from(publicKey);
hdkey.identifier = hash160(publicKey);
Expand All @@ -78,6 +124,10 @@ var HDKey = ("object" === typeof module && exports) || {};
};

hdkey.getPublicExtendedKey = function () {
if (!hdkey.publicKey) {
throw new Error("Missing public key");
}

return bs58check.encode(
serialize(hdkey, hdkey.versions.public, hdkey.publicKey),
);
Expand Down Expand Up @@ -118,7 +168,9 @@ var HDKey = ("object" === typeof module && exports) || {};

if (isHardened) {
// Hardened child
assert(Boolean(_privateKey), "Could not derive hardened child key");
if (!_privateKey) {
throw new Error("Could not derive hardened child key");
}

var pk = _privateKey;
var zb = Buffer.alloc(1, 0);
Expand Down Expand Up @@ -186,6 +238,10 @@ var HDKey = ("object" === typeof module && exports) || {};
};

hdkey.sign = function (hash) {
if (!_privateKey) {
throw new Error("Private Key must be set");
}

return Buffer.from(
secp256k1.ecdsaSign(Uint8Array.from(hash), Uint8Array.from(_privateKey))
.signature,
Expand Down Expand Up @@ -279,12 +335,21 @@ var HDKey = ("object" === typeof module && exports) || {};
return HDKey.fromExtendedKey(obj.xpriv);
};

/**
* @param {Boolean} assertion
* @param {String} message
*/
function assert(assertion, message) {
if (!assertion) {
throw new Error(message);
}
}

/**
* @param {hdkey} hdkey - TODO attach to hdkey
* @param {Number} version
* @param {Buffer} key
*/
function serialize(hdkey, version, key) {
// => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
var buffer = Buffer.allocUnsafe(LEN);
Expand All @@ -302,6 +367,10 @@ var HDKey = ("object" === typeof module && exports) || {};
return buffer;
}

/**
* @param {Buffer} buf
* @returns {Buffer}
*/
function hash160(buf) {
var sha = crypto.createHash("sha256").update(buf).digest();
return new RIPEMD160().update(sha).digest();
Expand All @@ -312,3 +381,104 @@ var HDKey = ("object" === typeof module && exports) || {};
if ("object" === typeof module) {
module.exports = HDKey;
}

// Type Definitions

/**
* @typedef HDVersions
* @prop {Number} private - 32-bit int (encodes to 'xprv' in base58)
* @prop {Number} public - 32-bit int (encodes to 'xpub' in base58)
*/

/**
* @typedef HDJSON
* @prop {String?} xpriv - base58check-encoded extended private key
* @prop {String} xpub - base58check-encoded extended public key
*/

// Function Definitions

/**
* @callback HDDeriveChild
* @param {Number} index - includes HARDENED_OFFSET, if applicable
*/

/**
* @callback HDDerivePath
* @param {String} path
*/

/**
* @callback HDFingerprint
* @returns {Number}
*/

/**
* @callback HDFromXKey
* @param {String} base58key - base58check-encoded xkey
* @param {HDVersions} [versions]
* @param {Boolean} [skipVerification]
* returns {hdkey}
*/

/**
* @callback HDFromJSON
* @param {HDFromJSONOpts} opts
* returns {hdkey}
*
* @typedef HDFromJSONOpts
* @prop {String} xpriv
*/

/**
* @callback HDFromSeed
* @param {Buffer} seedBuffer
* @param {HDVersions} [versions]
*/

/**
* @callback HDGetBuffer
* @returns {Buffer}
*/

/**
* @callback HDGetString
* @returns {String}
*/

/**
* @callback HDMaybeGetBuffer
* @returns {Buffer?}
*/

/**
* @callback HDMaybeGetString
* @returns {String?}
*/

/**
* @callback HDSetBuffer
* @param {Buffer} buf
*/

/**
* @callback HDSign
* @param {Buffer} hash
* @returns {Buffer} - signature
*/

/**
* @callback HDToJSON
* @returns {HDJSON}
*/

/**
* @callback HDVerify
* @param {Buffer} hash
* @param {Buffer} signature
* @returns {Boolean}
*/

/**
* @callback HDWipePrivates
*/

0 comments on commit 7472aad

Please sign in to comment.