import { hash160 , num2VarInt , reverseHex , StringStream } from "../../u" ; import { Account , getPublicKeysFromVerificationScript , getSignaturesFromInvocationScript , getSigningThresholdFromVerificationScript , getVerificationScriptFromPublicKey , verify , } from "../../wallet" ; export interface WitnessLike { invocationScript : string ; verificationScript : string ; } /**
 * A Witness is a section of VM code that is ran during the verification of the transaction.
 *
 * For example, the most common witness is the VM Script that pushes the ECDSA signature into the VM and calling CHECKSIG to prove the authority to spend the TransactionInputs in the transaction.
 */ export class Witness { public static deserialize ( hex ) { const ss = new StringStream ( hex ) ; return this . fromStream ( ss ) ; } public static fromStream ( ss ) { const invocationScript = ss . readVarBytes ( ) ; const verificationScript = ss . readVarBytes ( ) ; return new Witness ( { invocationScript , verificationScript } ) ; } public static fromSignature ( sig , publicKey ) { const invocationScript = "40" + sig ; const verificationScript = getVerificationScriptFromPublicKey ( publicKey ) ; return new Witness ( { invocationScript , verificationScript } ) ; } param tx param sigs param acctOrVerificationScript public static buildMultiSig ( tx , sigs , acctOrVerificationScript ) { const verificationScript = typeof acctOrVerificationScript === "string" ? acctOrVerificationScript : acctOrVerificationScript . contract . script ; const publicKeys = getPublicKeysFromVerificationScript ( verificationScript ) ; const orderedSigs = Array ( publicKeys . length ) . fill ( "" ) ; sigs . forEach ( ( element ) => { if ( typeof element === "string" ) { const position = publicKeys . findIndex ( ( key ) => verify ( tx , element , key ) ) ; if ( position === - 1 ) { throw new Error ( `Invalid signature given: ${ element }` ) ; } orderedSigs [ position ] = element ; } else if ( element instanceof Witness ) { const keys = getPublicKeysFromVerificationScript ( element . verificationScript ) ; if ( keys . length !== 1 ) { throw new Error ( "Given witness contains more than 1 public key!" ) ; } const position = publicKeys . indexOf ( keys [ 0 ] ) ; orderedSigs [ position ] = getSignaturesFromInvocationScript ( element . invocationScript ) [ 0 ] ; } else { throw new Error ( "Unable to process given signature" ) ; } } ) ; const signingThreshold = getSigningThresholdFromVerificationScript ( verificationScript ) ; const validSigs = orderedSigs . filter ( ( s ) => s !== "" ) ; if ( validSigs . length < signingThreshold ) { throw new Error ( `Insufficient signatures: expected ${ signingThreshold } but got ${ validSigs . length } instead` ) ; } return new Witness ( { invocationScript : validSigs . slice ( 0 , signingThreshold ) . map ( ( s ) => "40" + s ) . join ( "" ) , verificationScript , } ) ; } public invocationScript : string ; public verificationScript : string ; private _scriptHash = "" ; public constructor ( obj ) { if ( ! obj || obj . invocationScript === undefined || obj . verificationScript === undefined ) { throw new Error ( "Witness requires invocationScript and verificationScript fields" ) ; } this . invocationScript = obj . invocationScript ; this . verificationScript = obj . verificationScript ; } public get scriptHash ( ) : string { if ( this . _scriptHash ) { return this . _scriptHash ; } else if ( this . verificationScript ) { this . _scriptHash = reverseHex ( hash160 ( this . verificationScript ) ) ; return this . _scriptHash ; } else { throw new Error ( "Unable to produce scriptHash from empty verificationScript" ) ; } } public set scriptHash ( value ) { if ( this . verificationScript ) { throw new Error ( "Unable to set scriptHash when verificationScript is not empty" ) ; } this . _scriptHash = value ; } public serialize ( ) { const invoLength = num2VarInt ( this . invocationScript . length / 2 ) ; const veriLength = num2VarInt ( this . verificationScript . length / 2 ) ; return ( invoLength + this . invocationScript + veriLength + this . verificationScript ) ; } public export ( ) { return { invocationScript : this . invocationScript , verificationScript : this . verificationScript , } ; } public equals ( other ) { return ( this . invocationScript === other . invocationScript && this . verificationScript === other . verificationScript ) ; } private generateScriptHash ( ) { this . _scriptHash = reverseHex ( hash160 ( this . verificationScript ) ) ; } } export default Witness ;