diff --git a/src/psbt.js b/src/psbt.js index 7cab795..b80951f 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -293,8 +293,9 @@ class Psbt { script, inputIndex, 'input', - input.redeemScript, - input.witnessScript, + input.redeemScript || redeemFromFinalScriptSig(input.finalScriptSig), + input.witnessScript || + redeemFromFinalWitnessScript(input.finalScriptWitness), ); const type = result.type === 'raw' ? '' : result.type + '-'; const mainType = classifyScript(result.meaningfulScript); @@ -1272,6 +1273,36 @@ function pubkeyInOutput(pubkey, output, outputIndex, cache) { ); return pubkeyInScript(pubkey, meaningfulScript); } +function redeemFromFinalScriptSig(finalScript) { + if (!finalScript) return; + const decomp = bscript.decompile(finalScript); + if (!decomp) return; + const lastItem = decomp[decomp.length - 1]; + if ( + !Buffer.isBuffer(lastItem) || + isPubkeyLike(lastItem) || + isSigLike(lastItem) + ) + return; + const sDecomp = bscript.decompile(lastItem); + if (!sDecomp) return; + return lastItem; +} +function redeemFromFinalWitnessScript(finalScript) { + if (!finalScript) return; + const decomp = scriptWitnessToWitnessStack(finalScript); + const lastItem = decomp[decomp.length - 1]; + if (isPubkeyLike(lastItem)) return; + const sDecomp = bscript.decompile(lastItem); + if (!sDecomp) return; + return lastItem; +} +function isPubkeyLike(buf) { + return buf.length === 33 && bscript.isCanonicalPubKey(buf); +} +function isSigLike(buf) { + return bscript.isCanonicalScriptSignature(buf); +} function getMeaningfulScript( script, index, diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 5e88fe0..a5a2214 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -543,7 +543,8 @@ describe(`Psbt`, () => { }); describe('getInputType', () => { - const { publicKey } = ECPair.makeRandom(); + const key = ECPair.makeRandom(); + const { publicKey } = key; const p2wpkhPub = (pubkey: Buffer): Buffer => payments.p2wpkh({ pubkey, @@ -569,19 +570,26 @@ describe(`Psbt`, () => { redeemGetter, witnessGetter, expectedType, + finalize, }: any): void { const psbt = new Psbt(); - psbt.addInput({ - hash: - '0000000000000000000000000000000000000000000000000000000000000000', - index: 0, - witnessUtxo: { - script: outerScript(innerScript(publicKey)), - value: 2e3, - }, - ...(redeemGetter ? { redeemScript: redeemGetter(publicKey) } : {}), - ...(witnessGetter ? { witnessScript: witnessGetter(publicKey) } : {}), - }); + psbt + .addInput({ + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, + witnessUtxo: { + script: outerScript(innerScript(publicKey)), + value: 2e3, + }, + ...(redeemGetter ? { redeemScript: redeemGetter(publicKey) } : {}), + ...(witnessGetter ? { witnessScript: witnessGetter(publicKey) } : {}), + }) + .addOutput({ + script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d'), + value: 1800, + }); + if (finalize) psbt.signInput(0, key).finalizeInput(0); const type = psbt.getInputType(0); assert.strictEqual(type, expectedType, 'incorrect input type'); } @@ -613,6 +621,7 @@ describe(`Psbt`, () => { redeemGetter: p2wpkhPub, witnessGetter: null, expectedType: 'p2sh-witnesspubkeyhash', + finalize: true, }, { innerScript: p2pkhPub, @@ -620,6 +629,7 @@ describe(`Psbt`, () => { redeemGetter: null, witnessGetter: p2pkhPub, expectedType: 'p2wsh-pubkeyhash', + finalize: true, }, { innerScript: p2pkhPub, diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index cb14fc5..39d3a4c 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -363,8 +363,9 @@ export class Psbt { script, inputIndex, 'input', - input.redeemScript, - input.witnessScript, + input.redeemScript || redeemFromFinalScriptSig(input.finalScriptSig), + input.witnessScript || + redeemFromFinalWitnessScript(input.finalScriptWitness), ); const type = result.type === 'raw' ? '' : result.type + '-'; const mainType = classifyScript(result.meaningfulScript); @@ -1642,6 +1643,44 @@ function pubkeyInOutput( return pubkeyInScript(pubkey, meaningfulScript); } +function redeemFromFinalScriptSig( + finalScript: Buffer | undefined, +): Buffer | undefined { + if (!finalScript) return; + const decomp = bscript.decompile(finalScript); + if (!decomp) return; + const lastItem = decomp[decomp.length - 1]; + if ( + !Buffer.isBuffer(lastItem) || + isPubkeyLike(lastItem) || + isSigLike(lastItem) + ) + return; + const sDecomp = bscript.decompile(lastItem); + if (!sDecomp) return; + return lastItem; +} + +function redeemFromFinalWitnessScript( + finalScript: Buffer | undefined, +): Buffer | undefined { + if (!finalScript) return; + const decomp = scriptWitnessToWitnessStack(finalScript); + const lastItem = decomp[decomp.length - 1]; + if (isPubkeyLike(lastItem)) return; + const sDecomp = bscript.decompile(lastItem); + if (!sDecomp) return; + return lastItem; +} + +function isPubkeyLike(buf: Buffer): boolean { + return buf.length === 33 && bscript.isCanonicalPubKey(buf); +} + +function isSigLike(buf: Buffer): boolean { + return bscript.isCanonicalScriptSignature(buf); +} + function getMeaningfulScript( script: Buffer, index: number,