import ECKey from '@neo-one/ec-key' ; import { Constructor , utils } from '@neo-one/utils' ; import { BN } from 'bn.js' ; import base58 from 'bs58' ; import xor from 'buffer-xor' ; import { createCipheriv , createDecipheriv , createHash , createHmac , randomBytes } from 'crypto' ; import { curves , ec as EC } from 'elliptic' ; import scrypt from 'scrypt-js' ; import WIF from 'wif' ; import { common , ECPoint , InvalidFormatError , PrivateKey , UInt160 , UInt256 } from './common' ; import { Base58CheckError , InvalidAddressError , InvalidBIP32ChildIndexError , InvalidBIP32ExtendedKeyError , InvalidBIP32HardenedError , InvalidBIP32SerializePrivateNodeError , InvalidBIP32VersionError , InvalidNumberOfKeysError , InvalidPrivateKeyError , InvalidSignatureError , InvalidSignaturesError , TooManyPublicKeysError , } from './errors' ; import { Op , sha256 as sha256In , WitnessModel } from './models' ; import { p256 } from './precomputed' ; import { ScriptBuilder } from './ScriptBuilder' ; let ecCache ; const ec = ( ) => { if ( ecCache === undefined ) { ecCache = new EC ( new curves . PresetCurve ( p256 ) ) ; } return ecCache ; } ; const sha1 = ( value ) : Buffer => createHash ( 'sha1' ) . update ( value ) . digest ( ) ; const sha256 = sha256In ; const rmd160 = ( value ) : Buffer => createHash ( process ?. versions . hasOwnProperty ( 'electron' ) ? 'ripemd160' : 'rmd160' ) . update ( value ) . digest ( ) ; const hash160 = ( value ) : UInt160 => common . bufferToUInt160 ( rmd160 ( sha256 ( value ) ) ) ; const hash256 = ( value ) : UInt256 => common . bufferToUInt256 ( sha256 ( sha256 ( value ) ) ) ; const calculateHash = ( value ) : UInt256 => common . bufferToUInt256 ( sha256 ( value ) ) ; const hmacSha512 = ( key , data ) => createHmac ( 'sha512' , key ) . update ( data ) . digest ( ) ; const sign = ( { message , privateKey } ) : Buffer => { const sig = ec ( ) . sign ( sha256 ( message ) , common . privateKeyToBuffer ( privateKey ) ) ; return Buffer . concat ( [ sig . r . toArrayLike ( Buffer , 'be' , 32 ) , sig . s . toArrayLike ( Buffer , 'be' , 32 ) ] ) ; } ; const rmPadding = ( buf ) : number [ ] => { let i = 0 ; const len = buf . length - 1 ; while ( ! buf [ i ] && ! ( buf [ i + 1 ] & 0x80 ) && i < len ) { i += 1 ; } if ( i === 0 ) { return buf ; } return buf . slice ( i ) ; } ; const constructLength = ( arr , len ) : void => { if ( len < 0x80 ) { arr . push ( len ) ; return ; } let octets = 1 + ( ( Math . log ( len ) / Math . LN2 ) >>> 3 ) ; arr . push ( octets | 0x80 ) ; while ( -- octets ) { arr . push ( ( len >>> ( octets << 3 ) ) & 0xff ) ; } arr . push ( len ) ; } ; const verify = ( { message , signature : signatureIn , publicKey , } ) : boolean => { if ( signatureIn . length !== 64 ) { throw new InvalidSignatureError ( signatureIn . length ) ; } let r = [ ... signatureIn . slice ( 0 , 32 ) ] ; let s = [ ... signatureIn . slice ( 32 ) ] ; if ( r [ 0 ] & 0x80 ) { r = [ 0 ] . concat ( r ) ; } if ( s [ 0 ] & 0x80 ) { s = [ 0 ] . concat ( s ) ; } r = rmPadding ( r ) ; s = rmPadding ( s ) ; while ( s . length > 0 && ! s [ 0 ] && ! ( s [ 1 ] & 0x80 ) ) { s = s . slice ( 1 ) ; } let arr = [ 0x02 ] ; constructLength ( arr , r . length ) ; arr = arr . concat ( r ) ; arr . push ( 0x02 ) ; constructLength ( arr , s . length ) ; const backHalf = arr . concat ( s ) ; const res = [ 0x30 ] ; constructLength ( res , backHalf . length ) ; const signature = Buffer . from ( res . concat ( backHalf ) ) ; const key = new ECKey ( { crv : 'P-256' , publicKey : Buffer . from ( ec ( ) . keyFromPublic ( publicKey ) . getPublic ( false , 'hex' ) , 'hex' ) } ) ; return ( key . createVerify ( 'SHA256' ) . update ( message ) as any ) . verify ( signature ) ; } ; const toECPointFromKeyPair = ( pair ) : ECPoint => common . bufferToECPoint ( Buffer . from ( pair . getPublic ( true , 'hex' ) , 'hex' ) ) ; const mutablePublicKeyCache = { } ; const addPublicKey = ( privateKey , publicKey ) => { mutablePublicKeyCache [ common . privateKeyToString ( privateKey ) ] = publicKey ; } ; const privateKeyToPublicKey = ( privateKey ) : ECPoint => { const privateKeyHex = common . privateKeyToString ( privateKey ) ; let publicKey = mutablePublicKeyCache [ privateKeyHex ] ; if ( publicKey === undefined ) { const key = ec ( ) . keyFromPrivate ( common . privateKeyToBuffer ( privateKey ) ) ; key . getPublic ( true , 'hex' ) ; const { result } = key . validate ( ) ; if ( ! result ) { throw new InvalidPrivateKeyError ( privateKey ) ; } mutablePublicKeyCache [ privateKeyHex ] = publicKey = toECPointFromKeyPair ( key ) ; } return publicKey ; } ; const createKeyPair = ( ) : { readonly privateKey : PrivateKey ; readonly publicKey : ECPoint ; } => { const key = ec ( ) . genKeyPair ( ) ; const validation = key . validate ( ) ; if ( ! validation . result ) { return createKeyPair ( ) ; } return { privateKey : common . bufferToPrivateKey ( key . getPrivate ( ) . toArrayLike ( Buffer , 'be' ) ) , publicKey : toECPointFromKeyPair ( key ) } ; } ; const createPrivateKey = ( ) : PrivateKey => common . bufferToPrivateKey ( randomBytes ( 32 ) ) ; const toScriptHash = hash160 ; const getContractHash = ( sender , nefCheckSum , name ) => { const builder = new ScriptBuilder ( ) ; builder . emitOp ( 'ABORT' ) ; builder . emitPushUInt160 ( sender ) ; builder . emitPushInt ( nefCheckSum ) ; builder . emitPushString ( name ) ; return toScriptHash ( builder . build ( ) ) ; } ; const toECPoint = ( publicKey ) : ECPoint => toECPointFromKeyPair ( ec ( ) . keyFromPublic ( publicKey ) ) ; const base58Checksum = ( buffer ) : Buffer => common . uInt256ToBuffer ( hash256 ( buffer ) ) . slice ( 0 , 4 ) ; const base58CheckEncode = ( buffer ) : string => { const checksum = base58Checksum ( buffer ) ; return base58 . encode ( Buffer . concat ( [ buffer , checksum ] ) ) ; } ; const base58CheckDecode = ( value ) : Buffer => { const buffer = Buffer . from ( base58 . decode ( value ) ) ; const payload = buffer . slice ( 0 , - 4 ) ; const checksum = buffer . slice ( - 4 ) ; const payloadChecksum = base58Checksum ( payload ) ; if ( ! checksum . equals ( payloadChecksum ) ) { throw new Base58CheckError ( value ) ; } return payload ; } ; const scriptHashToAddress = ( { addressVersion , scriptHash , } ) : string => { const mutableBuffer = Buffer . allocUnsafe ( 21 ) ; mutableBuffer [ 0 ] = addressVersion ; common . uInt160ToBuffer ( scriptHash ) . copy ( mutableBuffer , 1 ) ; return base58CheckEncode ( mutableBuffer ) ; } ; const addressToScriptHash = ( { addressVersion , address , } ) : UInt160 => { const decodedAddress = base58CheckDecode ( address ) ; if ( decodedAddress . length !== 21 || decodedAddress [ 0 ] !== addressVersion ) { throw new InvalidAddressError ( address ) ; } return common . bufferToUInt160 ( decodedAddress . slice ( 1 ) ) ; } ; const createInvocationScriptForSignature = ( signature ) : Buffer => { const builder = new ScriptBuilder ( ) ; builder . emitPush ( signature ) ; return builder . build ( ) ; } ; const createInvocationScript = ( message , privateKey ) : Buffer => createInvocationScriptForSignature ( sign ( { message , privateKey } ) ) ; const createVerificationScript = ( publicKey ) : Buffer => { const builder = new ScriptBuilder ( ) ; builder . emitPushECPoint ( publicKey ) ; builder . emitSysCall ( 'System.Crypto.CheckSig' ) ; return builder . build ( ) ; } ; const createWitnessForSignature = < TWitness extends WitnessModel > ( signature , publicKey , Witness ) : TWitness => { const verification = createVerificationScript ( publicKey ) ; const invocation = createInvocationScriptForSignature ( signature ) ; return new Witness ( { verification , invocation } ) ; } ; const createSignatureRedeemScript = ( publicKey ) : Buffer => { const builder = new ScriptBuilder ( ) ; builder . emitPushECPoint ( publicKey ) ; builder . emitSysCall ( 'System.Crypto.CheckSig' ) ; return builder . build ( ) ; } ; const createWitness = < TWitness extends WitnessModel > ( message , privateKey , Witness ) : TWitness => createWitnessForSignature ( sign ( { message , privateKey } ) , privateKeyToPublicKey ( privateKey ) , Witness ) ; const getVerificationScriptHash = ( publicKey ) : UInt160 => toScriptHash ( createVerificationScript ( publicKey ) ) ; const compareKeys = ( a , b ) : number => { const aPublic = a . getPublic ( ) ; const bPublic = b . getPublic ( ) ; const result = aPublic . getX ( ) . cmp ( bPublic . getX ( ) ) ; if ( result !== 0 ) { return result ; } return aPublic . getY ( ) . cmp ( bPublic . getY ( ) ) ; } ; const sortKeys = ( publicKeys ) : readonly ECPoint [ ] => publicKeys . map ( ( publicKey ) => ec ( ) . keyFromPublic ( publicKey ) ) . sort ( compareKeys ) . map ( toECPointFromKeyPair ) ; const createMultiSignatureVerificationScript = ( mIn , publicKeys ) => { const m = Math . floor ( mIn ) ; if ( m < 1 || m > publicKeys . length ) { throw new InvalidNumberOfKeysError ( m , publicKeys . length ) ; } if ( publicKeys . length > 1024 ) { throw new TooManyPublicKeysError ( publicKeys . length ) ; } const builder = new ScriptBuilder ( ) ; builder . emitPushInt ( m ) ; const publicKeysSorted = publicKeys . length === 1 ? publicKeys : sortKeys ( publicKeys ) ; publicKeysSorted . forEach ( ( ecPoint ) => { builder . emitPushECPoint ( ecPoint ) ; } ) ; builder . emitPushInt ( publicKeysSorted . length ) ; builder . emitSysCall ( 'System.Crypto.CheckMultisig' ) ; return builder . build ( ) ; } ; const createMultiSignatureInvocationScript = ( signatures ) : Buffer => { const builder = new ScriptBuilder ( ) ; signatures . forEach ( ( signature ) => { builder . emitPush ( signature ) ; } ) ; return builder . build ( ) ; } ; const createMultiSignatureWitness = < TWitness extends WitnessModel > ( mIn , publicKeys , publicKeyToSignature , Witness ) : TWitness => { const m = Math . floor ( mIn ) ; const publicKeysSorted = publicKeys . length === 1 ? publicKeys : sortKeys ( publicKeys ) ; const signatures = publicKeysSorted . map ( ( publicKey ) => publicKeyToSignature [ common . ecPointToHex ( publicKey ) ] ) . filter ( utils . notNull ) ; if ( signatures . length !== m ) { throw new InvalidSignaturesError ( m , signatures . length ) ; } const verification = createMultiSignatureVerificationScript ( m , publicKeysSorted ) ; const invocation = createMultiSignatureInvocationScript ( signatures ) ; return new Witness ( { verification , invocation } ) ; } ; const getBFTAddress = ( publicKeys ) : UInt160 => toScriptHash ( createMultiSignatureRedeemScript ( publicKeys . length - ( publicKeys . length - 1 ) / 3 , publicKeys ) ) ; const createMultiSignatureRedeemScript = ( mIn , publicKeys ) => { const m = Math . floor ( mIn ) ; if ( m < 1 || m > publicKeys . length ) { throw new InvalidNumberOfKeysError ( m , publicKeys . length ) ; } if ( publicKeys . length > 1024 ) { throw new TooManyPublicKeysError ( publicKeys . length ) ; } const publicKeysSorted = publicKeys . length === 1 ? publicKeys : sortKeys ( publicKeys ) ; const builder = new ScriptBuilder ( ) ; builder . emitPushInt ( m ) ; publicKeysSorted . forEach ( ( key ) => builder . emitPushECPoint ( key ) ) ; builder . emitPushInt ( publicKeysSorted . length ) ; builder . emitSysCall ( 'System.Crypto.CheckMultisig' ) ; return builder . build ( ) ; } ; const getConsensusAddress = ( validators ) : UInt160 => toScriptHash ( createMultiSignatureRedeemScript ( Math . floor ( validators . length - ( validators . length - 1 ) / 3 ) , validators ) ) ; const wifToPrivateKey = ( wif , privateKeyVersion ) : PrivateKey => { const privateKeyDecoded = base58CheckDecode ( wif ) ; if ( privateKeyDecoded . length !== 34 || privateKeyDecoded [ 0 ] !== privateKeyVersion || privateKeyDecoded [ 33 ] !== 0x01 ) { throw new InvalidFormatError ( 'Private Key was invalid when decoded from WIF.' ) ; } return common . bufferToPrivateKey ( privateKeyDecoded . slice ( 1 , 33 ) ) ; } ; const privateKeyToWIF = ( privateKey , privateKeyVersion ) : string => WIF . encode ( privateKeyVersion , common . privateKeyToBuffer ( privateKey ) , true ) ; const publicKeyToScriptHash = getVerificationScriptHash ; const privateKeyToScriptHash = ( privateKey ) : UInt160 => publicKeyToScriptHash ( privateKeyToPublicKey ( privateKey ) ) ; const privateKeyToAddress = ( { addressVersion , privateKey , } ) : string => scriptHashToAddress ( { addressVersion , scriptHash : privateKeyToScriptHash ( privateKey ) } ) ; const NEP2_KDFPARAMS = { N : 16384 , r : 8 , p : 8 , dklen : 64 } ; const NEP2_ZERO = 0x01 ; const NEP2_ONE = 0x42 ; const NEP2_TWO = 0xe0 ; const NEP2_CIPHER = 'aes-256-ecb' ; const getNEP2Derived = async ( { password , salt , } ) : Promise < Buffer > => new Promise < Buffer > ( ( resolve , reject ) => scrypt ( Buffer . from ( password . normalize ( 'NFKC' ) , 'utf8' ) , salt , NEP2_KDFPARAMS . N , NEP2_KDFPARAMS . r , NEP2_KDFPARAMS . p , NEP2_KDFPARAMS . dklen , ( error , _progress , key ) => { if ( error != undefined ) { reject ( error ) ; } else if ( key ) { resolve ( Buffer . from ( [ ... key ] ) ) ; } } ) ) ; const getNEP2Salt = ( { addressVersion , privateKey , } ) => { const address = privateKeyToAddress ( { addressVersion , privateKey } ) ; return common . uInt256ToBuffer ( hash256 ( Buffer . from ( address , 'latin1' ) ) ) . slice ( 0 , 4 ) ; } ; const encryptNEP2 = async ( { addressVersion , password , privateKey , } ) : Promise < string > => { const salt = getNEP2Salt ( { addressVersion , privateKey } ) ; const derived = await getNEP2Derived ( { password , salt } ) ; const derived1 = derived . slice ( 0 , 32 ) ; const derived2 = derived . slice ( 32 , 64 ) ; const cipher = createCipheriv ( NEP2_CIPHER , derived2 , Buffer . alloc ( 0 , 0 ) ) ; cipher . setAutoPadding ( false ) ; cipher . end ( xor ( privateKey , derived1 ) ) ; const ciphertext = cipher . read ( ) as Buffer ; const result = Buffer . alloc ( 7 + 32 , 0 ) ; result . writeUInt8 ( NEP2_ZERO , 0 ) ; result . writeUInt8 ( NEP2_ONE , 1 ) ; result . writeUInt8 ( NEP2_TWO , 2 ) ; salt . copy ( result , 3 ) ; ciphertext . copy ( result , 7 ) ; return base58CheckEncode ( result ) ; } ; const isNEP2 = ( encryptedKey ) : boolean => { try { const decoded = base58CheckDecode ( encryptedKey ) ; return ( decoded . length === 39 && decoded . readUInt8 ( 0 ) === NEP2_ZERO && decoded . readUInt8 ( 1 ) === NEP2_ONE && decoded . readUInt8 ( 2 ) === NEP2_TWO ) ; } catch { return false ; } } ; const decryptNEP2 = async ( { addressVersion , encryptedKey , password , } ) : Promise < PrivateKey > => { const decoded = base58CheckDecode ( encryptedKey ) ; if ( decoded . length !== 39 || decoded . readUInt8 ( 0 ) !== NEP2_ZERO || decoded . readUInt8 ( 1 ) !== NEP2_ONE || decoded . readUInt8 ( 2 ) !== NEP2_TWO ) { throw new InvalidFormatError ( 'Invalid NEP2 format when decoded from encryptedKey.' ) ; } const salt = decoded . slice ( 3 , 7 ) ; const derived = await getNEP2Derived ( { password , salt } ) ; const derived1 = derived . slice ( 0 , 32 ) ; const derived2 = derived . slice ( 32 , 64 ) ; const decipher = createDecipheriv ( NEP2_CIPHER , derived2 , Buffer . alloc ( 0 , 0 ) ) ; decipher . setAutoPadding ( false ) ; decipher . end ( decoded . slice ( 7 , 7 + 32 ) ) ; const plainText = decipher . read ( ) as Buffer ; const privateKey = common . asPrivateKey ( xor ( plainText , derived1 ) ) ; const addressSalt = getNEP2Salt ( { addressVersion , privateKey } ) ; if ( ! salt . equals ( addressSalt ) ) { throw new Error ( 'Wrong passphrase.' ) ; } return common . bufferToPrivateKey ( privateKey ) ; } ; const checkMultisig = Buffer . from ( '9ed0dc3a' , 'hex' ) ; const checkSig = Buffer . from ( '56e7b327' , 'hex' ) ; type MultiSigResult = { readonly result : true ; readonly m : number ; readonly n : number ; readonly points : readonly ECPoint [ ] ; } | { readonly result : false ; } ; const isMultiSigContractWithResult = ( script ) : MultiSigResult => { let m = 0 ; let n = 0 ; let i = 0 ; let points = [ ] ; if ( script . length < 42 ) return { result : false } ; if ( script [ i ] > Op . PUSH16 ) return { result : false } ; if ( script [ i ] < Op . PUSH1 && script [ i ] !== 1 && script [ i ] !== 2 ) return { result : false } ; switch ( script [ i ] ) { case Op . PUSHINT8 : m = script [ ++ i ] ; ++ i ; break ; case Op . PUSHINT16 : m = script . readUInt16LE ( ++ i ) ; i += 2 ; break ; default : const b = script [ i ] ; if ( b >= Op . PUSH1 && b <= Op . PUSH16 ) { m = b - Op . PUSH0 ; ++ i ; break ; } return { result : false } ; } if ( m < 1 || m > 1024 ) return { result : false } ; while ( script [ i ] == Op . PUSHDATA1 ) { if ( script . length <= i + 35 ) return { result : false } ; if ( script [ ++ i ] !== 33 ) return { result : false } ; points . push ( common . bufferToECPoint ( script . slice ( i + 1 , i + 1 + 33 ) ) ) ; i += 34 ; ++ n ; } if ( n < m || n > 1024 ) return { result : false } ; switch ( script [ i ] ) { case Op . PUSHINT8 : if ( n != script [ ++ i ] ) return { result : false } ; ++ i ; break ; case Op . PUSHINT16 : if ( script . length < i + 3 || n != script . readUInt16LE ( ++ i ) ) return { result : false } ; i += 2 ; break ; default : const b = script [ i ] ; if ( b >= Op . PUSH1 && b <= Op . PUSH16 ) { if ( n !== b - Op . PUSH0 ) return { result : false } ; ++ i ; break ; } return { result : false } ; } if ( script . length !== i + 5 ) return { result : false } ; if ( script [ i ++ ] !== Op . SYSCALL ) return { result : false } ; if ( ! script . slice ( i ) . equals ( checkMultisig ) ) return { result : false } ; return { result : true , m , n , points } ; } ; const isSignatureContract = ( script ) => script . length === 40 && script [ 0 ] === Op . PUSHDATA1 && script [ 1 ] === 33 && script [ 35 ] === Op . SYSCALL && script . slice ( 36 ) . equals ( checkSig ) ; const isStandardContract = ( script ) => isSignatureContract ( script ) || isMultiSigContractWithResult ( script ) . result ; const getMultiSignatures = ( script ) => { let i = 0 ; const signatures = [ ] ; while ( i < script . length ) { if ( script [ i ++ ] !== Op . PUSHDATA1 ) { return undefined ; } if ( i + 65 > script . length ) { return undefined ; } if ( script [ i ++ ] !== 64 ) { return undefined ; } signatures . push ( script . slice ( i , i + 64 ) ) ; i += 64 ; } return signatures ; } ; const HARDENED_KEY_OFFSET = 0x80000000 ; const EXTENDED_KEY_BYTES = 78 ; const BIP32_VERSION = { public : 0x0488b21e , private : 0x0488ade4 } ; export interface HDNode { readonly privateKey ? : PrivateKey ; readonly publicKey : Buffer ; readonly version : number ; readonly depth : number ; readonly parentFingerprint ? : Buffer ; readonly index : number ; readonly chainCode : Buffer ; } const getFingerprint = ( value ) => hash160 ( value ) . slice ( 0 , 4 ) ; const parseMasterSeed = ( seedIn ) : HDNode => { const seed = seedIn instanceof Buffer ? seedIn : Buffer . from ( seedIn , 'hex' ) ; const hmac = hmacSha512 ( 'NEO Seed' , seed ) ; const privateKey = hmac . slice ( 0 , 32 ) ; const publicKey = privateKeyToPublicKey ( common . asPrivateKey ( privateKey ) ) ; const chainCode = hmac . slice ( 32 ) ; return { privateKey : common . asPrivateKey ( privateKey ) , publicKey , version : BIP32_VERSION . private , depth : 0 , index : 0 , chainCode } ; } ; const parseExtendedKey = ( key ) : HDNode => { const data = base58CheckDecode ( key ) ; if ( data . length !== EXTENDED_KEY_BYTES ) { throw new InvalidBIP32ExtendedKeyError ( key ) ; } const version = data . readUInt32BE ( 0 ) ; const depth = data . readUInt8 ( 4 ) ; const parentFingerprintIn = data . slice ( 5 , 9 ) ; const parentFingerprint = parentFingerprintIn . readUInt32BE ( 0 ) === 0 ? undefined : parentFingerprintIn ; const index = data . readUInt32BE ( 9 ) ; const chainCode = data . slice ( 13 , 45 ) ; const keyData = data . slice ( 45 ) ; const privateKeyIn = keyData [ 0 ] === 0 ? keyData . slice ( 1 ) : undefined ; const publicKey = keyData [ 0 ] !== 0 ? keyData : undefined ; if ( privateKeyIn !== undefined ) { if ( version !== BIP32_VERSION . private ) { throw new InvalidBIP32VersionError ( version , BIP32_VERSION . private ) ; } const privateKey = common . asPrivateKey ( privateKeyIn ) ; return { privateKey , publicKey : privateKeyToPublicKey ( privateKey ) , version , depth , parentFingerprint , index , chainCode } ; } if ( publicKey !== undefined ) { if ( version !== BIP32_VERSION . public ) { throw new InvalidBIP32VersionError ( version , BIP32_VERSION . public ) ; } return { publicKey , version , depth , parentFingerprint , index , chainCode } ; } throw new InvalidBIP32ExtendedKeyError ( key ) ; } ; const deriveChildKey = ( node , index , hardened ) : HDNode => { if ( index >= HARDENED_KEY_OFFSET ) { throw new InvalidBIP32ChildIndexError ( index ) ; } const data = Buffer . alloc ( 37 ) ; if ( hardened ) { if ( ! node . privateKey ) { throw new InvalidBIP32HardenedError ( ) ; } node . privateKey . copy ( data , 1 ) ; } else { node . publicKey . copy ( data , 0 ) ; } data . writeUInt32BE ( index , 33 ) ; const sha = hmacSha512 ( node . chainCode , data ) ; const shaLeft = new BN ( sha . slice ( 0 , 32 ) ) ; const shaRight = sha . slice ( 32 ) ; if ( shaLeft . cmp ( ec ( ) . n as BN ) >= 0 ) { return deriveChildKey ( node , index + 1 , hardened ) ; } if ( node . privateKey ) { const childKeyPrivate = shaLeft . add ( new BN ( node . privateKey ) ) . mod ( ec ( ) . n as BN ) ; if ( childKeyPrivate . cmp ( new BN ( 0 ) ) === 0 ) { return deriveChildKey ( node , index + 1 , hardened ) ; } const privateKey = common . asPrivateKey ( childKeyPrivate . toArrayLike ( Buffer , 'be' , 32 ) ) ; const publicKey = privateKeyToPublicKey ( privateKey ) ; return { depth : node . depth + 1 , privateKey , publicKey , chainCode : shaRight , parentFingerprint : getFingerprint ( node . publicKey ) , index : hardened ? index + HARDENED_KEY_OFFSET : index , version : BIP32_VERSION . private } ; } const parentKey = ec ( ) . keyFromPublic ( node . publicKey ) . getPublic ( ) ; const childKey = ec ( ) . g . mul ( shaLeft ) . add ( parentKey ) ; if ( childKey . isInfinity ( ) ) { return deriveChildKey ( node , index + 1 , false ) ; } const compressedChildKey = Buffer . from ( childKey . encode ( undefined , true ) ) ; return { depth : node . depth + 1 , publicKey : compressedChildKey , chainCode : shaRight , parentFingerprint : getFingerprint ( node . publicKey ) , index , version : BIP32_VERSION . public } ; } ; const serializeHDNode = ( node , privateNode = true ) : string => { if ( privateNode && node . privateKey === undefined ) { throw new InvalidBIP32SerializePrivateNodeError ( ) ; } const data = Buffer . alloc ( EXTENDED_KEY_BYTES ) ; data . writeUInt32BE ( privateNode ? BIP32_VERSION . private : BIP32_VERSION . public , 0 ) ; data . writeUInt8 ( node . depth , 4 ) ; if ( node . parentFingerprint !== undefined ) { node . parentFingerprint . copy ( data , 5 ) ; } data . writeUInt32BE ( node . index , 9 ) ; node . chainCode . copy ( data , 13 ) ; const key = privateNode && node . privateKey !== undefined ? node . privateKey : node . publicKey ; key . copy ( data , EXTENDED_KEY_BYTES - key . length ) ; const checksum = hash256 ( data ) . slice ( 0 , 4 ) ; return base58 . encode ( Buffer . concat ( [ data , checksum ] ) ) ; } ; export const crypto = { addPublicKey , rmd160 , sha1 , sha256 , hash160 , hash256 , calculateHash , sign , verify , privateKeyToPublicKey , toScriptHash , getContractHash , toECPoint , createKeyPair , scriptHashToAddress , addressToScriptHash , createInvocationScript , createVerificationScript , createWitness , createWitnessForSignature , createSignatureRedeemScript , getVerificationScriptHash , createMultiSignatureInvocationScript , createMultiSignatureVerificationScript , createMultiSignatureWitness , createMultiSignatureRedeemScript , getConsensusAddress , getBFTAddress , privateKeyToWIF , wifToPrivateKey , publicKeyToScriptHash , privateKeyToScriptHash , privateKeyToAddress , isNEP2 , encryptNEP2 , decryptNEP2 , createPrivateKey , isMultiSigContractWithResult , isSignatureContract , isStandardContract , getMultiSignatures , parseExtendedKey , parseMasterSeed , deriveChildKey , serializeHDNode } ;