From 5d19abfb85641887060804a2a396eacd6fea9117 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Wed, 29 Apr 2020 13:32:57 +0900
Subject: [PATCH] Add ability to get redeemScript|witnessScript from finalized
 scripts

---
 src/psbt.js       | 35 +++++++++++++++++++++++++++++++++--
 test/psbt.spec.ts | 34 ++++++++++++++++++++++------------
 ts_src/psbt.ts    | 43 +++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 96 insertions(+), 16 deletions(-)

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,