diff --git a/src/psbt.js b/src/psbt.js index 3326995..e28cd49 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -204,18 +204,7 @@ class Psbt { range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx)); return this; } - finalizeInput( - inputIndex, - { - classifyScript: classifyScriptF, - canFinalize: canFinalizeF, - getFinalScripts: getFinalScriptsF, - } = { - classifyScript, - canFinalize, - getFinalScripts, - }, - ) { + finalizeInput(inputIndex, finalScriptsFunc = getFinalScripts) { const input = utils_1.checkForInput(this.data.inputs, inputIndex); const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, @@ -223,14 +212,11 @@ class Psbt { this.__CACHE, ); if (!script) throw new Error(`No script found for input #${inputIndex}`); - const scriptType = classifyScriptF(script); - if (!canFinalizeF(input, script, scriptType)) - throw new Error(`Can not finalize input #${inputIndex}`); checkPartialSigSighashes(input); - const { finalScriptSig, finalScriptWitness } = getFinalScriptsF( + const { finalScriptSig, finalScriptWitness } = finalScriptsFunc( + inputIndex, + input, script, - scriptType, - input.partialSig, isSegwit, isP2SH, isP2WSH, @@ -749,7 +735,20 @@ function getTxCacheValue(key, name, inputs, c) { if (key === '__FEE_RATE') return c.__FEE_RATE; else if (key === '__FEE') return c.__FEE; } -function getFinalScripts( +function getFinalScripts(inputIndex, input, script, isSegwit, isP2SH, isP2WSH) { + const scriptType = classifyScript(script); + if (!canFinalize(input, script, scriptType)) + throw new Error(`Can not finalize input #${inputIndex}`); + return prepareFinalScripts( + script, + scriptType, + input.partialSig, + isSegwit, + isP2SH, + isP2WSH, + ); +} +function prepareFinalScripts( script, scriptType, partialSig, diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 6fa6f43..15a6ce8 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -253,15 +253,7 @@ export class Psbt { finalizeInput( inputIndex: number, - { - classifyScript: classifyScriptF, - canFinalize: canFinalizeF, - getFinalScripts: getFinalScriptsF, - }: IFinalizeFuncs = { - classifyScript, - canFinalize, - getFinalScripts, - }, + finalScriptsFunc: FinalScriptsFunc = getFinalScripts, ): this { const input = checkForInput(this.data.inputs, inputIndex); const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( @@ -271,16 +263,12 @@ export class Psbt { ); if (!script) throw new Error(`No script found for input #${inputIndex}`); - const scriptType = classifyScriptF(script); - if (!canFinalizeF(input, script, scriptType)) - throw new Error(`Can not finalize input #${inputIndex}`); - checkPartialSigSighashes(input); - const { finalScriptSig, finalScriptWitness } = getFinalScriptsF( + const { finalScriptSig, finalScriptWitness } = finalScriptsFunc( + inputIndex, + input, script, - scriptType, - input.partialSig!, isSegwit, isP2SH, isP2WSH, @@ -746,39 +734,6 @@ class PsbtTransaction implements ITransaction { } } -// This interface is added to allow for custom scripts to be finalized with PSBT. -interface IFinalizeFuncs { - classifyScript: FinalizeFuncClassifyScript; - canFinalize: FinalizeFuncCanFinalize; - getFinalScripts: FinalizeFuncGetFinalScripts; -} - -// Takes the meaningful script (redeemScript for P2SH and witnessScript for P2WSH) -// and returns a string to classify the script. -type FinalizeFuncClassifyScript = (script: Buffer) => string; -// Takes the Psbt data for the input and the meaningful script and its type name. -// returns true if we can finalize the input -type FinalizeFuncCanFinalize = ( - input: PsbtInput, - script: Buffer, - scriptType: string, -) => boolean; -// Takes the meaningful script, its type name, all the signatures from this input, -// and 3 booleans to tell you if it is segwit, P2SH, and P2WSH. -// it returns finalScriptSig and finalScriptWitness to be placed in the Psbt. -// if one is not needed, it should be undefined. (In TypeScript, it must be declared but undefined.) -type FinalizeFuncGetFinalScripts = ( - script: Buffer, - scriptType: string, - partialSig: PartialSig[], - isSegwit: boolean, - isP2SH: boolean, - isP2WSH: boolean, -) => { - finalScriptSig: Buffer | undefined; - finalScriptWitness: Buffer | undefined; -}; - function canFinalize( input: PsbtInput, script: Buffer, @@ -996,7 +951,49 @@ function getTxCacheValue( else if (key === '__FEE') return c.__FEE!; } +/** + * This function must do two things: + * 1. Check if the `input` can be finalized. If it can not be finalized, throw. + * ie. `Can not finalize input #${inputIndex}` + * 2. Create the finalScriptSig and finalScriptWitness Buffers. + */ +type FinalScriptsFunc = ( + inputIndex: number, // Which input is it? + input: PsbtInput, // The PSBT input contents + script: Buffer, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.) + isSegwit: boolean, // Is it segwit? + isP2SH: boolean, // Is it P2SH? + isP2WSH: boolean, // Is it P2WSH? +) => { + finalScriptSig: Buffer | undefined; + finalScriptWitness: Buffer | undefined; +}; + function getFinalScripts( + inputIndex: number, + input: PsbtInput, + script: Buffer, + isSegwit: boolean, + isP2SH: boolean, + isP2WSH: boolean, +): { + finalScriptSig: Buffer | undefined; + finalScriptWitness: Buffer | undefined; +} { + const scriptType = classifyScript(script); + if (!canFinalize(input, script, scriptType)) + throw new Error(`Can not finalize input #${inputIndex}`); + return prepareFinalScripts( + script, + scriptType, + input.partialSig!, + isSegwit, + isP2SH, + isP2WSH, + ); +} + +function prepareFinalScripts( script: Buffer, scriptType: string, partialSig: PartialSig[], diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 516d72f..44eb4d8 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,5 +1,5 @@ import { Psbt as PsbtBase } from 'bip174'; -import { KeyValue, PartialSig, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; +import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; @@ -58,7 +58,7 @@ export declare class Psbt { getFeeRate(): number; getFee(): number; finalizeAllInputs(): this; - finalizeInput(inputIndex: number, { classifyScript: classifyScriptF, canFinalize: canFinalizeF, getFinalScripts: getFinalScriptsF, }?: IFinalizeFuncs): this; + finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc): this; validateSignaturesOfAllInputs(): boolean; validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean; signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; @@ -124,14 +124,18 @@ interface HDSignerAsync extends HDSignerBase { derivePath(path: string): HDSignerAsync; sign(hash: Buffer): Promise; } -interface IFinalizeFuncs { - classifyScript: FinalizeFuncClassifyScript; - canFinalize: FinalizeFuncCanFinalize; - getFinalScripts: FinalizeFuncGetFinalScripts; -} -declare type FinalizeFuncClassifyScript = (script: Buffer) => string; -declare type FinalizeFuncCanFinalize = (input: PsbtInput, script: Buffer, scriptType: string) => boolean; -declare type FinalizeFuncGetFinalScripts = (script: Buffer, scriptType: string, partialSig: PartialSig[], isSegwit: boolean, isP2SH: boolean, isP2WSH: boolean) => { +/** + * This function must do two things: + * 1. Check if the `input` can be finalized. If it can not be finalized, throw. + * ie. `Can not finalize input #${inputIndex}` + * 2. Create the finalScriptSig and finalScriptWitness Buffers. + */ +declare type FinalScriptsFunc = (inputIndex: number, // Which input is it? +input: PsbtInput, // The PSBT input contents +script: Buffer, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.) +isSegwit: boolean, // Is it segwit? +isP2SH: boolean, // Is it P2SH? +isP2WSH: boolean) => { finalScriptSig: Buffer | undefined; finalScriptWitness: Buffer | undefined; };