@bsv/sdk
Version:
BSV Blockchain Software Development Kit
322 lines • 13.4 kB
JavaScript
// @ts-nocheck
import { fromBase58Check, toBase58Check, Writer, Reader, toArray, toHex } from '../primitives/utils.js';
import * as Hash from '../primitives/Hash.js';
import Curve from '../primitives/Curve.js';
import PrivateKey from '../primitives/PrivateKey.js';
import PublicKey from '../primitives/PublicKey.js';
import Random from '../primitives/Random.js';
import BigNumber from '../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.
*/
export default class HD {
versionBytesNum;
depth;
parentFingerPrint;
childIndex;
chainCode;
privKey;
pubKey;
constants = {
pubKey: 0x0488b21e,
privKey: 0x0488ade4
};
/**
* 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.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 = Random(32);
this.privKey = PrivateKey.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 = 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(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(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 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(keyBytes.slice(1, 33));
this.pubKey = this.privKey.toPublicKey();
}
else if (isPublic && (keyBytes[0] === 0x02 || keyBytes[0] === 0x03)) {
this.pubKey = PublicKey.fromString(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 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(hash.slice(0, 32));
const ir = hash.slice(32, 64);
// ki = IL + kpar (mod n).
const k = il.add(this.privKey).mod(new Curve().n);
ret = new HD();
ret.chainCode = ir;
ret.privKey = new PrivateKey(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(hash.slice(0, 32));
const ir = hash.slice(32, 64);
// Ki = (IL + kpar)*G = IL*G + Kpar
const ilG = new Curve().g.mul(il);
const Kpar = this.pubKey;
const Ki = ilG.add(Kpar);
const newpub = new PublicKey(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 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 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;
}
}
//# sourceMappingURL=HD.js.map