import * as crypto from "@node-lightning/crypto" ; import { BufferWriter } from "@node-lightning/bufio" ; import { BitcoinError } from "./BitcoinError" ; import { BitcoinErrorCode } from "./BitcoinErrorCode" ; import { HdKeyCodec } from "./HdKeyCodec" ; import { HdKeyType } from "./HdKeyType" ; import { HdPublicKey } from "./HdPublicKey" ; import { Network } from "./Network" ; import { PrivateKey } from "./PrivateKey" ; const MAX_INDEX = 0xffffffff ; const HARDENED_INDEX = 0x80000000 ; const BIP49_PURPOSE = 0x80000031 ; const BIP84_PURPOSE = 0x80000054 ; /**
 * Represnts a hierarchical deterministic extended private key as defined
 * in BIP32. This class contains helper methods to derive child keys and
 * manage derivation.
 */ export class HdPrivateKey { param path param seed param network param type returns public static fromPath ( path , seed , network , type ? ) { const parts = path . split ( "/" ) ; if ( parts [ 0 ] !== "m" || parts . length > 255 ) { throw new BitcoinError ( BitcoinErrorCode . InvalidHdPath , path ) ; } const nums = [ ] ; for ( let i = 1 ; i < parts . length ; i ++ ) { const part = parts [ i ] ; const hardened = part . endsWith ( "'" ) ; const num = hardened ? HARDENED_INDEX + Number ( part . substring ( 0 , part . length - 1 ) ) : Number ( part ) ; if ( isNaN ( num ) || num < 0 || num > MAX_INDEX || ( ! hardened && num >= HARDENED_INDEX ) ) { throw new BitcoinError ( BitcoinErrorCode . InvalidHdPath , path ) ; } nums . push ( num ) ; } if ( ! type ) { if ( nums [ 0 ] && nums [ 0 ] === BIP84_PURPOSE ) { type = HdKeyType . z ; } else if ( nums [ 0 ] && nums [ 0 ] === BIP49_PURPOSE ) { type = HdKeyType . y ; } else { type = HdKeyType . x ; } } let key = HdPrivateKey . fromSeed ( seed , network , type ) ; for ( const num of nums ) { key = key . derive ( num ) ; } return key ; } remarks param seed param network param type returns public static fromSeed ( seed , network , type = HdKeyType . x ) { const l = crypto . hmac ( Buffer . from ( "Bitcoin seed" ) , seed , "sha512" ) ; const key = new HdPrivateKey ( ) ; key . type = type ; key . depth = 0 ; key . number = 0 ; key . parentFingerprint = Buffer . from ( [ 0 , 0 , 0 , 0 ] ) ; key . chainCode = l . slice ( 32 ) ; key . privateKey = new PrivateKey ( l . slice ( 0 , 32 ) , network ) ; return key ; } remarks param input throws BitcoinError throws when there is an invalid
     * encoding, bad checksum, or if you attempt to decode a public key.
     *
     *  returns public static decode ( input ) { const result = HdKeyCodec . decode ( input ) ; if ( ! ( result instanceof HdPrivateKey ) ) { throw new BitcoinError ( BitcoinErrorCode . InvalidHdPrivateKey , input ) ; } else { return result ; } } /**
     * The type of extended key. This ensures that xprv, yprv, and
     * zprv values are not used in the same path.
     */ public type : HdKeyType ; /**
     * The depth of the derivation path. Depth of 0 is the master key.
     * Maximum depth is 255 since this is encoded using a single byte.
     */ public depth : number ; /**
     * The parents fingerprint which is the first 4-bytes of the hash160
     * of the compressed public key. The fingerprint of the parent only
     * serves as a fast way to detect parent and child keys and software
     * must be willing to deal with collisions.
     */ public parentFingerprint : Buffer ; /**
     * Specifies the child key number. Values between 0 to 2^31-1 are
     * non-hardened. Values between 2^31 to 2^32-1 are hardened.
     */ public number : number ; /**
     * The extended private key's chaincode
     */ public chainCode : Buffer ; /**
     * The underlying private key instance for extended private key
     */ public privateKey : PrivateKey ; private _fingerprint : Buffer ; /**
     * The network the extended private key belongs to
     */ public get network ( ) : Network { return this . privateKey . network ; } /**
     * The numeric version of the extended private key. This value is
     * determiend by the network and is used to encode a prefix when
     * the extended private key is encoded as a string.
     */ public get version ( ) : number { switch ( this . type ) { case HdKeyType . x : return this . network . xprvVersion ; case HdKeyType . y : return this . network . yprvVersion ; case HdKeyType . z : return this . network . zprvVersion ; } } /**
     * Returns true when the key is a hardened key. A key is hardened
     * when it has an index between 2^31 and 2^32-1
     */ public get isHardened ( ) : boolean { return this . number >= HARDENED_INDEX ; } /**
     * Lazy loads the fingerprint of the current extended private key
     */ public get fingerprint ( ) : Buffer { if ( ! this . _fingerprint ) { this . _fingerprint = crypto . hash160 ( this . privateKey . toPubKey ( true ) . toBuffer ( ) ) ; } return this . _fingerprint ; } remarks param i throws public derive ( i ) { const result = new HdPrivateKey ( ) ; result . type = this . type ; result . depth = this . depth + 1 ; result . number = i ; result . parentFingerprint = Buffer . from ( this . fingerprint . slice ( 0 , 4 ) ) ; const data = new BufferWriter ( Buffer . alloc ( 37 ) ) ; if ( i >= HARDENED_INDEX ) { data . writeUInt8 ( 0 ) ; data . writeBytes ( this . privateKey . toBuffer ( ) ) ; data . writeUInt32BE ( i ) ; } else { data . writeBytes ( this . privateKey . toPubKey ( true ) . toBuffer ( ) ) ; data . writeUInt32BE ( i ) ; } const l = crypto . hmac ( this . chainCode , data . toBuffer ( ) , "sha512" ) ; const ll = l . slice ( 0 , 32 ) ; const lr = l . slice ( 32 ) ; result . privateKey = this . privateKey . tweakAdd ( ll ) ; result . chainCode = lr ; return result ; } param i throws public deriveHardened ( i ) { if ( i < HARDENED_INDEX ) { i = i + HARDENED_INDEX ; } return this . derive ( i ) ; } /**
     * Constructs the corresponding  HdPublicKey .
     *
     * The derivation is defined in BIP32 using HMAC-SHA512
     *  https *
     * The derivation uses the parent's chaincode as the `key`. Then uses
     * for the `data`:
     *
     * * non-hardened: `parent_pub_key || i`
     * * hardened: `0x00 || parent_priv_key || i`
     *
     * As such the data is always 37-bytes.
     *
     * The resulting 64-bytes are split where where the left half is
     * used to tweakAdd against the parent private key. The right half
     * is the child's chain_code.
     *
     *  returns public toPubKey ( ) { const result = new HdPublicKey ( ) ; result . type = this . type ; result . depth = this . depth ; result . number = this . number ; result . parentFingerprint = Buffer . from ( this . parentFingerprint ) ; result . chainCode = Buffer . from ( this . chainCode ) ; result . publicKey = this . privateKey . toPubKey ( true ) ; return result ; } returns public toWif ( ) { return this . privateKey . toWif ( true ) ; } /**
     * Returns the address encoded according to the type of HD key.
     * For x-type this returns a base58 encoded P2PKH address.
     * For y-type this returns a base58 encoded P2SH-P2WPKH address.
     * For z-type this returns a bech32 encoded P2WPKH address.
     */ public toAddress ( ) { return this . toPubKey ( ) . toAddress ( ) ; } /**
     * Encodes the  HdPrivateKey according to BIP32.
     *
     * For example:
     * xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi
     *
     *  remarks returns public encode ( ) { return HdKeyCodec . encode ( this ) ; } /**
     * Returns a Buffer of the underlying private key encoded as a big
     * endian integer. This is sugar for `key.privateKey.toBuffer()`.
     */ public toBuffer ( ) { return this . privateKey . toBuffer ( ) ; } /**
     * Returns a hex string of the underlying private key. This is sugar
     * for `key.privateKey.toHex()`.
     */ public toHex ( ) { return this . privateKey . toHex ( ) ; } }