@bsv/sdk
Version:
BSV Blockchain Software Development Kit
344 lines • 14.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// @ts-nocheck
const utils_js_1 = require("../primitives/utils.js");
const Hash = __importStar(require("../primitives/Hash.js"));
const Curve_js_1 = __importDefault(require("../primitives/Curve.js"));
const PrivateKey_js_1 = __importDefault(require("../primitives/PrivateKey.js"));
const PublicKey_js_1 = __importDefault(require("../primitives/PublicKey.js"));
const Random_js_1 = __importDefault(require("../primitives/Random.js"));
const BigNumber_js_1 = __importDefault(require("../primitives/BigNumber.js"));
/**
* @deprecated
* The HD class implements the Bitcoin Improvement Proposal 32 (BIP32) hierarchical deterministic wallets.
* It allows the generation of child keys from a master key, ensuring a tree-like structure of keys and addresses.
* This class is deprecated due to the introduction of BRC-42, which offers an enhanced key derivation scheme.
* BRC-42 uses invoice numbers for key derivation, improving privacy and scalability compared to BIP32.
*
* @class HD
* @deprecated Replaced by BRC-42 which uses invoice numbers and supports private derivation.
*/
class HD {
/**
* Constructor for the BIP32 HD wallet.
* Initializes an HD wallet with optional parameters for version bytes, depth, parent fingerprint, child index, chain code, private key, and public key.
* @param versionBytesNum - Version bytes number for the wallet.
* @param depth - Depth of the key in the hierarchy.
* @param parentFingerPrint - Fingerprint of the parent key.
* @param childIndex - Index of the child key.
* @param chainCode - Chain code for key derivation.
* @param privKey - Private key of the wallet.
* @param pubKey - Public key of the wallet.
*/
constructor(versionBytesNum, depth, parentFingerPrint, childIndex, chainCode, privKey, pubKey) {
this.constants = {
pubKey: 0x0488b21e,
privKey: 0x0488ade4
};
this.versionBytesNum = versionBytesNum;
this.depth = depth;
this.parentFingerPrint = parentFingerPrint;
this.childIndex = childIndex;
this.chainCode = chainCode;
this.privKey = privKey;
this.pubKey = pubKey;
}
/**
* Generates a new HD wallet with random keys.
* This method creates a root HD wallet with randomly generated private and public keys.
* @returns {HD} The current HD instance with generated keys.
*/
fromRandom() {
this.versionBytesNum = this.constants.privKey;
this.depth = 0x00;
this.parentFingerPrint = [0, 0, 0, 0];
this.childIndex = 0;
this.chainCode = (0, Random_js_1.default)(32);
this.privKey = PrivateKey_js_1.default.fromRandom();
this.pubKey = this.privKey.toPublicKey();
return this;
}
/**
* Generates a new HD wallet with random keys.
* This method creates a root HD wallet with randomly generated private and public keys.
* @returns {HD} A new HD instance with generated keys.
* @static
*/
static fromRandom() {
return new this().fromRandom();
}
/**
* Initializes the HD wallet from a given base58 encoded string.
* This method decodes a provided string to set up the HD wallet's properties.
* @param str - A base58 encoded string representing the wallet.
* @returns {HD} The new instance with properties set from the string.
*/
static fromString(str) {
return new this().fromString(str);
}
/**
* Initializes the HD wallet from a given base58 encoded string.
* This method decodes a provided string to set up the HD wallet's properties.
* @param str - A base58 encoded string representing the wallet.
* @returns {HD} The current instance with properties set from the string.
*/
fromString(str) {
const decoded = (0, utils_js_1.fromBase58Check)(str);
return this.fromBinary([...decoded.prefix, ...decoded.data]);
}
/**
* Initializes the HD wallet from a seed.
* This method generates keys and other properties from a given seed, conforming to the BIP32 specification.
* @param bytes - An array of bytes representing the seed.
* @returns {HD} The current instance with properties set from the seed.
*/
static fromSeed(bytes) {
return new this().fromSeed(bytes);
}
/**
* Initializes the HD wallet from a seed.
* This method generates keys and other properties from a given seed, conforming to the BIP32 specification.
* @param bytes - An array of bytes representing the seed.
* @returns {HD} The current instance with properties set from the seed.
*/
fromSeed(bytes) {
if (bytes.length < 128 / 8) {
throw new Error('Need more than 128 bits of entropy');
}
if (bytes.length > 512 / 8) {
throw new Error('More than 512 bits of entropy is nonstandard');
}
const hash = Hash.sha512hmac((0, utils_js_1.toArray)('Bitcoin seed', 'utf8'), bytes);
this.depth = 0x00;
this.parentFingerPrint = [0, 0, 0, 0];
this.childIndex = 0;
this.chainCode = hash.slice(32, 64);
this.versionBytesNum = this.constants.privKey;
this.privKey = new PrivateKey_js_1.default(hash.slice(0, 32));
this.pubKey = this.privKey.toPublicKey();
return this;
}
/**
* Initializes the HD wallet from a binary buffer.
* Parses a binary buffer to set up the wallet's properties.
* @param buf - A buffer containing the wallet data.
* @returns {HD} The new instance with properties set from the buffer.
*/
static fromBinary(buf) {
return new this().fromBinary(buf);
}
/**
* Initializes the HD wallet from a binary buffer.
* Parses a binary buffer to set up the wallet's properties.
* @param buf - A buffer containing the wallet data.
* @returns {HD} The current instance with properties set from the buffer.
*/
fromBinary(buf) {
// Both pub and private extended keys are 78 buf
if (buf.length !== 78) {
throw new Error('incorrect bip32 data length');
}
const reader = new utils_js_1.Reader(buf);
this.versionBytesNum = reader.readUInt32BE();
this.depth = reader.readUInt8();
this.parentFingerPrint = reader.read(4);
this.childIndex = reader.readUInt32BE();
this.chainCode = reader.read(32);
const keyBytes = reader.read(33);
const isPrivate = this.versionBytesNum === this.constants.privKey;
const isPublic = this.versionBytesNum === this.constants.pubKey;
if (isPrivate && keyBytes[0] === 0) {
this.privKey = new PrivateKey_js_1.default(keyBytes.slice(1, 33));
this.pubKey = this.privKey.toPublicKey();
}
else if (isPublic && (keyBytes[0] === 0x02 || keyBytes[0] === 0x03)) {
this.pubKey = PublicKey_js_1.default.fromString((0, utils_js_1.toHex)(keyBytes));
}
else {
throw new Error('Invalid key');
}
return this;
}
/**
* Converts the HD wallet to a base58 encoded string.
* This method provides a string representation of the HD wallet's current state.
* @returns {string} A base58 encoded string of the HD wallet.
*/
toString() {
const bin = this.toBinary();
return (0, utils_js_1.toBase58Check)(bin, []);
}
/**
* Derives a child HD wallet based on a given path.
* The path specifies the hierarchy of the child key to be derived.
* @param path - A string representing the derivation path (e.g., 'm/0'/1).
* @returns {HD} A new HD instance representing the derived child wallet.
*/
derive(path) {
if (path === 'm') {
return this;
}
const e = path.split('/');
// eslint-disable-next-line @typescript-eslint/no-this-alias
let bip32 = this;
for (const [i, c] of e.entries()) {
if (i === 0) { // Since `i` is now a number, compare it to 0
if (c !== 'm') {
throw new Error('invalid path');
}
continue;
}
if (parseInt(c.replace("'", ''), 10).toString() !== c.replace("'", '')) {
throw new Error('invalid path');
}
const usePrivate = c.length > 1 && c[c.length - 1] === "'";
let childIndex = parseInt(usePrivate ? c.slice(0, c.length - 1) : c, 10) & 0x7fffffff;
if (usePrivate) {
childIndex += 0x80000000;
}
bip32 = bip32.deriveChild(childIndex);
}
return bip32;
}
/**
* Derives a child HD wallet from the current wallet based on an index.
* This method generates either a private or public child key depending on the current wallet's state.
* @param i - The index of the child key to derive.
* @returns {HD} A new HD instance representing the derived child wallet.
*/
deriveChild(i) {
if (typeof i !== 'number') {
throw new Error('i must be a number');
}
const ibc = [];
ibc.push((i >> 24) & 0xff);
ibc.push((i >> 16) & 0xff);
ibc.push((i >> 8) & 0xff);
ibc.push(i & 0xff);
const ib = [...ibc];
const usePrivate = (i & 0x80000000) !== 0;
const isPrivate = this.versionBytesNum === this.constants.privKey;
if (usePrivate && (this.privKey === null || this.privKey === undefined || !isPrivate)) {
throw new Error('Cannot do private key derivation without private key');
}
let ret = null;
if (this.privKey !== null && this.privKey !== undefined) {
let data = null;
if (usePrivate) {
data = [0, ...this.privKey.toArray('be', 32), ...ib];
}
else {
data = [...this.pubKey.encode(true), ...ib];
}
const hash = Hash.sha512hmac(this.chainCode, data);
const il = new BigNumber_js_1.default(hash.slice(0, 32));
const ir = hash.slice(32, 64);
// ki = IL + kpar (mod n).
const k = il.add(this.privKey).mod(new Curve_js_1.default().n);
ret = new HD();
ret.chainCode = ir;
ret.privKey = new PrivateKey_js_1.default(k.toArray());
ret.pubKey = ret.privKey.toPublicKey();
}
else {
const data = [...this.pubKey.encode(true), ...ib];
const hash = Hash.sha512hmac(this.chainCode, data);
const il = new BigNumber_js_1.default(hash.slice(0, 32));
const ir = hash.slice(32, 64);
// Ki = (IL + kpar)*G = IL*G + Kpar
const ilG = new Curve_js_1.default().g.mul(il);
const Kpar = this.pubKey;
const Ki = ilG.add(Kpar);
const newpub = new PublicKey_js_1.default(Ki.x, Ki.y);
ret = new HD();
ret.chainCode = ir;
ret.pubKey = newpub;
}
ret.childIndex = i;
const pubKeyhash = Hash.hash160(this.pubKey.encode(true));
ret.parentFingerPrint = pubKeyhash.slice(0, 4);
ret.versionBytesNum = this.versionBytesNum;
ret.depth = this.depth + 1;
return ret;
}
/**
* Converts the current HD wallet to a public-only wallet.
* This method strips away the private key information, leaving only the public part.
* @returns {HD} A new HD instance representing the public-only wallet.
*/
toPublic() {
const bip32 = new HD(this.versionBytesNum, this.depth, this.parentFingerPrint, this.childIndex, this.chainCode, this.privKey, this.pubKey);
bip32.versionBytesNum = this.constants.pubKey;
bip32.privKey = undefined;
return bip32;
}
/**
* Converts the HD wallet into a binary representation.
* This method serializes the wallet's properties into a binary format.
* @returns {number[]} An array of numbers representing the binary data of the wallet.
*/
toBinary() {
const isPrivate = this.versionBytesNum === this.constants.privKey;
const isPublic = this.versionBytesNum === this.constants.pubKey;
if (isPrivate) {
return new utils_js_1.Writer()
.writeUInt32BE(this.versionBytesNum)
.writeUInt8(this.depth)
.write(this.parentFingerPrint)
.writeUInt32BE(this.childIndex)
.write(this.chainCode)
.writeUInt8(0)
.write(this.privKey.toArray('be', 32))
.toArray();
}
else if (isPublic) {
return new utils_js_1.Writer()
.writeUInt32BE(this.versionBytesNum)
.writeUInt8(this.depth)
.write(this.parentFingerPrint)
.writeUInt32BE(this.childIndex)
.write(this.chainCode)
.write(this.pubKey.encode(true))
.toArray();
}
else {
throw new Error('bip32: invalid versionBytesNum byte');
}
}
/**
* Checks if the HD wallet contains a private key.
* This method determines whether the wallet is a private key wallet or a public key only wallet.
* @returns {boolean} A boolean value indicating whether the wallet has a private key (true) or not (false).
*/
isPrivate() {
return this.versionBytesNum === this.constants.privKey;
}
}
exports.default = HD;
//# sourceMappingURL=HD.js.map