import { Bytes } from './bytes.js'; import { HDPublicNode } from './hd-public-node.js'; import { Mnemonic } from './mnemonic.js'; import { PrivateKey } from './private-key.js'; import { encodeHdPrivateKey, decodeHdPrivateKey, deriveHdPath, deriveHdPathRelative, deriveHdPublicNode, deriveHdPrivateNodeFromSeed, generateRandomBytes, } from '@bitauth/libauth'; import type { DecodedHdKey, HdKeyNetwork, HdPrivateNodeValid, } from '@bitauth/libauth'; /** * Hierarchically Deterministic Private Node Entity. * * @example * * ```ts * // Import our primitives. * import { Mnemonic, HDPrivateNode } from '@xocash/primitives'; * * // Generate a mnemonic. * const mnemonic = Mnemonic.generateRandom(); * * // Initialize a HD Private Node from Mnemonic. * const hdPrivateNode = HDPrivateNode.fromMnemonic(mnemonic); * * // Derive the first Private Key. * const privateKey = hdPrivateNode.derivePath("m/44'/145'/0'/0/0").toPrivateKey(); * * // Output the BCH Address. * console.log(privateKey.derivePublicKey().deriveAddress().toCashAddr()); * ``` */ export class HDPrivateNode { // The HD Private Node. protected readonly node: HdPrivateNodeValid; /** * Construct a new HD Private Node. * * @param node The HD Private Node. */ protected constructor(node: HdPrivateNodeValid) { this.node = node; } /** * Creates a HD Private Node from a raw seed. * * @param seed The raw seed bytes. * * @throws {Error} If HD Private Node cannot be created. * * @returns The created HD Private Node. */ public static fromSeed(seed: Uint8Array | string): HDPrivateNode { // Cast from hex if a string was provided. if (typeof seed === 'string') { seed = Bytes.fromHex(seed); } // If the seed is not 64 bytes, throw an error. if (seed.length !== 64) { throw new Error( `Failed to derive Private Node from seed: Seed must be 64 bytes (${seed.length} given)`, ); } // Attempt to derive HD Private Node from the given seed. const deriveResult = deriveHdPrivateNodeFromSeed(seed); // If a string is returned, this indicates an error... if (typeof deriveResult === 'string') { throw new Error(deriveResult); } // Return a new HD Private Node from the given seed. return new this(deriveResult); } /** * Creates a HD Private Node from the given mnemonic. * * @param mnemonic The mnemonic to use. * * @throws {Error} If HD Private Node cannot be created. * * @returns The created HD Private Node. */ public static fromMnemonic(mnemonic: Mnemonic): HDPrivateNode { // Return a new HDPrivateNode from the given seed. return this.fromSeed(mnemonic.toSeed()); } /** * Creates a HD Private Node from the given XPriv Key. * * @param xpriv The XPriv Key. * * @throws {Error} If HD Private Node cannot be created. * * @returns The created HD Private Node. */ public static fromXPriv(xpriv: string): HDPrivateNode { // Attempt to decode the XPriv Key. const decodeResult = decodeHdPrivateKey(xpriv); // If a string is returned, this indicates an error... if (typeof decodeResult === 'string') { throw new Error(decodeResult); } // Return a new HD Private Node from the given XPriv. return new this(decodeResult.node); } /** * Creates a HD Private Node from raw node data. * * @param node The raw node data. * * @returns The created HD Private Node. */ public static fromRaw(node: HdPrivateNodeValid) { return new this(node); } /** * Generates a random HD Private Node. * * @throws {Error} If HD Private Node cannot be created. * * @returns The created HD Private Node. */ public static generateRandom(): HDPrivateNode { // Attempt to derive HD Private Node from randomly generated bytes. const deriveResult = deriveHdPrivateNodeFromSeed(generateRandomBytes(32)); // If a string is returned, this indicates an error... if (typeof deriveResult === 'string') { throw new Error(deriveResult); } // Return the new HD Private Node from the random bytes. return new this(deriveResult); } /** * Checks if the given XPriv string is a valid HD Private Key. * * @param xpriv The XPriv string to check. * * @returns True if the XPriv string is valid, false otherwise. */ public static isXPriv(xpriv: string): boolean { try { this.fromXPriv(xpriv); return true; } catch (_error) { return false; } } /** * Returns the Private Key of this node (as a Uint8Array). * * @returns The Node's Private Key (as a Uint8Array). */ public toBytes(): Uint8Array { // Encode the XPriv from our node info. return this.node.privateKey; } /** * Gets the Private Key entity for this node. * * @returns The Private Key Entity for this node. */ public toPrivateKey(): PrivateKey { // Return a Private Key Entity from the private key of our node. return PrivateKey.fromRaw(this.node.privateKey); } /** * Converts this node to an XPriv Key for export. * * @param network {HdKeyNetwork} The network to encode for. * * @returns The XPriv Key. */ public toXPriv(network: HdKeyNetwork = 'mainnet'): string { // Create our node info structure. const nodeInfo: DecodedHdKey = { network: network, node: this.node, }; // Encode the XPriv from our node info. return encodeHdPrivateKey(nodeInfo).hdPrivateKey; } /** * Returns the XPriv string for this node. * * @returns The XPriv string for this node. */ public toString(): string { // Return the Mainnet XPriv. return this.toXPriv(); } /** * Returns the raw node data. * * @returns The raw node data. */ public toRaw(): HdPrivateNodeValid { return this.node; } /** * Derives a HD Public Node from this Private Node. * * @returns The derived HD Public Node */ public deriveHDPublicNode(): HDPublicNode { // Derive a HD Public Node from this HD Private Node. const publicNode = deriveHdPublicNode(this.node); // Return a new HD Public Node entity from our derived Public Node. return HDPublicNode.fromRaw(publicNode); } /** * Derives a HD Private Node from the given BIP32 path. * * @param path {string} The BIP32 Path (e.g. m/44'/145'/0'/0/1). * * @throws {Error} If HD Private Node cannot be derived. * * @returns The derived HD Private Node */ public derivePath(path: string): HDPrivateNode { // Attempt to derive the given path. const derivePathResult = deriveHdPath(this.node, path); // If a string is returned, this indicates an error... if (typeof derivePathResult === 'string') { throw new Error(derivePathResult); } // Return a new HD Private Node Entity derived from the given path. return new HDPrivateNode(derivePathResult); } /** * Derives a HD Private Node from the given relative BIP32 path. * * @param path {string} The BIP32 Path (e.g. 0/0). * * @throws {Error} If HD Private Node cannot be derived. * * @returns The derived HD Private Node */ public deriveRelativePath(path: string): HDPrivateNode { // Attempt to derive the given path. const derivePathResult = deriveHdPathRelative(this.node, path); // If a string is returned, this indicates an error... if (typeof derivePathResult === 'string') { throw new Error(derivePathResult); } // Return a new HD Private Node Entity derived from the given path. return new HDPrivateNode(derivePathResult); } }