From 62f174902127ad9ccf30414b52e2dc23c588526f Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Mon, 24 Jun 2019 17:09:48 +0700
Subject: [PATCH 001/111] Add BIP174 dependency

---
 package-lock.json | 5 +++++
 package.json      | 1 +
 2 files changed, 6 insertions(+)

diff --git a/package-lock.json b/package-lock.json
index d769ef5..30a6172 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -199,6 +199,11 @@
         "file-uri-to-path": "1.0.0"
       }
     },
+    "bip174": {
+      "version": "0.0.4",
+      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.4.tgz",
+      "integrity": "sha512-KirRr35qCNih51thdf6klTevFTtm/cIw2iqumRgxtqFjNREFG78fFylfCkMM9aQdlYfSoj3sczxgXham0jZVnw=="
+    },
     "bip32": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.3.tgz",
diff --git a/package.json b/package.json
index b8ec5f3..fd5f5cf 100644
--- a/package.json
+++ b/package.json
@@ -47,6 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
+    "bip174": "0.0.4",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",

From 6a5e395ebd27eb3ae08953cae4c78ee4ba76c41f Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Mon, 24 Jun 2019 17:20:15 +0700
Subject: [PATCH 002/111] Extend BIP174 PSBT base class

---
 src/index.js     | 2 ++
 src/psbt.js      | 9 +++++++++
 ts_src/index.ts  | 1 +
 ts_src/psbt.ts   | 7 +++++++
 types/index.d.ts | 1 +
 types/psbt.d.ts  | 4 ++++
 6 files changed, 24 insertions(+)
 create mode 100644 src/psbt.js
 create mode 100644 ts_src/psbt.ts
 create mode 100644 types/psbt.d.ts

diff --git a/src/index.js b/src/index.js
index 499380e..bc71f02 100644
--- a/src/index.js
+++ b/src/index.js
@@ -16,6 +16,8 @@ const script = require('./script');
 exports.script = script;
 var block_1 = require('./block');
 exports.Block = block_1.Block;
+var psbt_1 = require('./psbt');
+exports.Psbt = psbt_1.Psbt;
 var script_1 = require('./script');
 exports.opcodes = script_1.OPS;
 var transaction_1 = require('./transaction');
diff --git a/src/psbt.js b/src/psbt.js
new file mode 100644
index 0000000..70b60e2
--- /dev/null
+++ b/src/psbt.js
@@ -0,0 +1,9 @@
+'use strict';
+Object.defineProperty(exports, '__esModule', { value: true });
+const bip174_1 = require('bip174');
+class Psbt extends bip174_1.Psbt {
+  constructor() {
+    super();
+  }
+}
+exports.Psbt = Psbt;
diff --git a/ts_src/index.ts b/ts_src/index.ts
index 4f2d498..58c39c7 100644
--- a/ts_src/index.ts
+++ b/ts_src/index.ts
@@ -9,6 +9,7 @@ import * as script from './script';
 export { ECPair, address, bip32, crypto, networks, payments, script };
 
 export { Block } from './block';
+export { Psbt } from './psbt';
 export { OPS as opcodes } from './script';
 export { Transaction } from './transaction';
 export { TransactionBuilder } from './transaction_builder';
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
new file mode 100644
index 0000000..08d2670
--- /dev/null
+++ b/ts_src/psbt.ts
@@ -0,0 +1,7 @@
+import { Psbt as PsbtBase } from 'bip174';
+
+export class Psbt extends PsbtBase {
+  constructor() {
+    super();
+  }
+}
diff --git a/types/index.d.ts b/types/index.d.ts
index 93d72e4..fc5a932 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -7,6 +7,7 @@ import * as payments from './payments';
 import * as script from './script';
 export { ECPair, address, bip32, crypto, networks, payments, script };
 export { Block } from './block';
+export { Psbt } from './psbt';
 export { OPS as opcodes } from './script';
 export { Transaction } from './transaction';
 export { TransactionBuilder } from './transaction_builder';
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
new file mode 100644
index 0000000..880ebd2
--- /dev/null
+++ b/types/psbt.d.ts
@@ -0,0 +1,4 @@
+import { Psbt as PsbtBase } from 'bip174';
+export declare class Psbt extends PsbtBase {
+    constructor();
+}

From 2ed89cdc68b83b286c5d216b4257a8a31f56281f Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Tue, 25 Jun 2019 17:47:26 +0700
Subject: [PATCH 003/111] Update BIP174 package

---
 package-lock.json | 6 +++---
 package.json      | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 30a6172..ac0507b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -200,9 +200,9 @@
       }
     },
     "bip174": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.4.tgz",
-      "integrity": "sha512-KirRr35qCNih51thdf6klTevFTtm/cIw2iqumRgxtqFjNREFG78fFylfCkMM9aQdlYfSoj3sczxgXham0jZVnw=="
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.5.tgz",
+      "integrity": "sha512-NNt0e9pz7h8EhC+pNAcB8G0Ca/Lei42YnAtPMewpcuLzRJGgaJO4vgtBpeQHH/f3fWlabZwSh/3tyEHwFNXlRw=="
     },
     "bip32": {
       "version": "2.0.3",
diff --git a/package.json b/package.json
index fd5f5cf..6527baf 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
-    "bip174": "0.0.4",
+    "bip174": "0.0.5",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",

From 6ed635d7b41b976f2d88dc7492e857c8adda0543 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Tue, 25 Jun 2019 18:22:00 +0700
Subject: [PATCH 004/111] Flesh out signInput interface

---
 src/psbt.js     | 12 ++++++++++++
 ts_src/psbt.ts  | 18 ++++++++++++++++++
 types/psbt.d.ts |  2 ++
 3 files changed, 32 insertions(+)

diff --git a/src/psbt.js b/src/psbt.js
index 70b60e2..887c814 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -5,5 +5,17 @@ class Psbt extends bip174_1.Psbt {
   constructor() {
     super();
   }
+  signInput(inputIndex, keyPair) {
+    // TODO: Implement BIP174 pre-sign checks:
+    // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
+    // TODO: Get hash to sign
+    const hash = Buffer.alloc(32);
+    const partialSig = {
+      pubkey: keyPair.publicKey,
+      signature: keyPair.sign(hash),
+    };
+    this.addPartialSigToInput(inputIndex, partialSig);
+    return this;
+  }
 }
 exports.Psbt = Psbt;
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 08d2670..8fec780 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,7 +1,25 @@
 import { Psbt as PsbtBase } from 'bip174';
+import { Signer } from './ecpair';
 
 export class Psbt extends PsbtBase {
   constructor() {
     super();
   }
+
+  signInput(inputIndex: number, keyPair: Signer): Psbt {
+    // TODO: Implement BIP174 pre-sign checks:
+    // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
+
+    // TODO: Get hash to sign
+    const hash = Buffer.alloc(32);
+
+    const partialSig = {
+      pubkey: keyPair.publicKey,
+      signature: keyPair.sign(hash),
+    };
+
+    this.addPartialSigToInput(inputIndex, partialSig);
+
+    return this;
+  }
 }
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 880ebd2..a58b982 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -1,4 +1,6 @@
 import { Psbt as PsbtBase } from 'bip174';
+import { Signer } from './ecpair';
 export declare class Psbt extends PsbtBase {
     constructor();
+    signInput(inputIndex: number, keyPair: Signer): Psbt;
 }

From ff3caa02fe23a949bad78b23c29a25d5da04d2e1 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 26 Jun 2019 16:29:00 +0700
Subject: [PATCH 005/111] Add BIP174 pseudo code for signing checks

---
 src/psbt.js    | 21 +++++++++++++++++++++
 ts_src/psbt.ts | 21 +++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/src/psbt.js b/src/psbt.js
index 887c814..5bdb176 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -8,6 +8,27 @@ class Psbt extends bip174_1.Psbt {
   signInput(inputIndex, keyPair) {
     // TODO: Implement BIP174 pre-sign checks:
     // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
+    //
+    // if non_witness_utxo.exists:
+    //     assert(sha256d(non_witness_utxo) == psbt.tx.innput[i].prevout.hash)
+    //     if redeemScript.exists:
+    //         assert(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey == P2SH(redeemScript))
+    //         sign_non_witness(redeemScript)
+    //     else:
+    //         sign_non_witness(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey)
+    // else if witness_utxo.exists:
+    //     if redeemScript.exists:
+    //         assert(witness_utxo.scriptPubKey == P2SH(redeemScript))
+    //         script = redeemScript
+    //     else:
+    //         script = witness_utxo.scriptPubKey
+    //     if IsP2WPKH(script):
+    //         sign_witness(P2PKH(script[2:22]))
+    //     else if IsP2WSH(script):
+    //         assert(script == P2WSH(witnessScript))
+    //         sign_witness(witnessScript)
+    // else:
+    //     assert False
     // TODO: Get hash to sign
     const hash = Buffer.alloc(32);
     const partialSig = {
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 8fec780..73308a2 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -9,6 +9,27 @@ export class Psbt extends PsbtBase {
   signInput(inputIndex: number, keyPair: Signer): Psbt {
     // TODO: Implement BIP174 pre-sign checks:
     // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
+    //
+    // if non_witness_utxo.exists:
+    //     assert(sha256d(non_witness_utxo) == psbt.tx.innput[i].prevout.hash)
+    //     if redeemScript.exists:
+    //         assert(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey == P2SH(redeemScript))
+    //         sign_non_witness(redeemScript)
+    //     else:
+    //         sign_non_witness(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey)
+    // else if witness_utxo.exists:
+    //     if redeemScript.exists:
+    //         assert(witness_utxo.scriptPubKey == P2SH(redeemScript))
+    //         script = redeemScript
+    //     else:
+    //         script = witness_utxo.scriptPubKey
+    //     if IsP2WPKH(script):
+    //         sign_witness(P2PKH(script[2:22]))
+    //     else if IsP2WSH(script):
+    //         assert(script == P2WSH(witnessScript))
+    //         sign_witness(witnessScript)
+    // else:
+    //     assert False
 
     // TODO: Get hash to sign
     const hash = Buffer.alloc(32);

From 98dff9a47e810292b17284a3797cfee437e994d1 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 26 Jun 2019 17:55:02 +0700
Subject: [PATCH 006/111] Check Non-witness UTXO hash when signing PSBT input

---
 src/psbt.js    | 19 +++++++++++++++++++
 ts_src/psbt.ts | 19 +++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/src/psbt.js b/src/psbt.js
index 5bdb176..5d40ab4 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -1,6 +1,7 @@
 'use strict';
 Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
+const transaction_1 = require('./transaction');
 class Psbt extends bip174_1.Psbt {
   constructor() {
     super();
@@ -29,6 +30,24 @@ class Psbt extends bip174_1.Psbt {
     //         sign_witness(witnessScript)
     // else:
     //     assert False
+    const input = this.inputs[inputIndex];
+    if (input === undefined) throw new Error(`No input #${inputIndex}`);
+    // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
+    if (input.nonWitnessUtxo) {
+      const unsignedTx = transaction_1.Transaction.fromBuffer(
+        this.globalMap.unsignedTx,
+      );
+      const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
+        input.nonWitnessUtxo,
+      );
+      const inputHash = unsignedTx.ins[inputIndex].hash;
+      const utxoHash = nonWitnessUtxoTx.getHash();
+      if (Buffer.compare(inputHash, utxoHash) !== 0) {
+        throw new Error(
+          `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
+        );
+      }
+    }
     // TODO: Get hash to sign
     const hash = Buffer.alloc(32);
     const partialSig = {
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 73308a2..9b0e9ce 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,5 +1,6 @@
 import { Psbt as PsbtBase } from 'bip174';
 import { Signer } from './ecpair';
+import { Transaction } from './transaction';
 
 export class Psbt extends PsbtBase {
   constructor() {
@@ -31,6 +32,24 @@ export class Psbt extends PsbtBase {
     // else:
     //     assert False
 
+    const input = this.inputs[inputIndex];
+    if (input === undefined) throw new Error(`No input #${inputIndex}`);
+
+    // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
+    if (input.nonWitnessUtxo) {
+      const unsignedTx = Transaction.fromBuffer(this.globalMap.unsignedTx!);
+      const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
+
+      const inputHash = unsignedTx.ins[inputIndex].hash;
+      const utxoHash = nonWitnessUtxoTx.getHash();
+
+      if (Buffer.compare(inputHash, utxoHash) !== 0) {
+        throw new Error(
+          `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
+        );
+      }
+    }
+
     // TODO: Get hash to sign
     const hash = Buffer.alloc(32);
 

From 2dcac556015e736e215c293e7af71a17a1a0a88a Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 26 Jun 2019 18:20:49 +0700
Subject: [PATCH 007/111] Add simple tests for non-witness UTXO check

---
 test/psbt.js | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100644 test/psbt.js

diff --git a/test/psbt.js b/test/psbt.js
new file mode 100644
index 0000000..9422d6e
--- /dev/null
+++ b/test/psbt.js
@@ -0,0 +1,43 @@
+const { describe, it, beforeEach } = require('mocha')
+const assert = require('assert')
+
+const ECPair = require('../src/ecpair')
+const Psbt = require('..').Psbt
+
+// For now, just hardcoding some test values is fine
+// const fixtures = require('./fixtures/psbt')
+
+describe(`Psbt`, () => {
+  // constants
+  const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex'))
+
+  describe('signInput', () => {
+    it('throws if non-witness UTXO hash doesn\'t match the hash specified in the prevout', () => {
+      const inputUtxo = '0200000001c1602ba68c8c241450a78b61dbfde272989181d07537b1e70d31b7db939557f2000000006a473044022029872b97579850c87658e431bb9df4a3f3e41590777529a55e25eb11eccafef50220511700aa1ea2c2cd499251f99014f22c5af63a00c76fd24da650014a0a8199e901210264187d9ee773aa333ac223678478b1df3ea268178fc9447e0a60c443eddaa749fdffffff01995d0000000000001976a914759d6677091e973b9e9d99f19c68fbf43e3f05f988acc8d30800'
+      const inputHash = 'd2d00ff71b1d1920d3a7717274b720f3d8230d9f38ec8f3e6867a207f2a40092'
+      const inputIndex = 0
+
+      const psbt = new Psbt()
+      psbt.addInput({hash: inputHash, index: inputIndex})
+      psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex'))
+
+      assert.throws(() => {
+        psbt.signInput(inputIndex, keyPair)
+      })
+    })
+
+    it('does not throw if non-witness UTXO hash matches the hash specified in the prevout', () => {
+      const inputUtxo = '01000000027dddcf79a2e541030bc753871d1c9d4dc163e4d6bd5aefae4bd84de64e16a652000000008b483045022100d3a2c3b58ae0f0b551711aa8949f478724428efa03f3179c3a50dc2c9ace46aa02201b2da84a21429a10af187731c882fc1f727e7b89573e07f0192e9e3de79fabf00141040e3a759c33b03e1af8e5d86fb447a40eff244c847a4f8274276db490054e8be076f8801ddc9c5246ee86b6f33cfe38e8b7e57ab9db390eb3ec1ec6ae9eeea113fdffffff4fef6d7f3c1e5d0bea733b2fd644fa456cdf73f21eb7e8866a2721d79266e9e8010000008a4730440220284a2989d45c48a6c8a556b30b3467eaf6abf866ec75c4d9f0e3872074f62c070220686ad82869c1669e6be162c4a34e4c617a971766f2b9688789eb2f498fe5eb6b0141040e3a759c33b03e1af8e5d86fb447a40eff244c847a4f8274276db490054e8be076f8801ddc9c5246ee86b6f33cfe38e8b7e57ab9db390eb3ec1ec6ae9eeea113fdffffff0224c70d00000000001976a914da6473ed373e08f46dd8003fca7ba72fbe9c555e88ac9cb00e00000000001976a91449707992598f85a31aa6715af70fe507610b6f8b88ac11c00800'
+      const inputHash = 'd2d00ff71b1d1920d3a7717274b720f3d8230d9f38ec8f3e6867a207f2a40092'
+      const inputIndex = 0
+
+      const psbt = new Psbt()
+      psbt.addInput({hash: inputHash, index: inputIndex})
+      psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex'))
+
+      assert.doesNotThrow(() => {
+        psbt.signInput(inputIndex, keyPair)
+      })
+    })
+  })
+})

From 5fd18d806f8dbce87b176cd2a3f869a82dc07990 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 27 Jun 2019 18:19:15 +0700
Subject: [PATCH 008/111] Check redeem script matches when signing PSBT input

---
 src/psbt.js    | 20 +++++++++++++++++---
 ts_src/psbt.ts | 23 ++++++++++++++++++++---
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 5d40ab4..6e72daa 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -1,6 +1,7 @@
 'use strict';
 Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
+const payments = require('./payments');
 const transaction_1 = require('./transaction');
 class Psbt extends bip174_1.Psbt {
   constructor() {
@@ -32,7 +33,6 @@ class Psbt extends bip174_1.Psbt {
     //     assert False
     const input = this.inputs[inputIndex];
     if (input === undefined) throw new Error(`No input #${inputIndex}`);
-    // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
     if (input.nonWitnessUtxo) {
       const unsignedTx = transaction_1.Transaction.fromBuffer(
         this.globalMap.unsignedTx,
@@ -40,13 +40,27 @@ class Psbt extends bip174_1.Psbt {
       const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
         input.nonWitnessUtxo,
       );
-      const inputHash = unsignedTx.ins[inputIndex].hash;
+      const prevoutHash = unsignedTx.ins[inputIndex].hash;
       const utxoHash = nonWitnessUtxoTx.getHash();
-      if (Buffer.compare(inputHash, utxoHash) !== 0) {
+      // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
+      if (Buffer.compare(prevoutHash, utxoHash) !== 0) {
         throw new Error(
           `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
         );
       }
+      if (input.redeemScript) {
+        const prevoutIndex = unsignedTx.ins[inputIndex].index;
+        const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
+        const redeemScriptOutput = payments.p2sh({
+          redeem: { output: input.redeemScript },
+        }).output;
+        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
+        if (Buffer.compare(prevout.script, redeemScriptOutput) !== 0) {
+          throw new Error(
+            `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+          );
+        }
+      }
     }
     // TODO: Get hash to sign
     const hash = Buffer.alloc(32);
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 9b0e9ce..8f40881 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,5 +1,6 @@
 import { Psbt as PsbtBase } from 'bip174';
 import { Signer } from './ecpair';
+import * as payments from './payments';
 import { Transaction } from './transaction';
 
 export class Psbt extends PsbtBase {
@@ -35,19 +36,35 @@ export class Psbt extends PsbtBase {
     const input = this.inputs[inputIndex];
     if (input === undefined) throw new Error(`No input #${inputIndex}`);
 
-    // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
     if (input.nonWitnessUtxo) {
       const unsignedTx = Transaction.fromBuffer(this.globalMap.unsignedTx!);
       const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
 
-      const inputHash = unsignedTx.ins[inputIndex].hash;
+      const prevoutHash = unsignedTx.ins[inputIndex].hash;
       const utxoHash = nonWitnessUtxoTx.getHash();
 
-      if (Buffer.compare(inputHash, utxoHash) !== 0) {
+      // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
+      if (Buffer.compare(prevoutHash, utxoHash) !== 0) {
         throw new Error(
           `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
         );
       }
+
+      if (input.redeemScript) {
+        const prevoutIndex = unsignedTx.ins[inputIndex].index;
+        const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
+
+        const redeemScriptOutput = payments.p2sh({
+          redeem: { output: input.redeemScript },
+        }).output as Buffer;
+
+        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
+        if (Buffer.compare(prevout.script, redeemScriptOutput) !== 0) {
+          throw new Error(
+            `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+          );
+        }
+      }
     }
 
     // TODO: Get hash to sign

From 64dc6543be759f36c0c14e9191213f04c6940b70 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 27 Jun 2019 18:20:19 +0700
Subject: [PATCH 009/111] Add simple tests for redeem script check

---
 test/psbt.js | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/test/psbt.js b/test/psbt.js
index 9422d6e..f814921 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -39,5 +39,37 @@ describe(`Psbt`, () => {
         psbt.signInput(inputIndex, keyPair)
       })
     })
+
+    it('throws if redeem script does not match the scriptPubKey in the prevout', () => {
+      const inputUtxo = '0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000'
+      const inputRedeemScript = '00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903'
+      const inputHash = '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858'
+      const inputIndex = 0
+
+      const psbt = new Psbt()
+      psbt.addInput({hash: inputHash, index: inputIndex})
+      psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex'))
+      psbt.addRedeemScriptToInput(inputIndex, Buffer.from(inputRedeemScript, 'hex'))
+
+      assert.throws(() => {
+        psbt.signInput(inputIndex, keyPair)
+      })
+    })
+
+    it('does not throw if redeem script matches the scriptPubKey in the prevout', () => {
+      const inputUtxo = '0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000'
+      const inputRedeemScript = '5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae'
+      const inputHash = '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858'
+      const inputIndex = 0
+
+      const psbt = new Psbt()
+      psbt.addInput({hash: inputHash, index: inputIndex})
+      psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex'))
+      psbt.addRedeemScriptToInput(inputIndex, Buffer.from(inputRedeemScript, 'hex'))
+
+      assert.doesNotThrow(() => {
+        psbt.signInput(inputIndex, keyPair)
+      })
+    })
   })
 })

From 1afac399b14a1d1e40974c5bee597c06687cfff6 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Fri, 28 Jun 2019 16:31:29 +0700
Subject: [PATCH 010/111] Update BIP174 package to fix inheritance issues

---
 package-lock.json |  6 +++---
 package.json      |  2 +-
 src/psbt.js       | 10 ++++++++--
 ts_src/psbt.ts    | 10 ++++++++--
 4 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index ac0507b..d409322 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -200,9 +200,9 @@
       }
     },
     "bip174": {
-      "version": "0.0.5",
-      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.5.tgz",
-      "integrity": "sha512-NNt0e9pz7h8EhC+pNAcB8G0Ca/Lei42YnAtPMewpcuLzRJGgaJO4vgtBpeQHH/f3fWlabZwSh/3tyEHwFNXlRw=="
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.8.tgz",
+      "integrity": "sha512-xWPzmlCvLoOWTlXk1wG7+TyOfaN8xX07IieuG4ug5su3igC9s4Lsdq+IEEMo+YHDQ4hPPAX9LYio6aEIAA+Zrg=="
     },
     "bip32": {
       "version": "2.0.3",
diff --git a/package.json b/package.json
index 6527baf..b964697 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
-    "bip174": "0.0.5",
+    "bip174": "0.0.8",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",
diff --git a/src/psbt.js b/src/psbt.js
index 6e72daa..9f29525 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -68,8 +68,14 @@ class Psbt extends bip174_1.Psbt {
       pubkey: keyPair.publicKey,
       signature: keyPair.sign(hash),
     };
-    this.addPartialSigToInput(inputIndex, partialSig);
-    return this;
+    // Just hardcode this for now to satisfy the stricter sig type checks
+    partialSig.signature = Buffer.from(
+      '304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6' +
+        '771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa9' +
+        '4b99bdf86151db9a9a01',
+      'hex',
+    );
+    return this.addPartialSigToInput(inputIndex, partialSig);
   }
 }
 exports.Psbt = Psbt;
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 8f40881..75a90d7 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -75,8 +75,14 @@ export class Psbt extends PsbtBase {
       signature: keyPair.sign(hash),
     };
 
-    this.addPartialSigToInput(inputIndex, partialSig);
+    // Just hardcode this for now to satisfy the stricter sig type checks
+    partialSig.signature = Buffer.from(
+      '304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6' +
+        '771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa9' +
+        '4b99bdf86151db9a9a01',
+      'hex',
+    );
 
-    return this;
+    return this.addPartialSigToInput(inputIndex, partialSig);
   }
 }

From 3a82486fb5667ee14bc6d830e745a1151163d202 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Fri, 28 Jun 2019 16:55:00 +0700
Subject: [PATCH 011/111] Loop over PSBT tests from fixtures

---
 test/fixtures/psbt.json | 30 +++++++++++++++++
 test/psbt.js            | 71 +++++++----------------------------------
 2 files changed, 42 insertions(+), 59 deletions(-)
 create mode 100644 test/fixtures/psbt.json

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
new file mode 100644
index 0000000..9268ed9
--- /dev/null
+++ b/test/fixtures/psbt.json
@@ -0,0 +1,30 @@
+{
+  "signInput": {
+    "checks": [
+      {
+        "description": "checks non-witness UTXO matches the hash specified in the prevout",
+        "shouldSign": {
+          "psbt": "cHNidP8BADMBAAAAAZIApPIHomdoPo/sOJ8NI9jzILd0cnGn0yAZHRv3D9DSAAAAAAD/////AAAAAAAAAQD9tQEBAAAAAn3dz3mi5UEDC8dThx0cnU3BY+TWvVrvrkvYTeZOFqZSAAAAAItIMEUCIQDTosO1iuDwtVFxGqiUn0eHJEKO+gPzF5w6UNwsms5GqgIgGy2oSiFCmhCvGHcxyIL8H3J+e4lXPgfwGS6ePeefq/ABQQQOOnWcM7A+Gvjl2G+0R6QO/yRMhHpPgnQnbbSQBU6L4Hb4gB3cnFJG7oa28zz+OOi35Xq52zkOs+wexq6e7qET/f///0/vbX88Hl0L6nM7L9ZE+kVs33PyHrfohmonIdeSZunoAQAAAIpHMEQCIChKKYnUXEimyKVWsws0Z+r2q/hm7HXE2fDjhyB09iwHAiBoatgoacFmnmvhYsSjTkxhepcXZvK5aIeJ6y9Jj+XrawFBBA46dZwzsD4a+OXYb7RHpA7/JEyEek+CdCdttJAFTovgdviAHdycUkbuhrbzPP446LflernbOQ6z7B7Grp7uoRP9////AiTHDQAAAAAAGXapFNpkc+03Pgj0bdgAP8p7py++nFVeiKycsA4AAAAAABl2qRRJcHmSWY+FoxqmcVr3D+UHYQtvi4isEcAIAAAA",
+          "inputToCheck": 0
+        },
+        "shouldThrow": {
+          "errorMessage": "Non-witness UTXO hash for input #0 doesn't match the hash specified in the prevout",
+          "psbt": "cHNidP8BADMBAAAAAZIApPIHomdoPo/sOJ8NI9jzILd0cnGn0yAZHRv3D9DSAAAAAAD/////AAAAAAAAAQC/AgAAAAHBYCumjIwkFFCni2Hb/eJymJGB0HU3secNMbfbk5VX8gAAAABqRzBEAiAphyuXV5hQyHZY5DG7nfSj8+QVkHd1KaVeJesR7Mr+9QIgURcAqh6iws1JklH5kBTyLFr2OgDHb9JNplABSgqBmekBIQJkGH2e53OqMzrCI2eEeLHfPqJoF4/JRH4KYMRD7dqnSf3///8BmV0AAAAAAAAZdqkUdZ1mdwkelzuenZnxnGj79D4/BfmIrMjTCAAAAA==",
+          "inputToCheck": 0
+        }
+      },
+      {
+        "description": "checks redeem script matches the scriptPubKey in the prevout",
+        "shouldSign": {
+          "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuAAA=",
+          "inputToCheck": 0
+        },
+        "shouldThrow": {
+          "errorMessage": "Redeem script for input #0 doesn't match the scriptPubKey in the prevout",
+          "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMAAA==",
+          "inputToCheck": 0
+        }
+      }
+    ]
+  }
+}
diff --git a/test/psbt.js b/test/psbt.js
index f814921..1d8ee90 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -4,71 +4,24 @@ const assert = require('assert')
 const ECPair = require('../src/ecpair')
 const Psbt = require('..').Psbt
 
-// For now, just hardcoding some test values is fine
-// const fixtures = require('./fixtures/psbt')
+const fixtures = require('./fixtures/psbt')
 
 describe(`Psbt`, () => {
   // constants
   const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex'))
 
   describe('signInput', () => {
-    it('throws if non-witness UTXO hash doesn\'t match the hash specified in the prevout', () => {
-      const inputUtxo = '0200000001c1602ba68c8c241450a78b61dbfde272989181d07537b1e70d31b7db939557f2000000006a473044022029872b97579850c87658e431bb9df4a3f3e41590777529a55e25eb11eccafef50220511700aa1ea2c2cd499251f99014f22c5af63a00c76fd24da650014a0a8199e901210264187d9ee773aa333ac223678478b1df3ea268178fc9447e0a60c443eddaa749fdffffff01995d0000000000001976a914759d6677091e973b9e9d99f19c68fbf43e3f05f988acc8d30800'
-      const inputHash = 'd2d00ff71b1d1920d3a7717274b720f3d8230d9f38ec8f3e6867a207f2a40092'
-      const inputIndex = 0
-
-      const psbt = new Psbt()
-      psbt.addInput({hash: inputHash, index: inputIndex})
-      psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex'))
-
-      assert.throws(() => {
-        psbt.signInput(inputIndex, keyPair)
-      })
-    })
-
-    it('does not throw if non-witness UTXO hash matches the hash specified in the prevout', () => {
-      const inputUtxo = '01000000027dddcf79a2e541030bc753871d1c9d4dc163e4d6bd5aefae4bd84de64e16a652000000008b483045022100d3a2c3b58ae0f0b551711aa8949f478724428efa03f3179c3a50dc2c9ace46aa02201b2da84a21429a10af187731c882fc1f727e7b89573e07f0192e9e3de79fabf00141040e3a759c33b03e1af8e5d86fb447a40eff244c847a4f8274276db490054e8be076f8801ddc9c5246ee86b6f33cfe38e8b7e57ab9db390eb3ec1ec6ae9eeea113fdffffff4fef6d7f3c1e5d0bea733b2fd644fa456cdf73f21eb7e8866a2721d79266e9e8010000008a4730440220284a2989d45c48a6c8a556b30b3467eaf6abf866ec75c4d9f0e3872074f62c070220686ad82869c1669e6be162c4a34e4c617a971766f2b9688789eb2f498fe5eb6b0141040e3a759c33b03e1af8e5d86fb447a40eff244c847a4f8274276db490054e8be076f8801ddc9c5246ee86b6f33cfe38e8b7e57ab9db390eb3ec1ec6ae9eeea113fdffffff0224c70d00000000001976a914da6473ed373e08f46dd8003fca7ba72fbe9c555e88ac9cb00e00000000001976a91449707992598f85a31aa6715af70fe507610b6f8b88ac11c00800'
-      const inputHash = 'd2d00ff71b1d1920d3a7717274b720f3d8230d9f38ec8f3e6867a207f2a40092'
-      const inputIndex = 0
-
-      const psbt = new Psbt()
-      psbt.addInput({hash: inputHash, index: inputIndex})
-      psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex'))
-
-      assert.doesNotThrow(() => {
-        psbt.signInput(inputIndex, keyPair)
-      })
-    })
-
-    it('throws if redeem script does not match the scriptPubKey in the prevout', () => {
-      const inputUtxo = '0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000'
-      const inputRedeemScript = '00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903'
-      const inputHash = '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858'
-      const inputIndex = 0
-
-      const psbt = new Psbt()
-      psbt.addInput({hash: inputHash, index: inputIndex})
-      psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex'))
-      psbt.addRedeemScriptToInput(inputIndex, Buffer.from(inputRedeemScript, 'hex'))
-
-      assert.throws(() => {
-        psbt.signInput(inputIndex, keyPair)
-      })
-    })
-
-    it('does not throw if redeem script matches the scriptPubKey in the prevout', () => {
-      const inputUtxo = '0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000'
-      const inputRedeemScript = '5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae'
-      const inputHash = '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858'
-      const inputIndex = 0
-
-      const psbt = new Psbt()
-      psbt.addInput({hash: inputHash, index: inputIndex})
-      psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex'))
-      psbt.addRedeemScriptToInput(inputIndex, Buffer.from(inputRedeemScript, 'hex'))
-
-      assert.doesNotThrow(() => {
-        psbt.signInput(inputIndex, keyPair)
+    fixtures.signInput.checks.forEach(f => {
+      it(f.description, () => {
+        const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+        assert.doesNotThrow(() => {
+          psbtThatShouldsign.signInput(f.shouldSign.inputToCheck, keyPair)
+        })
+    
+        const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+        assert.throws(() => {
+          psbtThatShouldThrow.signInput(f.shouldThrow.inputToCheck, keyPair)
+        }, {message: f.shouldThrow.errorMessage})
       })
     })
   })

From 6562ee96a48f49d80f4b66ade10b6b17c982f8c7 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Fri, 28 Jun 2019 17:17:18 +0700
Subject: [PATCH 012/111] Remove redundant import from test

---
 test/psbt.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/psbt.js b/test/psbt.js
index 1d8ee90..3bf873d 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -1,4 +1,4 @@
-const { describe, it, beforeEach } = require('mocha')
+const { describe, it } = require('mocha')
 const assert = require('assert')
 
 const ECPair = require('../src/ecpair')

From 08627e65a3faeade0b81ab2487379de773bdb6d0 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Fri, 28 Jun 2019 18:14:37 +0700
Subject: [PATCH 013/111] Check redeem script matches witness utxo when signing
 PSBT input

---
 src/psbt.js    | 14 ++++++++++++++
 ts_src/psbt.ts | 15 +++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/src/psbt.js b/src/psbt.js
index 9f29525..4dc72ff 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -61,6 +61,20 @@ class Psbt extends bip174_1.Psbt {
           );
         }
       }
+    } else if (input.witnessUtxo) {
+      if (input.redeemScript) {
+        const redeemScriptOutput = payments.p2sh({
+          redeem: { output: input.redeemScript },
+        }).output;
+        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
+        if (
+          Buffer.compare(input.witnessUtxo.script, redeemScriptOutput) !== 0
+        ) {
+          throw new Error(
+            `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+          );
+        }
+      }
     }
     // TODO: Get hash to sign
     const hash = Buffer.alloc(32);
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 75a90d7..ab1fd7b 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -65,6 +65,21 @@ export class Psbt extends PsbtBase {
           );
         }
       }
+    } else if (input.witnessUtxo) {
+      if (input.redeemScript) {
+        const redeemScriptOutput = payments.p2sh({
+          redeem: { output: input.redeemScript },
+        }).output as Buffer;
+
+        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
+        if (
+          Buffer.compare(input.witnessUtxo.script, redeemScriptOutput) !== 0
+        ) {
+          throw new Error(
+            `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+          );
+        }
+      }
     }
 
     // TODO: Get hash to sign

From 10b3aff4fde3ecd74bdad6a04e88dabdc151beac Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Fri, 28 Jun 2019 18:15:01 +0700
Subject: [PATCH 014/111] Test redeem script witness utxo check

---
 test/fixtures/psbt.json | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 9268ed9..1b72874 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -14,7 +14,7 @@
         }
       },
       {
-        "description": "checks redeem script matches the scriptPubKey in the prevout",
+        "description": "checks redeem script matches the scriptPubKey in a non-witness prevout",
         "shouldSign": {
           "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuAAA=",
           "inputToCheck": 0
@@ -24,6 +24,18 @@
           "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMAAA==",
           "inputToCheck": 0
         }
+      },
+      {
+        "description": "checks redeem script matches the scriptPubKey in a witness prevout",
+        "shouldSign": {
+          "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==",
+          "inputToCheck": 1
+        },
+        "shouldThrow": {
+          "errorMessage": "Redeem script for input #1 doesn't match the scriptPubKey in the prevout",
+          "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==",
+          "inputToCheck": 1
+        }
       }
     ]
   }

From 95b4a2806d6d433bd0d766d0be7c0183d28541cd Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Fri, 28 Jun 2019 18:21:32 +0700
Subject: [PATCH 015/111] Improve code re-use for redeem script checks

---
 src/psbt.js    | 37 +++++++++++++++++--------------------
 ts_src/psbt.ts | 46 +++++++++++++++++++++++-----------------------
 2 files changed, 40 insertions(+), 43 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 4dc72ff..c68afb0 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -3,6 +3,17 @@ Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
 const payments = require('./payments');
 const transaction_1 = require('./transaction');
+const checkRedeemScript = (inputIndex, scriptPubKey, redeemScript) => {
+  const redeemScriptOutput = payments.p2sh({
+    redeem: { output: redeemScript },
+  }).output;
+  // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
+  if (Buffer.compare(scriptPubKey, redeemScriptOutput) !== 0) {
+    throw new Error(
+      `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+    );
+  }
+};
 class Psbt extends bip174_1.Psbt {
   constructor() {
     super();
@@ -51,29 +62,15 @@ class Psbt extends bip174_1.Psbt {
       if (input.redeemScript) {
         const prevoutIndex = unsignedTx.ins[inputIndex].index;
         const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
-        const redeemScriptOutput = payments.p2sh({
-          redeem: { output: input.redeemScript },
-        }).output;
-        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
-        if (Buffer.compare(prevout.script, redeemScriptOutput) !== 0) {
-          throw new Error(
-            `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
-          );
-        }
+        checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
       }
     } else if (input.witnessUtxo) {
       if (input.redeemScript) {
-        const redeemScriptOutput = payments.p2sh({
-          redeem: { output: input.redeemScript },
-        }).output;
-        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
-        if (
-          Buffer.compare(input.witnessUtxo.script, redeemScriptOutput) !== 0
-        ) {
-          throw new Error(
-            `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
-          );
-        }
+        checkRedeemScript(
+          inputIndex,
+          input.witnessUtxo.script,
+          input.redeemScript,
+        );
       }
     }
     // TODO: Get hash to sign
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index ab1fd7b..70e04c9 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -3,6 +3,23 @@ import { Signer } from './ecpair';
 import * as payments from './payments';
 import { Transaction } from './transaction';
 
+const checkRedeemScript = (
+  inputIndex: number,
+  scriptPubKey: Buffer,
+  redeemScript: Buffer,
+): void => {
+  const redeemScriptOutput = payments.p2sh({
+    redeem: { output: redeemScript },
+  }).output as Buffer;
+
+  // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
+  if (Buffer.compare(scriptPubKey, redeemScriptOutput) !== 0) {
+    throw new Error(
+      `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+    );
+  }
+};
+
 export class Psbt extends PsbtBase {
   constructor() {
     super();
@@ -53,32 +70,15 @@ export class Psbt extends PsbtBase {
       if (input.redeemScript) {
         const prevoutIndex = unsignedTx.ins[inputIndex].index;
         const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
-
-        const redeemScriptOutput = payments.p2sh({
-          redeem: { output: input.redeemScript },
-        }).output as Buffer;
-
-        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
-        if (Buffer.compare(prevout.script, redeemScriptOutput) !== 0) {
-          throw new Error(
-            `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
-          );
-        }
+        checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
       }
     } else if (input.witnessUtxo) {
       if (input.redeemScript) {
-        const redeemScriptOutput = payments.p2sh({
-          redeem: { output: input.redeemScript },
-        }).output as Buffer;
-
-        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
-        if (
-          Buffer.compare(input.witnessUtxo.script, redeemScriptOutput) !== 0
-        ) {
-          throw new Error(
-            `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
-          );
-        }
+        checkRedeemScript(
+          inputIndex,
+          input.witnessUtxo.script,
+          input.redeemScript,
+        );
       }
     }
 

From f961724c73b97e1d503d5311a64d8d228e1476bd Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Fri, 28 Jun 2019 18:26:42 +0700
Subject: [PATCH 016/111] Prefer buf1.equals(buf2) over Buffer.compare(buf1,
 buf2) !== 0

---
 src/psbt.js    | 4 ++--
 ts_src/psbt.ts | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index c68afb0..55e6ac9 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -8,7 +8,7 @@ const checkRedeemScript = (inputIndex, scriptPubKey, redeemScript) => {
     redeem: { output: redeemScript },
   }).output;
   // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
-  if (Buffer.compare(scriptPubKey, redeemScriptOutput) !== 0) {
+  if (!scriptPubKey.equals(redeemScriptOutput)) {
     throw new Error(
       `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
     );
@@ -54,7 +54,7 @@ class Psbt extends bip174_1.Psbt {
       const prevoutHash = unsignedTx.ins[inputIndex].hash;
       const utxoHash = nonWitnessUtxoTx.getHash();
       // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
-      if (Buffer.compare(prevoutHash, utxoHash) !== 0) {
+      if (!prevoutHash.equals(utxoHash)) {
         throw new Error(
           `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
         );
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 70e04c9..7e5d13c 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -13,7 +13,7 @@ const checkRedeemScript = (
   }).output as Buffer;
 
   // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
-  if (Buffer.compare(scriptPubKey, redeemScriptOutput) !== 0) {
+  if (!scriptPubKey.equals(redeemScriptOutput)) {
     throw new Error(
       `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
     );
@@ -61,7 +61,7 @@ export class Psbt extends PsbtBase {
       const utxoHash = nonWitnessUtxoTx.getHash();
 
       // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
-      if (Buffer.compare(prevoutHash, utxoHash) !== 0) {
+      if (!prevoutHash.equals(utxoHash)) {
         throw new Error(
           `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
         );

From 18e7c9de80db6f5d335574f33ef39dedab82d0e3 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Fri, 28 Jun 2019 18:28:28 +0700
Subject: [PATCH 017/111] Move comments to main check logic

---
 src/psbt.js    | 3 ++-
 ts_src/psbt.ts | 4 +++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 55e6ac9..2513fb9 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -7,7 +7,6 @@ const checkRedeemScript = (inputIndex, scriptPubKey, redeemScript) => {
   const redeemScriptOutput = payments.p2sh({
     redeem: { output: redeemScript },
   }).output;
-  // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
   if (!scriptPubKey.equals(redeemScriptOutput)) {
     throw new Error(
       `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
@@ -62,10 +61,12 @@ class Psbt extends bip174_1.Psbt {
       if (input.redeemScript) {
         const prevoutIndex = unsignedTx.ins[inputIndex].index;
         const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
+        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
         checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
       }
     } else if (input.witnessUtxo) {
       if (input.redeemScript) {
+        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
         checkRedeemScript(
           inputIndex,
           input.witnessUtxo.script,
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 7e5d13c..167f15f 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -12,7 +12,6 @@ const checkRedeemScript = (
     redeem: { output: redeemScript },
   }).output as Buffer;
 
-  // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
   if (!scriptPubKey.equals(redeemScriptOutput)) {
     throw new Error(
       `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
@@ -70,10 +69,13 @@ export class Psbt extends PsbtBase {
       if (input.redeemScript) {
         const prevoutIndex = unsignedTx.ins[inputIndex].index;
         const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
+
+        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
         checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
       }
     } else if (input.witnessUtxo) {
       if (input.redeemScript) {
+        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
         checkRedeemScript(
           inputIndex,
           input.witnessUtxo.script,

From 667ffb58eb14aafef64b1816f9bf3e2adf610c50 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Mon, 1 Jul 2019 18:01:46 +0900
Subject: [PATCH 018/111] Use signature encode

---
 src/psbt.js    | 13 +++++--------
 ts_src/psbt.ts | 14 +++++---------
 2 files changed, 10 insertions(+), 17 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 2513fb9..7bfa439 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -2,6 +2,7 @@
 Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
 const payments = require('./payments');
+const script = require('./script');
 const transaction_1 = require('./transaction');
 const checkRedeemScript = (inputIndex, scriptPubKey, redeemScript) => {
   const redeemScriptOutput = payments.p2sh({
@@ -78,15 +79,11 @@ class Psbt extends bip174_1.Psbt {
     const hash = Buffer.alloc(32);
     const partialSig = {
       pubkey: keyPair.publicKey,
-      signature: keyPair.sign(hash),
+      signature: script.signature.encode(
+        keyPair.sign(hash),
+        input.sighashType || 0x01,
+      ),
     };
-    // Just hardcode this for now to satisfy the stricter sig type checks
-    partialSig.signature = Buffer.from(
-      '304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6' +
-        '771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa9' +
-        '4b99bdf86151db9a9a01',
-      'hex',
-    );
     return this.addPartialSigToInput(inputIndex, partialSig);
   }
 }
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 167f15f..63bc057 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,6 +1,7 @@
 import { Psbt as PsbtBase } from 'bip174';
 import { Signer } from './ecpair';
 import * as payments from './payments';
+import * as script from './script';
 import { Transaction } from './transaction';
 
 const checkRedeemScript = (
@@ -89,17 +90,12 @@ export class Psbt extends PsbtBase {
 
     const partialSig = {
       pubkey: keyPair.publicKey,
-      signature: keyPair.sign(hash),
+      signature: script.signature.encode(
+        keyPair.sign(hash),
+        input.sighashType || 0x01,
+      ),
     };
 
-    // Just hardcode this for now to satisfy the stricter sig type checks
-    partialSig.signature = Buffer.from(
-      '304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6' +
-        '771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa9' +
-        '4b99bdf86151db9a9a01',
-      'hex',
-    );
-
     return this.addPartialSigToInput(inputIndex, partialSig);
   }
 }

From f87b66eb24850fbca2a8626e8473d4ece0b22ae1 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Mon, 1 Jul 2019 18:55:18 +0900
Subject: [PATCH 019/111] Finish sign

---
 src/psbt.js    | 81 +++++++++++++++++++++++++++++++++++++++---------
 ts_src/psbt.ts | 84 +++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 135 insertions(+), 30 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 7bfa439..9a9ad7e 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -2,18 +2,35 @@
 Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
 const payments = require('./payments');
-const script = require('./script');
+const bscript = require('./script');
 const transaction_1 = require('./transaction');
-const checkRedeemScript = (inputIndex, scriptPubKey, redeemScript) => {
-  const redeemScriptOutput = payments.p2sh({
+const scriptCheckerFactory = (payment, paymentScriptName) => (
+  inputIndex,
+  scriptPubKey,
+  redeemScript,
+) => {
+  const redeemScriptOutput = payment({
     redeem: { output: redeemScript },
   }).output;
   if (!scriptPubKey.equals(redeemScriptOutput)) {
     throw new Error(
-      `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+      `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
     );
   }
 };
+const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
+const checkWitnessScript = scriptCheckerFactory(
+  payments.p2wsh,
+  'Witness script',
+);
+const isP2WPKH = script => {
+  try {
+    payments.p2wpkh({ output: script });
+    return true;
+  } catch (err) {
+    return false;
+  }
+};
 class Psbt extends bip174_1.Psbt {
   constructor() {
     super();
@@ -44,10 +61,12 @@ class Psbt extends bip174_1.Psbt {
     //     assert False
     const input = this.inputs[inputIndex];
     if (input === undefined) throw new Error(`No input #${inputIndex}`);
+    const unsignedTx = transaction_1.Transaction.fromBuffer(
+      this.globalMap.unsignedTx,
+    );
+    const sighashType = input.sighashType || 0x01;
+    let hash;
     if (input.nonWitnessUtxo) {
-      const unsignedTx = transaction_1.Transaction.fromBuffer(
-        this.globalMap.unsignedTx,
-      );
       const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
         input.nonWitnessUtxo,
       );
@@ -59,13 +78,25 @@ class Psbt extends bip174_1.Psbt {
           `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
         );
       }
+      const prevoutIndex = unsignedTx.ins[inputIndex].index;
+      const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
       if (input.redeemScript) {
-        const prevoutIndex = unsignedTx.ins[inputIndex].index;
-        const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
         // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
         checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
+        hash = unsignedTx.hashForSignature(
+          inputIndex,
+          input.redeemScript,
+          sighashType,
+        );
+      } else {
+        hash = unsignedTx.hashForSignature(
+          inputIndex,
+          prevout.script,
+          sighashType,
+        );
       }
     } else if (input.witnessUtxo) {
+      let script;
       if (input.redeemScript) {
         // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
         checkRedeemScript(
@@ -73,16 +104,36 @@ class Psbt extends bip174_1.Psbt {
           input.witnessUtxo.script,
           input.redeemScript,
         );
+        script = input.redeemScript;
+      } else {
+        script = input.witnessUtxo.script;
       }
+      if (isP2WPKH(script)) {
+        // P2WPKH uses the P2PKH template for prevoutScript when signing
+        const signingScript = payments.p2pkh({ hash: script.slice(2) }).output;
+        hash = unsignedTx.hashForWitnessV0(
+          inputIndex,
+          signingScript,
+          input.witnessUtxo.value,
+          sighashType,
+        );
+      } else {
+        if (!input.witnessScript)
+          throw new Error('Segwit input needs witnessScript if not P2WPKH');
+        checkWitnessScript(inputIndex, script, input.witnessScript);
+        hash = unsignedTx.hashForWitnessV0(
+          inputIndex,
+          script,
+          input.witnessUtxo.value,
+          sighashType,
+        );
+      }
+    } else {
+      throw new Error('Need a Utxo input item for signing');
     }
-    // TODO: Get hash to sign
-    const hash = Buffer.alloc(32);
     const partialSig = {
       pubkey: keyPair.publicKey,
-      signature: script.signature.encode(
-        keyPair.sign(hash),
-        input.sighashType || 0x01,
-      ),
+      signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
     };
     return this.addPartialSigToInput(inputIndex, partialSig);
   }
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 63bc057..5ec9ef1 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,25 +1,45 @@
 import { Psbt as PsbtBase } from 'bip174';
 import { Signer } from './ecpair';
 import * as payments from './payments';
-import * as script from './script';
+import * as bscript from './script';
 import { Transaction } from './transaction';
 
-const checkRedeemScript = (
+type ScriptCheckerFunction = (idx: number, spk: Buffer, rs: Buffer) => void;
+
+const scriptCheckerFactory = (
+  payment: any,
+  paymentScriptName: string,
+): ScriptCheckerFunction => (
   inputIndex: number,
   scriptPubKey: Buffer,
   redeemScript: Buffer,
 ): void => {
-  const redeemScriptOutput = payments.p2sh({
+  const redeemScriptOutput = payment({
     redeem: { output: redeemScript },
   }).output as Buffer;
 
   if (!scriptPubKey.equals(redeemScriptOutput)) {
     throw new Error(
-      `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+      `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
     );
   }
 };
 
+const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
+const checkWitnessScript = scriptCheckerFactory(
+  payments.p2wsh,
+  'Witness script',
+);
+
+const isP2WPKH = (script: Buffer): boolean => {
+  try {
+    payments.p2wpkh({ output: script });
+    return true;
+  } catch (err) {
+    return false;
+  }
+};
+
 export class Psbt extends PsbtBase {
   constructor() {
     super();
@@ -53,8 +73,11 @@ export class Psbt extends PsbtBase {
     const input = this.inputs[inputIndex];
     if (input === undefined) throw new Error(`No input #${inputIndex}`);
 
+    const unsignedTx = Transaction.fromBuffer(this.globalMap.unsignedTx!);
+    const sighashType = input.sighashType || 0x01;
+    let hash: Buffer;
+
     if (input.nonWitnessUtxo) {
-      const unsignedTx = Transaction.fromBuffer(this.globalMap.unsignedTx!);
       const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
 
       const prevoutHash = unsignedTx.ins[inputIndex].hash;
@@ -67,14 +90,26 @@ export class Psbt extends PsbtBase {
         );
       }
 
-      if (input.redeemScript) {
-        const prevoutIndex = unsignedTx.ins[inputIndex].index;
-        const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
+      const prevoutIndex = unsignedTx.ins[inputIndex].index;
+      const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
 
+      if (input.redeemScript) {
         // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
         checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
+        hash = unsignedTx.hashForSignature(
+          inputIndex,
+          input.redeemScript,
+          sighashType,
+        );
+      } else {
+        hash = unsignedTx.hashForSignature(
+          inputIndex,
+          prevout.script,
+          sighashType,
+        );
       }
     } else if (input.witnessUtxo) {
+      let script: Buffer;
       if (input.redeemScript) {
         // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
         checkRedeemScript(
@@ -82,18 +117,37 @@ export class Psbt extends PsbtBase {
           input.witnessUtxo.script,
           input.redeemScript,
         );
+        script = input.redeemScript;
+      } else {
+        script = input.witnessUtxo.script;
       }
+      if (isP2WPKH(script)) {
+        // P2WPKH uses the P2PKH template for prevoutScript when signing
+        const signingScript = payments.p2pkh({ hash: script.slice(2) }).output!;
+        hash = unsignedTx.hashForWitnessV0(
+          inputIndex,
+          signingScript,
+          input.witnessUtxo.value,
+          sighashType,
+        );
+      } else {
+        if (!input.witnessScript)
+          throw new Error('Segwit input needs witnessScript if not P2WPKH');
+        checkWitnessScript(inputIndex, script, input.witnessScript);
+        hash = unsignedTx.hashForWitnessV0(
+          inputIndex,
+          script,
+          input.witnessUtxo.value,
+          sighashType,
+        );
+      }
+    } else {
+      throw new Error('Need a Utxo input item for signing');
     }
 
-    // TODO: Get hash to sign
-    const hash = Buffer.alloc(32);
-
     const partialSig = {
       pubkey: keyPair.publicKey,
-      signature: script.signature.encode(
-        keyPair.sign(hash),
-        input.sighashType || 0x01,
-      ),
+      signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
     };
 
     return this.addPartialSigToInput(inputIndex, partialSig);

From f72c915ff150188575c0766ee4ab6dbc622bfed0 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Mon, 1 Jul 2019 19:57:35 +0900
Subject: [PATCH 020/111] Start towards finalizing inputs

---
 package-lock.json |  6 ++---
 package.json      |  2 +-
 src/psbt.js       | 60 ++++++++++++++++++++++++++++++++++++++++---
 ts_src/psbt.ts    | 65 ++++++++++++++++++++++++++++++++++++++++++++---
 types/psbt.d.ts   |  5 +++-
 5 files changed, 125 insertions(+), 13 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index d409322..4195bf2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -200,9 +200,9 @@
       }
     },
     "bip174": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.8.tgz",
-      "integrity": "sha512-xWPzmlCvLoOWTlXk1wG7+TyOfaN8xX07IieuG4ug5su3igC9s4Lsdq+IEEMo+YHDQ4hPPAX9LYio6aEIAA+Zrg=="
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.10.tgz",
+      "integrity": "sha512-gFtSEMayg7HPKGnIQcEx5CqD/qHWuMlxLJ/+VV4k4Q2mcA0rY040JbNpFuCGVI6rJYv211f0NA7nkU4xkPX4nQ=="
     },
     "bip32": {
       "version": "2.0.3",
diff --git a/package.json b/package.json
index b964697..1346928 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
-    "bip174": "0.0.8",
+    "bip174": "0.0.10",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",
diff --git a/src/psbt.js b/src/psbt.js
index 9a9ad7e..9d00e41 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -1,6 +1,8 @@
 'use strict';
 Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
+const utils_1 = require('bip174/src/lib/utils');
+const classify = require('./classify');
 const payments = require('./payments');
 const bscript = require('./script');
 const transaction_1 = require('./transaction');
@@ -23,17 +25,67 @@ const checkWitnessScript = scriptCheckerFactory(
   payments.p2wsh,
   'Witness script',
 );
-const isP2WPKH = script => {
+const isPayment = (script, payment) => {
   try {
-    payments.p2wpkh({ output: script });
+    payment({ output: script });
     return true;
   } catch (err) {
     return false;
   }
 };
+function getScriptFromInput(inputIndex, input, _unsignedTx) {
+  let script;
+  if (input.nonWitnessUtxo) {
+    if (input.redeemScript) {
+      script = input.redeemScript;
+    } else {
+      const unsignedTx = transaction_1.Transaction.fromBuffer(_unsignedTx);
+      const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
+        input.nonWitnessUtxo,
+      );
+      const prevoutIndex = unsignedTx.ins[inputIndex].index;
+      script = nonWitnessUtxoTx.outs[prevoutIndex].script;
+    }
+  } else if (input.witnessUtxo) {
+    if (input.witnessScript) {
+      script = input.witnessScript;
+    } else if (input.redeemScript) {
+      script = payments.p2pkh({ hash: input.redeemScript.slice(2) }).output;
+    } else {
+      script = payments.p2pkh({ hash: input.witnessUtxo.script.slice(2) })
+        .output;
+    }
+  } else {
+    return;
+  }
+  return script;
+}
 class Psbt extends bip174_1.Psbt {
-  constructor() {
+  constructor(network) {
     super();
+    this.network = network;
+  }
+  canFinalize(inputIndex) {
+    const input = utils_1.checkForInput(this.inputs, inputIndex);
+    const script = getScriptFromInput(
+      inputIndex,
+      input,
+      this.globalMap.unsignedTx,
+    );
+    if (!script) return false;
+    const scriptType = classify.output(script);
+    switch (scriptType) {
+      case 'pubkey':
+        return false;
+      case 'pubkeyhash':
+        return false;
+      case 'multisig':
+        return false;
+      case 'witnesspubkeyhash':
+        return false;
+      default:
+        return false;
+    }
   }
   signInput(inputIndex, keyPair) {
     // TODO: Implement BIP174 pre-sign checks:
@@ -108,7 +160,7 @@ class Psbt extends bip174_1.Psbt {
       } else {
         script = input.witnessUtxo.script;
       }
-      if (isP2WPKH(script)) {
+      if (isPayment(script, payments.p2wpkh)) {
         // P2WPKH uses the P2PKH template for prevoutScript when signing
         const signingScript = payments.p2pkh({ hash: script.slice(2) }).output;
         hash = unsignedTx.hashForWitnessV0(
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 5ec9ef1..2f0b6ea 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,5 +1,9 @@
 import { Psbt as PsbtBase } from 'bip174';
+import { PsbtInput } from 'bip174/src/lib/interfaces';
+import { checkForInput } from 'bip174/src/lib/utils';
+import * as classify from './classify';
 import { Signer } from './ecpair';
+import { Network } from './networks';
 import * as payments from './payments';
 import * as bscript from './script';
 import { Transaction } from './transaction';
@@ -31,20 +35,73 @@ const checkWitnessScript = scriptCheckerFactory(
   'Witness script',
 );
 
-const isP2WPKH = (script: Buffer): boolean => {
+const isPayment = (script: Buffer, payment: any): boolean => {
   try {
-    payments.p2wpkh({ output: script });
+    payment({ output: script });
     return true;
   } catch (err) {
     return false;
   }
 };
 
+function getScriptFromInput(
+  inputIndex: number,
+  input: PsbtInput,
+  _unsignedTx: Buffer,
+): Buffer | undefined {
+  let script: Buffer;
+  if (input.nonWitnessUtxo) {
+    if (input.redeemScript) {
+      script = input.redeemScript;
+    } else {
+      const unsignedTx = Transaction.fromBuffer(_unsignedTx);
+      const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
+      const prevoutIndex = unsignedTx.ins[inputIndex].index;
+      script = nonWitnessUtxoTx.outs[prevoutIndex].script;
+    }
+  } else if (input.witnessUtxo) {
+    if (input.witnessScript) {
+      script = input.witnessScript;
+    } else if (input.redeemScript) {
+      script = payments.p2pkh({ hash: input.redeemScript.slice(2) }).output!;
+    } else {
+      script = payments.p2pkh({ hash: input.witnessUtxo.script.slice(2) })
+        .output!;
+    }
+  } else {
+    return;
+  }
+  return script;
+}
+
 export class Psbt extends PsbtBase {
-  constructor() {
+  constructor(public network?: Network) {
     super();
   }
 
+  canFinalize(inputIndex: number): boolean {
+    const input = checkForInput(this.inputs, inputIndex);
+    const script = getScriptFromInput(
+      inputIndex,
+      input,
+      this.globalMap.unsignedTx!,
+    );
+    if (!script) return false;
+    const scriptType = classify.output(script);
+    switch (scriptType) {
+      case 'pubkey':
+        return false;
+      case 'pubkeyhash':
+        return false;
+      case 'multisig':
+        return false;
+      case 'witnesspubkeyhash':
+        return false;
+      default:
+        return false;
+    }
+  }
+
   signInput(inputIndex: number, keyPair: Signer): Psbt {
     // TODO: Implement BIP174 pre-sign checks:
     // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
@@ -121,7 +178,7 @@ export class Psbt extends PsbtBase {
       } else {
         script = input.witnessUtxo.script;
       }
-      if (isP2WPKH(script)) {
+      if (isPayment(script, payments.p2wpkh)) {
         // P2WPKH uses the P2PKH template for prevoutScript when signing
         const signingScript = payments.p2pkh({ hash: script.slice(2) }).output!;
         hash = unsignedTx.hashForWitnessV0(
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index a58b982..fda7e6b 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -1,6 +1,9 @@
 import { Psbt as PsbtBase } from 'bip174';
 import { Signer } from './ecpair';
+import { Network } from './networks';
 export declare class Psbt extends PsbtBase {
-    constructor();
+    network?: Network | undefined;
+    constructor(network?: Network | undefined);
+    canFinalize(inputIndex: number): boolean;
     signInput(inputIndex: number, keyPair: Signer): Psbt;
 }

From f28e9cef71d457366e97423ca254e8c6da28dcda Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 2 Jul 2019 15:03:24 +0900
Subject: [PATCH 021/111] Refactor

- Clean up sign
- Get the meaningful script
- Search for pubkey and prevent sign if can't find self
- Tests failed, so comment out for now
---
 src/psbt.js    | 289 +++++++++++++++++++++++---------------------
 ts_src/psbt.ts | 318 ++++++++++++++++++++++++++++---------------------
 2 files changed, 335 insertions(+), 272 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 9d00e41..fa4ba9f 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -2,10 +2,153 @@
 Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
 const utils_1 = require('bip174/src/lib/utils');
-const classify = require('./classify');
 const payments = require('./payments');
 const bscript = require('./script');
 const transaction_1 = require('./transaction');
+class Psbt extends bip174_1.Psbt {
+  constructor(network) {
+    super();
+    this.network = network;
+  }
+  canFinalize(inputIndex) {
+    const input = utils_1.checkForInput(this.inputs, inputIndex);
+    const script = getScriptFromInput(
+      inputIndex,
+      input,
+      this.globalMap.unsignedTx,
+    );
+    if (!script) return false;
+    const scriptType = classifyScript(script);
+    // TODO: for each type
+    switch (scriptType) {
+      case 'pubkey':
+        return false;
+      case 'pubkeyhash':
+        return false;
+      case 'multisig':
+        return false;
+      case 'witnesspubkeyhash':
+        return false;
+      default:
+        return false;
+    }
+  }
+  signInput(inputIndex, keyPair) {
+    const input = this.inputs[inputIndex];
+    if (input === undefined) throw new Error(`No input #${inputIndex}`);
+    const { hash, sighashType } = getHashForSig(
+      inputIndex,
+      input,
+      this.globalMap.unsignedTx,
+    );
+    const pubkey = keyPair.publicKey;
+    // // TODO: throw error when the pubkey or pubkey hash is not found anywhere
+    // // in the script
+    // const pubkeyHash = hash160(keyPair.publicKey);
+    //
+    // const decompiled = bscript.decompile(script);
+    // if (decompiled === null) throw new Error('Unknown script error');
+    //
+    // const hasKey = decompiled.some(element => {
+    //   if (typeof element === 'number') return false;
+    //   return element.equals(pubkey) || element.equals(pubkeyHash);
+    // });
+    //
+    // if (!hasKey) {
+    //   throw new Error(
+    //     `Can not sign for this input with the key ${pubkey.toString('hex')}`,
+    //   );
+    // }
+    const partialSig = {
+      pubkey,
+      signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
+    };
+    return this.addPartialSigToInput(inputIndex, partialSig);
+  }
+}
+exports.Psbt = Psbt;
+const getHashForSig = (inputIndex, input, txBuf) => {
+  const unsignedTx = transaction_1.Transaction.fromBuffer(txBuf);
+  const sighashType =
+    input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
+  let hash;
+  let script;
+  if (input.nonWitnessUtxo) {
+    const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
+      input.nonWitnessUtxo,
+    );
+    const prevoutHash = unsignedTx.ins[inputIndex].hash;
+    const utxoHash = nonWitnessUtxoTx.getHash();
+    // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
+    if (!prevoutHash.equals(utxoHash)) {
+      throw new Error(
+        `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
+      );
+    }
+    const prevoutIndex = unsignedTx.ins[inputIndex].index;
+    const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
+    if (input.redeemScript) {
+      // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
+      checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
+      script = input.redeemScript;
+      hash = unsignedTx.hashForSignature(
+        inputIndex,
+        input.redeemScript,
+        sighashType,
+      );
+    } else {
+      script = prevout.script;
+      hash = unsignedTx.hashForSignature(
+        inputIndex,
+        prevout.script,
+        sighashType,
+      );
+    }
+  } else if (input.witnessUtxo) {
+    let _script; // so we don't shadow the `let script` above
+    if (input.redeemScript) {
+      // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
+      checkRedeemScript(
+        inputIndex,
+        input.witnessUtxo.script,
+        input.redeemScript,
+      );
+      _script = input.redeemScript;
+    } else {
+      _script = input.witnessUtxo.script;
+    }
+    if (isP2WPKH(_script)) {
+      // P2WPKH uses the P2PKH template for prevoutScript when signing
+      const signingScript = payments.p2pkh({ hash: _script.slice(2) }).output;
+      hash = unsignedTx.hashForWitnessV0(
+        inputIndex,
+        signingScript,
+        input.witnessUtxo.value,
+        sighashType,
+      );
+      script = _script;
+    } else {
+      if (!input.witnessScript)
+        throw new Error('Segwit input needs witnessScript if not P2WPKH');
+      checkWitnessScript(inputIndex, _script, input.witnessScript);
+      hash = unsignedTx.hashForWitnessV0(
+        inputIndex,
+        _script,
+        input.witnessUtxo.value,
+        sighashType,
+      );
+      // want to make sure the script we return is the actual meaningful script
+      script = input.witnessScript;
+    }
+  } else {
+    throw new Error('Need a Utxo input item for signing');
+  }
+  return {
+    script,
+    sighashType,
+    hash,
+  };
+};
 const scriptCheckerFactory = (payment, paymentScriptName) => (
   inputIndex,
   scriptPubKey,
@@ -25,7 +168,7 @@ const checkWitnessScript = scriptCheckerFactory(
   payments.p2wsh,
   'Witness script',
 );
-const isPayment = (script, payment) => {
+const isPaymentFactory = payment => script => {
   try {
     payment({ output: script });
     return true;
@@ -33,6 +176,17 @@ const isPayment = (script, payment) => {
     return false;
   }
 };
+const isP2WPKH = isPaymentFactory(payments.p2wpkh);
+const isP2PKH = isPaymentFactory(payments.p2pkh);
+const isP2MS = isPaymentFactory(payments.p2ms);
+const isP2PK = isPaymentFactory(payments.p2pk);
+const classifyScript = script => {
+  if (isP2WPKH(script)) return 'witnesspubkeyhash';
+  if (isP2PKH(script)) return 'pubkeyhash';
+  if (isP2MS(script)) return 'multisig';
+  if (isP2PK(script)) return 'pubkey';
+  return 'nonstandard';
+};
 function getScriptFromInput(inputIndex, input, _unsignedTx) {
   let script;
   if (input.nonWitnessUtxo) {
@@ -60,134 +214,3 @@ function getScriptFromInput(inputIndex, input, _unsignedTx) {
   }
   return script;
 }
-class Psbt extends bip174_1.Psbt {
-  constructor(network) {
-    super();
-    this.network = network;
-  }
-  canFinalize(inputIndex) {
-    const input = utils_1.checkForInput(this.inputs, inputIndex);
-    const script = getScriptFromInput(
-      inputIndex,
-      input,
-      this.globalMap.unsignedTx,
-    );
-    if (!script) return false;
-    const scriptType = classify.output(script);
-    switch (scriptType) {
-      case 'pubkey':
-        return false;
-      case 'pubkeyhash':
-        return false;
-      case 'multisig':
-        return false;
-      case 'witnesspubkeyhash':
-        return false;
-      default:
-        return false;
-    }
-  }
-  signInput(inputIndex, keyPair) {
-    // TODO: Implement BIP174 pre-sign checks:
-    // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
-    //
-    // if non_witness_utxo.exists:
-    //     assert(sha256d(non_witness_utxo) == psbt.tx.innput[i].prevout.hash)
-    //     if redeemScript.exists:
-    //         assert(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey == P2SH(redeemScript))
-    //         sign_non_witness(redeemScript)
-    //     else:
-    //         sign_non_witness(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey)
-    // else if witness_utxo.exists:
-    //     if redeemScript.exists:
-    //         assert(witness_utxo.scriptPubKey == P2SH(redeemScript))
-    //         script = redeemScript
-    //     else:
-    //         script = witness_utxo.scriptPubKey
-    //     if IsP2WPKH(script):
-    //         sign_witness(P2PKH(script[2:22]))
-    //     else if IsP2WSH(script):
-    //         assert(script == P2WSH(witnessScript))
-    //         sign_witness(witnessScript)
-    // else:
-    //     assert False
-    const input = this.inputs[inputIndex];
-    if (input === undefined) throw new Error(`No input #${inputIndex}`);
-    const unsignedTx = transaction_1.Transaction.fromBuffer(
-      this.globalMap.unsignedTx,
-    );
-    const sighashType = input.sighashType || 0x01;
-    let hash;
-    if (input.nonWitnessUtxo) {
-      const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
-        input.nonWitnessUtxo,
-      );
-      const prevoutHash = unsignedTx.ins[inputIndex].hash;
-      const utxoHash = nonWitnessUtxoTx.getHash();
-      // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
-      if (!prevoutHash.equals(utxoHash)) {
-        throw new Error(
-          `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
-        );
-      }
-      const prevoutIndex = unsignedTx.ins[inputIndex].index;
-      const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
-      if (input.redeemScript) {
-        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
-        checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
-        hash = unsignedTx.hashForSignature(
-          inputIndex,
-          input.redeemScript,
-          sighashType,
-        );
-      } else {
-        hash = unsignedTx.hashForSignature(
-          inputIndex,
-          prevout.script,
-          sighashType,
-        );
-      }
-    } else if (input.witnessUtxo) {
-      let script;
-      if (input.redeemScript) {
-        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
-        checkRedeemScript(
-          inputIndex,
-          input.witnessUtxo.script,
-          input.redeemScript,
-        );
-        script = input.redeemScript;
-      } else {
-        script = input.witnessUtxo.script;
-      }
-      if (isPayment(script, payments.p2wpkh)) {
-        // P2WPKH uses the P2PKH template for prevoutScript when signing
-        const signingScript = payments.p2pkh({ hash: script.slice(2) }).output;
-        hash = unsignedTx.hashForWitnessV0(
-          inputIndex,
-          signingScript,
-          input.witnessUtxo.value,
-          sighashType,
-        );
-      } else {
-        if (!input.witnessScript)
-          throw new Error('Segwit input needs witnessScript if not P2WPKH');
-        checkWitnessScript(inputIndex, script, input.witnessScript);
-        hash = unsignedTx.hashForWitnessV0(
-          inputIndex,
-          script,
-          input.witnessUtxo.value,
-          sighashType,
-        );
-      }
-    } else {
-      throw new Error('Need a Utxo input item for signing');
-    }
-    const partialSig = {
-      pubkey: keyPair.publicKey,
-      signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
-    };
-    return this.addPartialSigToInput(inputIndex, partialSig);
-  }
-}
-exports.Psbt = Psbt;
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 2f0b6ea..63cc318 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,13 +1,174 @@
 import { Psbt as PsbtBase } from 'bip174';
 import { PsbtInput } from 'bip174/src/lib/interfaces';
 import { checkForInput } from 'bip174/src/lib/utils';
-import * as classify from './classify';
+// import { hash160 } from './crypto'; // TODO: used in pubkey check
 import { Signer } from './ecpair';
 import { Network } from './networks';
 import * as payments from './payments';
 import * as bscript from './script';
 import { Transaction } from './transaction';
 
+export class Psbt extends PsbtBase {
+  constructor(public network?: Network) {
+    super();
+  }
+
+  canFinalize(inputIndex: number): boolean {
+    const input = checkForInput(this.inputs, inputIndex);
+    const script = getScriptFromInput(
+      inputIndex,
+      input,
+      this.globalMap.unsignedTx!,
+    );
+    if (!script) return false;
+    const scriptType = classifyScript(script);
+    // TODO: for each type
+    switch (scriptType) {
+      case 'pubkey':
+        return false;
+      case 'pubkeyhash':
+        return false;
+      case 'multisig':
+        return false;
+      case 'witnesspubkeyhash':
+        return false;
+      default:
+        return false;
+    }
+  }
+
+  signInput(inputIndex: number, keyPair: Signer): Psbt {
+    const input = this.inputs[inputIndex];
+    if (input === undefined) throw new Error(`No input #${inputIndex}`);
+    const {
+      hash,
+      sighashType,
+      // script, // TODO: use for pubkey check below
+    } = getHashForSig(inputIndex, input, this.globalMap.unsignedTx!);
+
+    const pubkey = keyPair.publicKey;
+    // // TODO: throw error when the pubkey or pubkey hash is not found anywhere
+    // // in the script
+    // const pubkeyHash = hash160(keyPair.publicKey);
+    //
+    // const decompiled = bscript.decompile(script);
+    // if (decompiled === null) throw new Error('Unknown script error');
+    //
+    // const hasKey = decompiled.some(element => {
+    //   if (typeof element === 'number') return false;
+    //   return element.equals(pubkey) || element.equals(pubkeyHash);
+    // });
+    //
+    // if (!hasKey) {
+    //   throw new Error(
+    //     `Can not sign for this input with the key ${pubkey.toString('hex')}`,
+    //   );
+    // }
+
+    const partialSig = {
+      pubkey,
+      signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
+    };
+
+    return this.addPartialSigToInput(inputIndex, partialSig);
+  }
+}
+
+interface HashForSigData {
+  script: Buffer;
+  hash: Buffer;
+  sighashType: number;
+}
+
+const getHashForSig = (
+  inputIndex: number,
+  input: PsbtInput,
+  txBuf: Buffer,
+): HashForSigData => {
+  const unsignedTx = Transaction.fromBuffer(txBuf);
+  const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
+  let hash: Buffer;
+  let script: Buffer;
+
+  if (input.nonWitnessUtxo) {
+    const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
+
+    const prevoutHash = unsignedTx.ins[inputIndex].hash;
+    const utxoHash = nonWitnessUtxoTx.getHash();
+
+    // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
+    if (!prevoutHash.equals(utxoHash)) {
+      throw new Error(
+        `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
+      );
+    }
+
+    const prevoutIndex = unsignedTx.ins[inputIndex].index;
+    const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
+
+    if (input.redeemScript) {
+      // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
+      checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
+      script = input.redeemScript;
+      hash = unsignedTx.hashForSignature(
+        inputIndex,
+        input.redeemScript,
+        sighashType,
+      );
+    } else {
+      script = prevout.script;
+      hash = unsignedTx.hashForSignature(
+        inputIndex,
+        prevout.script,
+        sighashType,
+      );
+    }
+  } else if (input.witnessUtxo) {
+    let _script: Buffer; // so we don't shadow the `let script` above
+    if (input.redeemScript) {
+      // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
+      checkRedeemScript(
+        inputIndex,
+        input.witnessUtxo.script,
+        input.redeemScript,
+      );
+      _script = input.redeemScript;
+    } else {
+      _script = input.witnessUtxo.script;
+    }
+    if (isP2WPKH(_script)) {
+      // P2WPKH uses the P2PKH template for prevoutScript when signing
+      const signingScript = payments.p2pkh({ hash: _script.slice(2) }).output!;
+      hash = unsignedTx.hashForWitnessV0(
+        inputIndex,
+        signingScript,
+        input.witnessUtxo.value,
+        sighashType,
+      );
+      script = _script;
+    } else {
+      if (!input.witnessScript)
+        throw new Error('Segwit input needs witnessScript if not P2WPKH');
+      checkWitnessScript(inputIndex, _script, input.witnessScript);
+      hash = unsignedTx.hashForWitnessV0(
+        inputIndex,
+        _script,
+        input.witnessUtxo.value,
+        sighashType,
+      );
+      // want to make sure the script we return is the actual meaningful script
+      script = input.witnessScript;
+    }
+  } else {
+    throw new Error('Need a Utxo input item for signing');
+  }
+  return {
+    script,
+    sighashType,
+    hash,
+  };
+};
+
 type ScriptCheckerFunction = (idx: number, spk: Buffer, rs: Buffer) => void;
 
 const scriptCheckerFactory = (
@@ -35,7 +196,11 @@ const checkWitnessScript = scriptCheckerFactory(
   'Witness script',
 );
 
-const isPayment = (script: Buffer, payment: any): boolean => {
+type isPaymentFunction = (script: Buffer) => boolean;
+
+const isPaymentFactory = (payment: any): isPaymentFunction => (
+  script: Buffer,
+): boolean => {
   try {
     payment({ output: script });
     return true;
@@ -43,6 +208,18 @@ const isPayment = (script: Buffer, payment: any): boolean => {
     return false;
   }
 };
+const isP2WPKH = isPaymentFactory(payments.p2wpkh);
+const isP2PKH = isPaymentFactory(payments.p2pkh);
+const isP2MS = isPaymentFactory(payments.p2ms);
+const isP2PK = isPaymentFactory(payments.p2pk);
+
+const classifyScript = (script: Buffer): string => {
+  if (isP2WPKH(script)) return 'witnesspubkeyhash';
+  if (isP2PKH(script)) return 'pubkeyhash';
+  if (isP2MS(script)) return 'multisig';
+  if (isP2PK(script)) return 'pubkey';
+  return 'nonstandard';
+};
 
 function getScriptFromInput(
   inputIndex: number,
@@ -73,140 +250,3 @@ function getScriptFromInput(
   }
   return script;
 }
-
-export class Psbt extends PsbtBase {
-  constructor(public network?: Network) {
-    super();
-  }
-
-  canFinalize(inputIndex: number): boolean {
-    const input = checkForInput(this.inputs, inputIndex);
-    const script = getScriptFromInput(
-      inputIndex,
-      input,
-      this.globalMap.unsignedTx!,
-    );
-    if (!script) return false;
-    const scriptType = classify.output(script);
-    switch (scriptType) {
-      case 'pubkey':
-        return false;
-      case 'pubkeyhash':
-        return false;
-      case 'multisig':
-        return false;
-      case 'witnesspubkeyhash':
-        return false;
-      default:
-        return false;
-    }
-  }
-
-  signInput(inputIndex: number, keyPair: Signer): Psbt {
-    // TODO: Implement BIP174 pre-sign checks:
-    // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
-    //
-    // if non_witness_utxo.exists:
-    //     assert(sha256d(non_witness_utxo) == psbt.tx.innput[i].prevout.hash)
-    //     if redeemScript.exists:
-    //         assert(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey == P2SH(redeemScript))
-    //         sign_non_witness(redeemScript)
-    //     else:
-    //         sign_non_witness(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey)
-    // else if witness_utxo.exists:
-    //     if redeemScript.exists:
-    //         assert(witness_utxo.scriptPubKey == P2SH(redeemScript))
-    //         script = redeemScript
-    //     else:
-    //         script = witness_utxo.scriptPubKey
-    //     if IsP2WPKH(script):
-    //         sign_witness(P2PKH(script[2:22]))
-    //     else if IsP2WSH(script):
-    //         assert(script == P2WSH(witnessScript))
-    //         sign_witness(witnessScript)
-    // else:
-    //     assert False
-
-    const input = this.inputs[inputIndex];
-    if (input === undefined) throw new Error(`No input #${inputIndex}`);
-
-    const unsignedTx = Transaction.fromBuffer(this.globalMap.unsignedTx!);
-    const sighashType = input.sighashType || 0x01;
-    let hash: Buffer;
-
-    if (input.nonWitnessUtxo) {
-      const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
-
-      const prevoutHash = unsignedTx.ins[inputIndex].hash;
-      const utxoHash = nonWitnessUtxoTx.getHash();
-
-      // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
-      if (!prevoutHash.equals(utxoHash)) {
-        throw new Error(
-          `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
-        );
-      }
-
-      const prevoutIndex = unsignedTx.ins[inputIndex].index;
-      const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
-
-      if (input.redeemScript) {
-        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
-        checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
-        hash = unsignedTx.hashForSignature(
-          inputIndex,
-          input.redeemScript,
-          sighashType,
-        );
-      } else {
-        hash = unsignedTx.hashForSignature(
-          inputIndex,
-          prevout.script,
-          sighashType,
-        );
-      }
-    } else if (input.witnessUtxo) {
-      let script: Buffer;
-      if (input.redeemScript) {
-        // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
-        checkRedeemScript(
-          inputIndex,
-          input.witnessUtxo.script,
-          input.redeemScript,
-        );
-        script = input.redeemScript;
-      } else {
-        script = input.witnessUtxo.script;
-      }
-      if (isPayment(script, payments.p2wpkh)) {
-        // P2WPKH uses the P2PKH template for prevoutScript when signing
-        const signingScript = payments.p2pkh({ hash: script.slice(2) }).output!;
-        hash = unsignedTx.hashForWitnessV0(
-          inputIndex,
-          signingScript,
-          input.witnessUtxo.value,
-          sighashType,
-        );
-      } else {
-        if (!input.witnessScript)
-          throw new Error('Segwit input needs witnessScript if not P2WPKH');
-        checkWitnessScript(inputIndex, script, input.witnessScript);
-        hash = unsignedTx.hashForWitnessV0(
-          inputIndex,
-          script,
-          input.witnessUtxo.value,
-          sighashType,
-        );
-      }
-    } else {
-      throw new Error('Need a Utxo input item for signing');
-    }
-
-    const partialSig = {
-      pubkey: keyPair.publicKey,
-      signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
-    };
-
-    return this.addPartialSigToInput(inputIndex, partialSig);
-  }
-}

From 4644e9d2ebc65fe7822070c6ed3bbe2821e5a9e7 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 2 Jul 2019 15:18:00 +0900
Subject: [PATCH 022/111] Finish canFinalize

---
 src/psbt.js    | 16 +++++++++++-----
 ts_src/psbt.ts | 18 +++++++++++++-----
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index fa4ba9f..cf1edca 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -19,16 +19,22 @@ class Psbt extends bip174_1.Psbt {
     );
     if (!script) return false;
     const scriptType = classifyScript(script);
-    // TODO: for each type
+    const hasSigs = (neededSigs, partialSig) => {
+      if (!partialSig) return false;
+      if (partialSig.length > neededSigs)
+        throw new Error('Too many signatures');
+      return partialSig.length === neededSigs;
+    };
     switch (scriptType) {
       case 'pubkey':
-        return false;
+        return hasSigs(1, input.partialSig);
       case 'pubkeyhash':
-        return false;
+        return hasSigs(1, input.partialSig);
       case 'multisig':
-        return false;
+        const p2ms = payments.p2ms({ output: script });
+        return hasSigs(p2ms.m, input.partialSig);
       case 'witnesspubkeyhash':
-        return false;
+        return hasSigs(1, input.partialSig);
       default:
         return false;
     }
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 63cc318..b1190aa 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -22,16 +22,24 @@ export class Psbt extends PsbtBase {
     );
     if (!script) return false;
     const scriptType = classifyScript(script);
-    // TODO: for each type
+
+    const hasSigs = (neededSigs: number, partialSig?: any[]): boolean => {
+      if (!partialSig) return false;
+      if (partialSig.length > neededSigs)
+        throw new Error('Too many signatures');
+      return partialSig.length === neededSigs;
+    };
+
     switch (scriptType) {
       case 'pubkey':
-        return false;
+        return hasSigs(1, input.partialSig);
       case 'pubkeyhash':
-        return false;
+        return hasSigs(1, input.partialSig);
       case 'multisig':
-        return false;
+        const p2ms = payments.p2ms({ output: script });
+        return hasSigs(p2ms.m!, input.partialSig);
       case 'witnesspubkeyhash':
-        return false;
+        return hasSigs(1, input.partialSig);
       default:
         return false;
     }

From 354d67a31aaca6c04dc352d361371810ed8ddfa1 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 2 Jul 2019 15:35:23 +0900
Subject: [PATCH 023/111] Just some ideas, TODO mostly.

---
 src/psbt.js    | 12 ++++++++++++
 ts_src/psbt.ts | 12 ++++++++++++
 2 files changed, 24 insertions(+)

diff --git a/src/psbt.js b/src/psbt.js
index cf1edca..651aeb2 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -6,9 +6,21 @@ const payments = require('./payments');
 const bscript = require('./script');
 const transaction_1 = require('./transaction');
 class Psbt extends bip174_1.Psbt {
+  // protected __TX: Transaction;
   constructor(network) {
     super();
     this.network = network;
+    // // TODO: figure out a way to use a Transaction Object instead of a Buffer
+    // // TODO: Caching, since .toBuffer() calls every time we get is lame.
+    // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
+    // delete this.globalMap.unsignedTx;
+    // Object.defineProperty(this.globalMap, 'unsignedTx', {
+    //   enumerable: true,
+    //   writable: false,
+    //   get(): Buffer {
+    //     return this.__TX.toBuffer();
+    //   }
+    // });
   }
   canFinalize(inputIndex) {
     const input = utils_1.checkForInput(this.inputs, inputIndex);
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index b1190aa..cf60f91 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -9,8 +9,20 @@ import * as bscript from './script';
 import { Transaction } from './transaction';
 
 export class Psbt extends PsbtBase {
+  // protected __TX: Transaction;
   constructor(public network?: Network) {
     super();
+    // // TODO: figure out a way to use a Transaction Object instead of a Buffer
+    // // TODO: Caching, since .toBuffer() calls every time we get is lame.
+    // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
+    // delete this.globalMap.unsignedTx;
+    // Object.defineProperty(this.globalMap, 'unsignedTx', {
+    //   enumerable: true,
+    //   writable: false,
+    //   get(): Buffer {
+    //     return this.__TX.toBuffer();
+    //   }
+    // });
   }
 
   canFinalize(inputIndex: number): boolean {

From 7ff40cebc4abb81b83cb8c0a287e066d8d64e094 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Tue, 2 Jul 2019 18:15:30 +0700
Subject: [PATCH 024/111] Recreate test case PSBTs and try and sign them with
 the valid key

---
 test/fixtures/psbt.json | 32 +++++++++++++++++++-------------
 test/psbt.js            | 13 ++++++++-----
 2 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 1b72874..4aaec98 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -4,37 +4,43 @@
       {
         "description": "checks non-witness UTXO matches the hash specified in the prevout",
         "shouldSign": {
-          "psbt": "cHNidP8BADMBAAAAAZIApPIHomdoPo/sOJ8NI9jzILd0cnGn0yAZHRv3D9DSAAAAAAD/////AAAAAAAAAQD9tQEBAAAAAn3dz3mi5UEDC8dThx0cnU3BY+TWvVrvrkvYTeZOFqZSAAAAAItIMEUCIQDTosO1iuDwtVFxGqiUn0eHJEKO+gPzF5w6UNwsms5GqgIgGy2oSiFCmhCvGHcxyIL8H3J+e4lXPgfwGS6ePeefq/ABQQQOOnWcM7A+Gvjl2G+0R6QO/yRMhHpPgnQnbbSQBU6L4Hb4gB3cnFJG7oa28zz+OOi35Xq52zkOs+wexq6e7qET/f///0/vbX88Hl0L6nM7L9ZE+kVs33PyHrfohmonIdeSZunoAQAAAIpHMEQCIChKKYnUXEimyKVWsws0Z+r2q/hm7HXE2fDjhyB09iwHAiBoatgoacFmnmvhYsSjTkxhepcXZvK5aIeJ6y9Jj+XrawFBBA46dZwzsD4a+OXYb7RHpA7/JEyEek+CdCdttJAFTovgdviAHdycUkbuhrbzPP446LflernbOQ6z7B7Grp7uoRP9////AiTHDQAAAAAAGXapFNpkc+03Pgj0bdgAP8p7py++nFVeiKycsA4AAAAAABl2qRRJcHmSWY+FoxqmcVr3D+UHYQtvi4isEcAIAAAA",
-          "inputToCheck": 0
+          "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
         },
         "shouldThrow": {
           "errorMessage": "Non-witness UTXO hash for input #0 doesn't match the hash specified in the prevout",
-          "psbt": "cHNidP8BADMBAAAAAZIApPIHomdoPo/sOJ8NI9jzILd0cnGn0yAZHRv3D9DSAAAAAAD/////AAAAAAAAAQC/AgAAAAHBYCumjIwkFFCni2Hb/eJymJGB0HU3secNMbfbk5VX8gAAAABqRzBEAiAphyuXV5hQyHZY5DG7nfSj8+QVkHd1KaVeJesR7Mr+9QIgURcAqh6iws1JklH5kBTyLFr2OgDHb9JNplABSgqBmekBIQJkGH2e53OqMzrCI2eEeLHfPqJoF4/JRH4KYMRD7dqnSf3///8BmV0AAAAAAAAZdqkUdZ1mdwkelzuenZnxnGj79D4/BfmIrMjTCAAAAA==",
-          "inputToCheck": 0
+          "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQD4AQAAAAABAbD7u8z1SxTjfvhwmkQQvdbbWA+n3GKBBmGecSIAaM5jBQAAABcWABS0SqIhdn2LbW4TAJc3GVh7SnD/eP////8CNg8AAAAAAAAWABSmNm8WWVF+wq+QAeRo9d763jEXhRAnAAAAAAAAGXapFNpkc+03Pgj0bdgAP8p7py++nFVeiKwCRzBEAiB/u0BLwdeerqWf0JH33wwMv8Nn3sKblFvj+CntdC4B9gIgKVVHBH1c9ewnzkuyW6dnz1YARujBJnle1eBNSBAJD9IBIQOmYxHmd2Yz53FpC9+nv+pKdM+5OyEAW3BAN2cccQ0LkgAAAAAAAA==",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
         }
       },
       {
         "description": "checks redeem script matches the scriptPubKey in a non-witness prevout",
         "shouldSign": {
-          "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuAAA=",
-          "inputToCheck": 0
+          "psbt": "cHNidP8BADMBAAAAAR2dq8JwBaxnbWHZGw0HdxuUGFcg6dvx3pgjWMm+Pzf2AAAAAAD/////AAAAAAAAAQC9AgAAAAH//////////////////////////////////////////wAAAABqRzBEAiAf7N+IK1uuxTxvEOoVGabNsiT7jMlfSDCd0VYxv+sQTQIgQVYM7ig9TIx1LzrX2RXgw2zW2fMKuRs/bT9eZx6jmYwBIQJpKKFOB6PrPJhRAtaQ+cHHryY5QYIi5dxZtkMwCtuFYf////8BAOH1BQAAAAAXqRRdh8wk5NRiF7VGQ4Zb4i8Vl1YFMocAAAAAAQRpUiECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWEhAgHGF3SgP82qhvqMptNluTLHhLtzDsmc0pNWEDETNj/rIQIFIl+T3Z90vBFGN8uYJHCrUO4DvrOGVWkVDsBeEzBUi1OuAAA=",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
         },
         "shouldThrow": {
           "errorMessage": "Redeem script for input #0 doesn't match the scriptPubKey in the prevout",
-          "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMAAA==",
-          "inputToCheck": 0
+          "psbt": "cHNidP8BADMBAAAAAR2dq8JwBaxnbWHZGw0HdxuUGFcg6dvx3pgjWMm+Pzf2AAAAAAD/////AAAAAAAAAQC9AgAAAAH//////////////////////////////////////////wAAAABqRzBEAiAf7N+IK1uuxTxvEOoVGabNsiT7jMlfSDCd0VYxv+sQTQIgQVYM7ig9TIx1LzrX2RXgw2zW2fMKuRs/bT9eZx6jmYwBIQJpKKFOB6PrPJhRAtaQ+cHHryY5QYIi5dxZtkMwCtuFYf////8BAOH1BQAAAAAXqRRdh8wk5NRiF7VGQ4Zb4i8Vl1YFMocAAAAAAQRpUiEDGMZFrWWJBIIu33FdV9Q+Zit0fcoBOdgS7ooA2h2QlbAhAuAzQeDZh730hBbfTPzlaXJgCh2Jyui/ufS0k8wqJ55FIQKMg6lgEnyRnGIZ90eP4MmuRdT3EcO4+irJEm5yTCiko1OuAAA=",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
         }
       },
       {
         "description": "checks redeem script matches the scriptPubKey in a witness prevout",
         "shouldSign": {
-          "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==",
-          "inputToCheck": 1
+          "psbt": "cHNidP8BADMBAAAAAYaq+PdOUY2PnV9kZKa82XlqrPByOqwH2TRg2+LQdqs2AAAAAAD/////AAAAAAAAAQEgAOH1BQAAAAAXqRSTNeWHqa9INvSnQ120SZeJc+6JSocBBBYAFC8spHIOpiw9giaEPd5RGkMYvXRHAAA=",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
         },
         "shouldThrow": {
-          "errorMessage": "Redeem script for input #1 doesn't match the scriptPubKey in the prevout",
-          "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==",
-          "inputToCheck": 1
+          "errorMessage": "Redeem script for input #0 doesn't match the scriptPubKey in the prevout",
+          "psbt": "cHNidP8BADMBAAAAAYaq+PdOUY2PnV9kZKa82XlqrPByOqwH2TRg2+LQdqs2AAAAAAD/////AAAAAAAAAQEgAOH1BQAAAAAXqRSTNeWHqa9INvSnQ120SZeJc+6JSocBBBYAFA3zpl6FMnlgCviVJgbcnBj01iLgAAA=",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
         }
       }
     ]
diff --git a/test/psbt.js b/test/psbt.js
index 3bf873d..8584bd6 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -7,20 +7,23 @@ const Psbt = require('..').Psbt
 const fixtures = require('./fixtures/psbt')
 
 describe(`Psbt`, () => {
-  // constants
-  const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex'))
-
   describe('signInput', () => {
     fixtures.signInput.checks.forEach(f => {
       it(f.description, () => {
         const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
         assert.doesNotThrow(() => {
-          psbtThatShouldsign.signInput(f.shouldSign.inputToCheck, keyPair)
+          psbtThatShouldsign.signInput(
+            f.shouldSign.inputToCheck,
+            ECPair.fromWIF(f.shouldSign.WIF),
+          )
         })
     
         const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
         assert.throws(() => {
-          psbtThatShouldThrow.signInput(f.shouldThrow.inputToCheck, keyPair)
+          psbtThatShouldThrow.signInput(
+            f.shouldThrow.inputToCheck,
+            ECPair.fromWIF(f.shouldThrow.WIF),
+          )
         }, {message: f.shouldThrow.errorMessage})
       })
     })

From 8d74bebe044c76c4229e424ebf10dc35feb45a06 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Tue, 2 Jul 2019 18:17:37 +0700
Subject: [PATCH 025/111] Throw error when signing with a privkey that doesn't
 match the pubkey

---
 src/psbt.js    | 32 ++++++++++++++------------------
 ts_src/psbt.ts | 44 +++++++++++++++++++++-----------------------
 2 files changed, 35 insertions(+), 41 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 651aeb2..e03969d 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -2,6 +2,7 @@
 Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
 const utils_1 = require('bip174/src/lib/utils');
+const crypto_1 = require('./crypto');
 const payments = require('./payments');
 const bscript = require('./script');
 const transaction_1 = require('./transaction');
@@ -54,29 +55,24 @@ class Psbt extends bip174_1.Psbt {
   signInput(inputIndex, keyPair) {
     const input = this.inputs[inputIndex];
     if (input === undefined) throw new Error(`No input #${inputIndex}`);
-    const { hash, sighashType } = getHashForSig(
+    const { hash, sighashType, script } = getHashForSig(
       inputIndex,
       input,
       this.globalMap.unsignedTx,
     );
     const pubkey = keyPair.publicKey;
-    // // TODO: throw error when the pubkey or pubkey hash is not found anywhere
-    // // in the script
-    // const pubkeyHash = hash160(keyPair.publicKey);
-    //
-    // const decompiled = bscript.decompile(script);
-    // if (decompiled === null) throw new Error('Unknown script error');
-    //
-    // const hasKey = decompiled.some(element => {
-    //   if (typeof element === 'number') return false;
-    //   return element.equals(pubkey) || element.equals(pubkeyHash);
-    // });
-    //
-    // if (!hasKey) {
-    //   throw new Error(
-    //     `Can not sign for this input with the key ${pubkey.toString('hex')}`,
-    //   );
-    // }
+    const pubkeyHash = crypto_1.hash160(keyPair.publicKey);
+    const decompiled = bscript.decompile(script);
+    if (decompiled === null) throw new Error('Unknown script error');
+    const hasKey = decompiled.some(element => {
+      if (typeof element === 'number') return false;
+      return element.equals(pubkey) || element.equals(pubkeyHash);
+    });
+    if (!hasKey) {
+      throw new Error(
+        `Can not sign for this input with the key ${pubkey.toString('hex')}`,
+      );
+    }
     const partialSig = {
       pubkey,
       signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index cf60f91..194a1f7 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,7 +1,7 @@
 import { Psbt as PsbtBase } from 'bip174';
 import { PsbtInput } from 'bip174/src/lib/interfaces';
 import { checkForInput } from 'bip174/src/lib/utils';
-// import { hash160 } from './crypto'; // TODO: used in pubkey check
+import { hash160 } from './crypto';
 import { Signer } from './ecpair';
 import { Network } from './networks';
 import * as payments from './payments';
@@ -60,30 +60,28 @@ export class Psbt extends PsbtBase {
   signInput(inputIndex: number, keyPair: Signer): Psbt {
     const input = this.inputs[inputIndex];
     if (input === undefined) throw new Error(`No input #${inputIndex}`);
-    const {
-      hash,
-      sighashType,
-      // script, // TODO: use for pubkey check below
-    } = getHashForSig(inputIndex, input, this.globalMap.unsignedTx!);
+    const { hash, sighashType, script } = getHashForSig(
+      inputIndex,
+      input,
+      this.globalMap.unsignedTx!,
+    );
 
     const pubkey = keyPair.publicKey;
-    // // TODO: throw error when the pubkey or pubkey hash is not found anywhere
-    // // in the script
-    // const pubkeyHash = hash160(keyPair.publicKey);
-    //
-    // const decompiled = bscript.decompile(script);
-    // if (decompiled === null) throw new Error('Unknown script error');
-    //
-    // const hasKey = decompiled.some(element => {
-    //   if (typeof element === 'number') return false;
-    //   return element.equals(pubkey) || element.equals(pubkeyHash);
-    // });
-    //
-    // if (!hasKey) {
-    //   throw new Error(
-    //     `Can not sign for this input with the key ${pubkey.toString('hex')}`,
-    //   );
-    // }
+    const pubkeyHash = hash160(keyPair.publicKey);
+
+    const decompiled = bscript.decompile(script);
+    if (decompiled === null) throw new Error('Unknown script error');
+
+    const hasKey = decompiled.some(element => {
+      if (typeof element === 'number') return false;
+      return element.equals(pubkey) || element.equals(pubkeyHash);
+    });
+
+    if (!hasKey) {
+      throw new Error(
+        `Can not sign for this input with the key ${pubkey.toString('hex')}`,
+      );
+    }
 
     const partialSig = {
       pubkey,

From 658ea845b12a9212567b7bdb262594c009510117 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Tue, 2 Jul 2019 18:20:55 +0700
Subject: [PATCH 026/111] Test matching privkey check

---
 test/fixtures/psbt.json | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 4aaec98..696cdcc 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -1,6 +1,20 @@
 {
   "signInput": {
     "checks": [
+      {
+        "description": "checks privkey matches the input it's signing",
+        "shouldSign": {
+          "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
+        },
+        "shouldThrow": {
+          "errorMessage": "Can not sign for this input with the key 02e717fee6be913148f9fd676c0876b7e4574118542c6758b4a9fb9f38f171842b",
+          "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=",
+          "inputToCheck": 0,
+          "WIF": "Kz4mjzErKCH5eQ97RXNQd3Wv7WsLA83BjynfQk4N7BB8J5xuUjAv"
+        }
+      },
       {
         "description": "checks non-witness UTXO matches the hash specified in the prevout",
         "shouldSign": {

From b8789c5d13be55d136eb24904fa285ccfcaa8668 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Tue, 2 Jul 2019 18:29:14 +0700
Subject: [PATCH 027/111] Test input exists check

---
 test/fixtures/psbt.json | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 696cdcc..5e008a4 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -1,6 +1,20 @@
 {
   "signInput": {
     "checks": [
+      {
+        "description": "checks the input exists",
+        "shouldSign": {
+          "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
+        },
+        "shouldThrow": {
+          "errorMessage": "No input #1",
+          "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=",
+          "inputToCheck": 1,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
+        }
+      },
       {
         "description": "checks privkey matches the input it's signing",
         "shouldSign": {

From 343297a3597141a25ef69035f8c20f66f8ae18e1 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Tue, 2 Jul 2019 18:31:46 +0700
Subject: [PATCH 028/111] Test error if UTXO doesn't exist

---
 test/fixtures/psbt.json | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 5e008a4..371a8b7 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -15,6 +15,20 @@
           "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
         }
       },
+      {
+        "description": "checks a UTXO value exists for the input",
+        "shouldSign": {
+          "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
+        },
+        "shouldThrow": {
+          "errorMessage": "Need a Utxo input item for signing",
+          "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAAA=",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
+        }
+      },
       {
         "description": "checks privkey matches the input it's signing",
         "shouldSign": {

From 813b84f91f0294c89fc11cdb66aefd80e9adb28a Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Wed, 3 Jul 2019 15:13:36 +0900
Subject: [PATCH 029/111] Finalize and extract done

---
 src/psbt.js     | 217 +++++++++++++++++++++++++++++++--------
 ts_src/psbt.ts  | 268 +++++++++++++++++++++++++++++++++++++++---------
 types/psbt.d.ts |   8 +-
 3 files changed, 400 insertions(+), 93 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index e03969d..59d898c 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -6,6 +6,7 @@ const crypto_1 = require('./crypto');
 const payments = require('./payments');
 const bscript = require('./script');
 const transaction_1 = require('./transaction');
+const varuint = require('varuint-bitcoin');
 class Psbt extends bip174_1.Psbt {
   // protected __TX: Transaction;
   constructor(network) {
@@ -23,56 +24,75 @@ class Psbt extends bip174_1.Psbt {
     //   }
     // });
   }
-  canFinalize(inputIndex) {
+  extractTransaction() {
+    if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
+    const tx = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
+    this.inputs.forEach((input, idx) => {
+      if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
+      if (input.finalScriptWitness) {
+        const decompiled = bscript.decompile(input.finalScriptWitness);
+        if (decompiled) tx.ins[idx].witness = bscript.toStack(decompiled);
+      }
+    });
+    return tx;
+  }
+  finalizeAllInputs() {
+    const inputResults = range(this.inputs.length).map(idx =>
+      this.finalizeInput(idx),
+    );
+    const result = inputResults.every(val => val === true);
+    return {
+      result,
+      inputResults,
+    };
+  }
+  finalizeInput(inputIndex) {
     const input = utils_1.checkForInput(this.inputs, inputIndex);
-    const script = getScriptFromInput(
+    const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
       this.globalMap.unsignedTx,
     );
     if (!script) return false;
     const scriptType = classifyScript(script);
-    const hasSigs = (neededSigs, partialSig) => {
-      if (!partialSig) return false;
-      if (partialSig.length > neededSigs)
-        throw new Error('Too many signatures');
-      return partialSig.length === neededSigs;
-    };
-    switch (scriptType) {
-      case 'pubkey':
-        return hasSigs(1, input.partialSig);
-      case 'pubkeyhash':
-        return hasSigs(1, input.partialSig);
-      case 'multisig':
-        const p2ms = payments.p2ms({ output: script });
-        return hasSigs(p2ms.m, input.partialSig);
-      case 'witnesspubkeyhash':
-        return hasSigs(1, input.partialSig);
-      default:
-        return false;
+    if (!canFinalize(input, script, scriptType)) return false;
+    let finalScriptSig;
+    let finalScriptWitness;
+    // Wow, the payments API is very handy
+    const payment = getPayment(script, scriptType, input.partialSig);
+    const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
+    const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
+    if (isSegwit) {
+      if (p2wsh) {
+        finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness);
+      } else {
+        finalScriptWitness = witnessStackToScriptWitness(payment.witness);
+      }
+      if (p2sh) {
+        finalScriptSig = bscript.compile([p2sh.redeem.output]);
+      }
+    } else {
+      finalScriptSig = payment.input;
     }
+    if (finalScriptSig)
+      this.addFinalScriptSigToInput(inputIndex, finalScriptSig);
+    if (finalScriptWitness)
+      this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
+    if (!finalScriptSig && !finalScriptWitness) return false;
+    this.clearFinalizedInput(inputIndex);
+    return true;
   }
   signInput(inputIndex, keyPair) {
-    const input = this.inputs[inputIndex];
-    if (input === undefined) throw new Error(`No input #${inputIndex}`);
+    const input = utils_1.checkForInput(this.inputs, inputIndex);
+    if (!keyPair || !keyPair.publicKey)
+      throw new Error('Need Signer to sign input');
     const { hash, sighashType, script } = getHashForSig(
       inputIndex,
       input,
       this.globalMap.unsignedTx,
     );
     const pubkey = keyPair.publicKey;
-    const pubkeyHash = crypto_1.hash160(keyPair.publicKey);
-    const decompiled = bscript.decompile(script);
-    if (decompiled === null) throw new Error('Unknown script error');
-    const hasKey = decompiled.some(element => {
-      if (typeof element === 'number') return false;
-      return element.equals(pubkey) || element.equals(pubkeyHash);
-    });
-    if (!hasKey) {
-      throw new Error(
-        `Can not sign for this input with the key ${pubkey.toString('hex')}`,
-      );
-    }
+    checkScriptForPubkey(pubkey, script);
     const partialSig = {
       pubkey,
       signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
@@ -81,6 +101,77 @@ class Psbt extends bip174_1.Psbt {
   }
 }
 exports.Psbt = Psbt;
+//
+//
+//
+//
+// Helper functions
+//
+//
+//
+//
+function isFinalized(input) {
+  return !!input.finalScriptSig || !!input.finalScriptWitness;
+}
+function getPayment(script, scriptType, partialSig) {
+  let payment;
+  switch (scriptType) {
+    case 'multisig':
+      payment = payments.p2ms({
+        output: script,
+        signatures: partialSig.map(ps => ps.signature),
+      });
+      break;
+    case 'pubkey':
+      payment = payments.p2pk({
+        output: script,
+        signature: partialSig[0].signature,
+      });
+      break;
+    case 'pubkeyhash':
+      payment = payments.p2pkh({
+        output: script,
+        pubkey: partialSig[0].pubkey,
+        signature: partialSig[0].signature,
+      });
+      break;
+    case 'witnesspubkeyhash':
+      payment = payments.p2wpkh({
+        output: script,
+        pubkey: partialSig[0].pubkey,
+        signature: partialSig[0].signature,
+      });
+      break;
+  }
+  return payment;
+}
+function canFinalize(input, script, scriptType) {
+  switch (scriptType) {
+    case 'pubkey':
+    case 'pubkeyhash':
+    case 'witnesspubkeyhash':
+      return hasSigs(1, input.partialSig);
+    case 'multisig':
+      const p2ms = payments.p2ms({ output: script });
+      return hasSigs(p2ms.m, input.partialSig);
+    default:
+      return false;
+  }
+}
+function checkScriptForPubkey(pubkey, script) {
+  const pubkeyHash = crypto_1.hash160(pubkey);
+  const decompiled = bscript.decompile(script);
+  if (decompiled === null) throw new Error('Unknown script error');
+  const hasKey = decompiled.some(element => {
+    if (typeof element === 'number') return false;
+    return element.equals(pubkey) || element.equals(pubkeyHash);
+  });
+  if (!hasKey) {
+    throw new Error(
+      `Can not sign for this input with the key ${pubkey.toString('hex')}`,
+    );
+  }
+}
 const getHashForSig = (inputIndex, input, txBuf) => {
   const unsignedTx = transaction_1.Transaction.fromBuffer(txBuf);
   const sighashType =
@@ -202,29 +293,67 @@ const classifyScript = script => {
   return 'nonstandard';
 };
 function getScriptFromInput(inputIndex, input, _unsignedTx) {
-  let script;
+  const res = {
+    script: null,
+    isSegwit: false,
+    isP2SH: false,
+    isP2WSH: false,
+  };
   if (input.nonWitnessUtxo) {
     if (input.redeemScript) {
-      script = input.redeemScript;
+      res.isP2SH = true;
+      res.script = input.redeemScript;
     } else {
       const unsignedTx = transaction_1.Transaction.fromBuffer(_unsignedTx);
       const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
         input.nonWitnessUtxo,
       );
       const prevoutIndex = unsignedTx.ins[inputIndex].index;
-      script = nonWitnessUtxoTx.outs[prevoutIndex].script;
+      res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
     }
   } else if (input.witnessUtxo) {
+    res.isSegwit = true;
+    res.isP2SH = !!input.redeemScript;
+    res.isP2WSH = !!input.witnessScript;
     if (input.witnessScript) {
-      script = input.witnessScript;
+      res.script = input.witnessScript;
     } else if (input.redeemScript) {
-      script = payments.p2pkh({ hash: input.redeemScript.slice(2) }).output;
+      res.script = payments.p2pkh({
+        hash: input.redeemScript.slice(2),
+      }).output;
     } else {
-      script = payments.p2pkh({ hash: input.witnessUtxo.script.slice(2) })
-        .output;
+      res.script = payments.p2pkh({
+        hash: input.witnessUtxo.script.slice(2),
+      }).output;
     }
-  } else {
-    return;
   }
-  return script;
+  return res;
 }
+const hasSigs = (neededSigs, partialSig) => {
+  if (!partialSig) return false;
+  if (partialSig.length > neededSigs) throw new Error('Too many signatures');
+  return partialSig.length === neededSigs;
+};
+function witnessStackToScriptWitness(witness) {
+  let buffer = Buffer.allocUnsafe(0);
+  function writeSlice(slice) {
+    buffer = Buffer.concat([buffer, Buffer.from(slice)]);
+  }
+  function writeVarInt(i) {
+    const currentLen = buffer.length;
+    const varintLen = varuint.encodingLength(i);
+    buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
+    varuint.encode(i, buffer, currentLen);
+  }
+  function writeVarSlice(slice) {
+    writeVarInt(slice.length);
+    writeSlice(slice);
+  }
+  function writeVector(vector) {
+    writeVarInt(vector.length);
+    vector.forEach(writeVarSlice);
+  }
+  writeVector(witness);
+  return buffer;
+}
+const range = n => [...Array(n).keys()];
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 194a1f7..273a1c3 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,5 +1,5 @@
 import { Psbt as PsbtBase } from 'bip174';
-import { PsbtInput } from 'bip174/src/lib/interfaces';
+import { PartialSig, PsbtInput } from 'bip174/src/lib/interfaces';
 import { checkForInput } from 'bip174/src/lib/utils';
 import { hash160 } from './crypto';
 import { Signer } from './ecpair';
@@ -7,6 +7,7 @@ import { Network } from './networks';
 import * as payments from './payments';
 import * as bscript from './script';
 import { Transaction } from './transaction';
+const varuint = require('varuint-bitcoin');
 
 export class Psbt extends PsbtBase {
   // protected __TX: Transaction;
@@ -25,41 +26,84 @@ export class Psbt extends PsbtBase {
     // });
   }
 
-  canFinalize(inputIndex: number): boolean {
+  extractTransaction(): Transaction {
+    if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
+    const tx = Transaction.fromBuffer(this.globalMap.unsignedTx!);
+    this.inputs.forEach((input, idx) => {
+      if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
+      if (input.finalScriptWitness) {
+        const decompiled = bscript.decompile(input.finalScriptWitness);
+        if (decompiled) tx.ins[idx].witness = bscript.toStack(decompiled);
+      }
+    });
+    return tx;
+  }
+
+  finalizeAllInputs(): {
+    result: boolean;
+    inputResults: boolean[];
+  } {
+    const inputResults = range(this.inputs.length).map(idx =>
+      this.finalizeInput(idx),
+    );
+    const result = inputResults.every(val => val === true);
+    return {
+      result,
+      inputResults,
+    };
+  }
+
+  finalizeInput(inputIndex: number): boolean {
     const input = checkForInput(this.inputs, inputIndex);
-    const script = getScriptFromInput(
+    const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
       this.globalMap.unsignedTx!,
     );
     if (!script) return false;
+
     const scriptType = classifyScript(script);
+    if (!canFinalize(input, script, scriptType)) return false;
 
-    const hasSigs = (neededSigs: number, partialSig?: any[]): boolean => {
-      if (!partialSig) return false;
-      if (partialSig.length > neededSigs)
-        throw new Error('Too many signatures');
-      return partialSig.length === neededSigs;
-    };
+    let finalScriptSig: Buffer | undefined;
+    let finalScriptWitness: Buffer | undefined;
 
-    switch (scriptType) {
-      case 'pubkey':
-        return hasSigs(1, input.partialSig);
-      case 'pubkeyhash':
-        return hasSigs(1, input.partialSig);
-      case 'multisig':
-        const p2ms = payments.p2ms({ output: script });
-        return hasSigs(p2ms.m!, input.partialSig);
-      case 'witnesspubkeyhash':
-        return hasSigs(1, input.partialSig);
-      default:
-        return false;
+    // Wow, the payments API is very handy
+    const payment: payments.Payment = getPayment(
+      script,
+      scriptType,
+      input.partialSig!,
+    );
+    const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
+    const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
+
+    if (isSegwit) {
+      if (p2wsh) {
+        finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness!);
+      } else {
+        finalScriptWitness = witnessStackToScriptWitness(payment.witness!);
+      }
+      if (p2sh) {
+        finalScriptSig = bscript.compile([p2sh.redeem!.output!]);
+      }
+    } else {
+      finalScriptSig = payment.input;
     }
+
+    if (finalScriptSig)
+      this.addFinalScriptSigToInput(inputIndex, finalScriptSig);
+    if (finalScriptWitness)
+      this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
+    if (!finalScriptSig && !finalScriptWitness) return false;
+
+    this.clearFinalizedInput(inputIndex);
+    return true;
   }
 
   signInput(inputIndex: number, keyPair: Signer): Psbt {
-    const input = this.inputs[inputIndex];
-    if (input === undefined) throw new Error(`No input #${inputIndex}`);
+    const input = checkForInput(this.inputs, inputIndex);
+    if (!keyPair || !keyPair.publicKey)
+      throw new Error('Need Signer to sign input');
     const { hash, sighashType, script } = getHashForSig(
       inputIndex,
       input,
@@ -67,21 +111,8 @@ export class Psbt extends PsbtBase {
     );
 
     const pubkey = keyPair.publicKey;
-    const pubkeyHash = hash160(keyPair.publicKey);
 
-    const decompiled = bscript.decompile(script);
-    if (decompiled === null) throw new Error('Unknown script error');
-
-    const hasKey = decompiled.some(element => {
-      if (typeof element === 'number') return false;
-      return element.equals(pubkey) || element.equals(pubkeyHash);
-    });
-
-    if (!hasKey) {
-      throw new Error(
-        `Can not sign for this input with the key ${pubkey.toString('hex')}`,
-      );
-    }
+    checkScriptForPubkey(pubkey, script);
 
     const partialSig = {
       pubkey,
@@ -92,6 +123,93 @@ export class Psbt extends PsbtBase {
   }
 }
 
+//
+//
+//
+//
+// Helper functions
+//
+//
+//
+//
+
+function isFinalized(input: PsbtInput): boolean {
+  return !!input.finalScriptSig || !!input.finalScriptWitness;
+}
+
+function getPayment(
+  script: Buffer,
+  scriptType: string,
+  partialSig: PartialSig[],
+): payments.Payment {
+  let payment: payments.Payment;
+  switch (scriptType) {
+    case 'multisig':
+      payment = payments.p2ms({
+        output: script,
+        signatures: partialSig.map(ps => ps.signature),
+      });
+      break;
+    case 'pubkey':
+      payment = payments.p2pk({
+        output: script,
+        signature: partialSig[0].signature,
+      });
+      break;
+    case 'pubkeyhash':
+      payment = payments.p2pkh({
+        output: script,
+        pubkey: partialSig[0].pubkey,
+        signature: partialSig[0].signature,
+      });
+      break;
+    case 'witnesspubkeyhash':
+      payment = payments.p2wpkh({
+        output: script,
+        pubkey: partialSig[0].pubkey,
+        signature: partialSig[0].signature,
+      });
+      break;
+  }
+  return payment!;
+}
+
+function canFinalize(
+  input: PsbtInput,
+  script: Buffer,
+  scriptType: string,
+): boolean {
+  switch (scriptType) {
+    case 'pubkey':
+    case 'pubkeyhash':
+    case 'witnesspubkeyhash':
+      return hasSigs(1, input.partialSig);
+    case 'multisig':
+      const p2ms = payments.p2ms({ output: script });
+      return hasSigs(p2ms.m!, input.partialSig);
+    default:
+      return false;
+  }
+}
+
+function checkScriptForPubkey(pubkey: Buffer, script: Buffer): void {
+  const pubkeyHash = hash160(pubkey);
+
+  const decompiled = bscript.decompile(script);
+  if (decompiled === null) throw new Error('Unknown script error');
+
+  const hasKey = decompiled.some(element => {
+    if (typeof element === 'number') return false;
+    return element.equals(pubkey) || element.equals(pubkeyHash);
+  });
+
+  if (!hasKey) {
+    throw new Error(
+      `Can not sign for this input with the key ${pubkey.toString('hex')}`,
+    );
+  }
+}
+
 interface HashForSigData {
   script: Buffer;
   hash: Buffer;
@@ -239,32 +357,86 @@ const classifyScript = (script: Buffer): string => {
   return 'nonstandard';
 };
 
+interface GetScriptReturn {
+  script: Buffer | null;
+  isSegwit: boolean;
+  isP2SH: boolean;
+  isP2WSH: boolean;
+}
 function getScriptFromInput(
   inputIndex: number,
   input: PsbtInput,
   _unsignedTx: Buffer,
-): Buffer | undefined {
-  let script: Buffer;
+): GetScriptReturn {
+  const res: GetScriptReturn = {
+    script: null,
+    isSegwit: false,
+    isP2SH: false,
+    isP2WSH: false,
+  };
   if (input.nonWitnessUtxo) {
     if (input.redeemScript) {
-      script = input.redeemScript;
+      res.isP2SH = true;
+      res.script = input.redeemScript;
     } else {
       const unsignedTx = Transaction.fromBuffer(_unsignedTx);
       const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
       const prevoutIndex = unsignedTx.ins[inputIndex].index;
-      script = nonWitnessUtxoTx.outs[prevoutIndex].script;
+      res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
     }
   } else if (input.witnessUtxo) {
+    res.isSegwit = true;
+    res.isP2SH = !!input.redeemScript;
+    res.isP2WSH = !!input.witnessScript;
     if (input.witnessScript) {
-      script = input.witnessScript;
+      res.script = input.witnessScript;
     } else if (input.redeemScript) {
-      script = payments.p2pkh({ hash: input.redeemScript.slice(2) }).output!;
+      res.script = payments.p2pkh({
+        hash: input.redeemScript.slice(2),
+      }).output!;
     } else {
-      script = payments.p2pkh({ hash: input.witnessUtxo.script.slice(2) })
-        .output!;
+      res.script = payments.p2pkh({
+        hash: input.witnessUtxo.script.slice(2),
+      }).output!;
     }
-  } else {
-    return;
   }
-  return script;
+  return res;
 }
+
+const hasSigs = (neededSigs: number, partialSig?: any[]): boolean => {
+  if (!partialSig) return false;
+  if (partialSig.length > neededSigs) throw new Error('Too many signatures');
+  return partialSig.length === neededSigs;
+};
+
+function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
+  let buffer = Buffer.allocUnsafe(0);
+
+  function writeSlice(slice: Buffer): void {
+    buffer = Buffer.concat([buffer, Buffer.from(slice)]);
+  }
+
+  function writeVarInt(i: number): void {
+    const currentLen = buffer.length;
+    const varintLen = varuint.encodingLength(i);
+
+    buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
+    varuint.encode(i, buffer, currentLen);
+  }
+
+  function writeVarSlice(slice: Buffer): void {
+    writeVarInt(slice.length);
+    writeSlice(slice);
+  }
+
+  function writeVector(vector: Buffer[]): void {
+    writeVarInt(vector.length);
+    vector.forEach(writeVarSlice);
+  }
+
+  writeVector(witness);
+
+  return buffer;
+}
+
+const range = (n: number): number[] => [...Array(n).keys()];
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index fda7e6b..f5b6430 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -1,9 +1,15 @@
 import { Psbt as PsbtBase } from 'bip174';
 import { Signer } from './ecpair';
 import { Network } from './networks';
+import { Transaction } from './transaction';
 export declare class Psbt extends PsbtBase {
     network?: Network | undefined;
     constructor(network?: Network | undefined);
-    canFinalize(inputIndex: number): boolean;
+    extractTransaction(): Transaction;
+    finalizeAllInputs(): {
+        result: boolean;
+        inputResults: boolean[];
+    };
+    finalizeInput(inputIndex: number): boolean;
     signInput(inputIndex: number, keyPair: Signer): Psbt;
 }

From 77dde89acc047607ed9a30506379bc80fdba3642 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Wed, 3 Jul 2019 15:34:18 +0900
Subject: [PATCH 030/111] Add async signing method

---
 src/psbt.js     |  94 ++++++++++++++++++++++++++++----------
 ts_src/psbt.ts  | 117 ++++++++++++++++++++++++++++++++++++------------
 tsconfig.json   |   2 +-
 types/psbt.d.ts |   3 +-
 4 files changed, 161 insertions(+), 55 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 59d898c..ae60861 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -56,24 +56,14 @@ class Psbt extends bip174_1.Psbt {
     if (!script) return false;
     const scriptType = classifyScript(script);
     if (!canFinalize(input, script, scriptType)) return false;
-    let finalScriptSig;
-    let finalScriptWitness;
-    // Wow, the payments API is very handy
-    const payment = getPayment(script, scriptType, input.partialSig);
-    const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
-    const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
-    if (isSegwit) {
-      if (p2wsh) {
-        finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness);
-      } else {
-        finalScriptWitness = witnessStackToScriptWitness(payment.witness);
-      }
-      if (p2sh) {
-        finalScriptSig = bscript.compile([p2sh.redeem.output]);
-      }
-    } else {
-      finalScriptSig = payment.input;
-    }
+    const { finalScriptSig, finalScriptWitness } = getFinalScripts(
+      script,
+      scriptType,
+      input.partialSig,
+      isSegwit,
+      isP2SH,
+      isP2WSH,
+    );
     if (finalScriptSig)
       this.addFinalScriptSigToInput(inputIndex, finalScriptSig);
     if (finalScriptWitness)
@@ -83,22 +73,38 @@ class Psbt extends bip174_1.Psbt {
     return true;
   }
   signInput(inputIndex, keyPair) {
-    const input = utils_1.checkForInput(this.inputs, inputIndex);
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
-    const { hash, sighashType, script } = getHashForSig(
+    const { hash, sighashType } = getHashAndSighashType(
+      this.inputs,
       inputIndex,
-      input,
+      keyPair.publicKey,
       this.globalMap.unsignedTx,
     );
-    const pubkey = keyPair.publicKey;
-    checkScriptForPubkey(pubkey, script);
     const partialSig = {
-      pubkey,
+      pubkey: keyPair.publicKey,
       signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
     };
     return this.addPartialSigToInput(inputIndex, partialSig);
   }
+  async signInputAsync(inputIndex, keyPair) {
+    if (!keyPair || !keyPair.publicKey)
+      throw new Error('Need Signer to sign input');
+    const { hash, sighashType } = getHashAndSighashType(
+      this.inputs,
+      inputIndex,
+      keyPair.publicKey,
+      this.globalMap.unsignedTx,
+    );
+    const partialSig = {
+      pubkey: keyPair.publicKey,
+      signature: bscript.signature.encode(
+        await keyPair.sign(hash),
+        sighashType,
+      ),
+    };
+    this.addPartialSigToInput(inputIndex, partialSig);
+  }
 }
 exports.Psbt = Psbt;
 //
@@ -113,6 +119,46 @@ exports.Psbt = Psbt;
 function isFinalized(input) {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
+function getHashAndSighashType(inputs, inputIndex, pubkey, txBuf) {
+  const input = utils_1.checkForInput(inputs, inputIndex);
+  const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf);
+  checkScriptForPubkey(pubkey, script);
+  return {
+    hash,
+    sighashType,
+  };
+}
+function getFinalScripts(
+  script,
+  scriptType,
+  partialSig,
+  isSegwit,
+  isP2SH,
+  isP2WSH,
+) {
+  let finalScriptSig;
+  let finalScriptWitness;
+  // Wow, the payments API is very handy
+  const payment = getPayment(script, scriptType, partialSig);
+  const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
+  const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
+  if (isSegwit) {
+    if (p2wsh) {
+      finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness);
+    } else {
+      finalScriptWitness = witnessStackToScriptWitness(payment.witness);
+    }
+    if (p2sh) {
+      finalScriptSig = bscript.compile([p2sh.redeem.output]);
+    }
+  } else {
+    finalScriptSig = payment.input;
+  }
+  return {
+    finalScriptSig,
+    finalScriptWitness,
+  };
+}
 function getPayment(script, scriptType, partialSig) {
   let payment;
   switch (scriptType) {
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 273a1c3..d9a059c 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -2,7 +2,7 @@ import { Psbt as PsbtBase } from 'bip174';
 import { PartialSig, PsbtInput } from 'bip174/src/lib/interfaces';
 import { checkForInput } from 'bip174/src/lib/utils';
 import { hash160 } from './crypto';
-import { Signer } from './ecpair';
+import { Signer, SignerAsync } from './ecpair';
 import { Network } from './networks';
 import * as payments from './payments';
 import * as bscript from './script';
@@ -65,30 +65,14 @@ export class Psbt extends PsbtBase {
     const scriptType = classifyScript(script);
     if (!canFinalize(input, script, scriptType)) return false;
 
-    let finalScriptSig: Buffer | undefined;
-    let finalScriptWitness: Buffer | undefined;
-
-    // Wow, the payments API is very handy
-    const payment: payments.Payment = getPayment(
+    const { finalScriptSig, finalScriptWitness } = getFinalScripts(
       script,
       scriptType,
       input.partialSig!,
+      isSegwit,
+      isP2SH,
+      isP2WSH,
     );
-    const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
-    const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
-
-    if (isSegwit) {
-      if (p2wsh) {
-        finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness!);
-      } else {
-        finalScriptWitness = witnessStackToScriptWitness(payment.witness!);
-      }
-      if (p2sh) {
-        finalScriptSig = bscript.compile([p2sh.redeem!.output!]);
-      }
-    } else {
-      finalScriptSig = payment.input;
-    }
 
     if (finalScriptSig)
       this.addFinalScriptSigToInput(inputIndex, finalScriptSig);
@@ -101,26 +85,46 @@ export class Psbt extends PsbtBase {
   }
 
   signInput(inputIndex: number, keyPair: Signer): Psbt {
-    const input = checkForInput(this.inputs, inputIndex);
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
-    const { hash, sighashType, script } = getHashForSig(
+    const { hash, sighashType } = getHashAndSighashType(
+      this.inputs,
       inputIndex,
-      input,
+      keyPair.publicKey,
       this.globalMap.unsignedTx!,
     );
 
-    const pubkey = keyPair.publicKey;
-
-    checkScriptForPubkey(pubkey, script);
-
     const partialSig = {
-      pubkey,
+      pubkey: keyPair.publicKey,
       signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
     };
 
     return this.addPartialSigToInput(inputIndex, partialSig);
   }
+
+  async signInputAsync(
+    inputIndex: number,
+    keyPair: SignerAsync,
+  ): Promise<void> {
+    if (!keyPair || !keyPair.publicKey)
+      throw new Error('Need Signer to sign input');
+    const { hash, sighashType } = getHashAndSighashType(
+      this.inputs,
+      inputIndex,
+      keyPair.publicKey,
+      this.globalMap.unsignedTx!,
+    );
+
+    const partialSig = {
+      pubkey: keyPair.publicKey,
+      signature: bscript.signature.encode(
+        await keyPair.sign(hash),
+        sighashType,
+      ),
+    };
+
+    this.addPartialSigToInput(inputIndex, partialSig);
+  }
 }
 
 //
@@ -137,6 +141,61 @@ function isFinalized(input: PsbtInput): boolean {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
 
+function getHashAndSighashType(
+  inputs: PsbtInput[],
+  inputIndex: number,
+  pubkey: Buffer,
+  txBuf: Buffer,
+): {
+  hash: Buffer;
+  sighashType: number;
+} {
+  const input = checkForInput(inputs, inputIndex);
+  const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf);
+  checkScriptForPubkey(pubkey, script);
+  return {
+    hash,
+    sighashType,
+  };
+}
+
+function getFinalScripts(
+  script: Buffer,
+  scriptType: string,
+  partialSig: PartialSig[],
+  isSegwit: boolean,
+  isP2SH: boolean,
+  isP2WSH: boolean,
+): {
+  finalScriptSig: Buffer | undefined;
+  finalScriptWitness: Buffer | undefined;
+} {
+  let finalScriptSig: Buffer | undefined;
+  let finalScriptWitness: Buffer | undefined;
+
+  // Wow, the payments API is very handy
+  const payment: payments.Payment = getPayment(script, scriptType, partialSig);
+  const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
+  const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
+
+  if (isSegwit) {
+    if (p2wsh) {
+      finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness!);
+    } else {
+      finalScriptWitness = witnessStackToScriptWitness(payment.witness!);
+    }
+    if (p2sh) {
+      finalScriptSig = bscript.compile([p2sh.redeem!.output!]);
+    }
+  } else {
+    finalScriptSig = payment.input;
+  }
+  return {
+    finalScriptSig,
+    finalScriptWitness,
+  };
+}
+
 function getPayment(
   script: Buffer,
   scriptType: string,
diff --git a/tsconfig.json b/tsconfig.json
index f770a45..1de632d 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
 {
   "compilerOptions": {
-    "target": "ES2015",
+    "target": "ES2017",
     "module": "commonjs",
     "outDir": "./src",
     "declaration": true,
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index f5b6430..26ae0a7 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -1,5 +1,5 @@
 import { Psbt as PsbtBase } from 'bip174';
-import { Signer } from './ecpair';
+import { Signer, SignerAsync } from './ecpair';
 import { Network } from './networks';
 import { Transaction } from './transaction';
 export declare class Psbt extends PsbtBase {
@@ -12,4 +12,5 @@ export declare class Psbt extends PsbtBase {
     };
     finalizeInput(inputIndex: number): boolean;
     signInput(inputIndex: number, keyPair: Signer): Psbt;
+    signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>;
 }

From 1c8fc6978015ae86f6950eccfdffb68f5d73d5a1 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Wed, 3 Jul 2019 15:48:56 +0900
Subject: [PATCH 031/111] Stick with ES2015 for now

---
 src/psbt.js    | 17 ++++++++---------
 ts_src/psbt.ts | 20 ++++++++------------
 tsconfig.json  |  2 +-
 3 files changed, 17 insertions(+), 22 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index ae60861..1ed7354 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -87,7 +87,7 @@ class Psbt extends bip174_1.Psbt {
     };
     return this.addPartialSigToInput(inputIndex, partialSig);
   }
-  async signInputAsync(inputIndex, keyPair) {
+  signInputAsync(inputIndex, keyPair) {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
     const { hash, sighashType } = getHashAndSighashType(
@@ -96,14 +96,13 @@ class Psbt extends bip174_1.Psbt {
       keyPair.publicKey,
       this.globalMap.unsignedTx,
     );
-    const partialSig = {
-      pubkey: keyPair.publicKey,
-      signature: bscript.signature.encode(
-        await keyPair.sign(hash),
-        sighashType,
-      ),
-    };
-    this.addPartialSigToInput(inputIndex, partialSig);
+    return keyPair.sign(hash).then(signature => {
+      const partialSig = {
+        pubkey: keyPair.publicKey,
+        signature: bscript.signature.encode(signature, sighashType),
+      };
+      this.addPartialSigToInput(inputIndex, partialSig);
+    });
   }
 }
 exports.Psbt = Psbt;
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index d9a059c..680ea92 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -102,10 +102,7 @@ export class Psbt extends PsbtBase {
     return this.addPartialSigToInput(inputIndex, partialSig);
   }
 
-  async signInputAsync(
-    inputIndex: number,
-    keyPair: SignerAsync,
-  ): Promise<void> {
+  signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void> {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
     const { hash, sighashType } = getHashAndSighashType(
@@ -115,15 +112,14 @@ export class Psbt extends PsbtBase {
       this.globalMap.unsignedTx!,
     );
 
-    const partialSig = {
-      pubkey: keyPair.publicKey,
-      signature: bscript.signature.encode(
-        await keyPair.sign(hash),
-        sighashType,
-      ),
-    };
+    return keyPair.sign(hash).then(signature => {
+      const partialSig = {
+        pubkey: keyPair.publicKey,
+        signature: bscript.signature.encode(signature, sighashType),
+      };
 
-    this.addPartialSigToInput(inputIndex, partialSig);
+      this.addPartialSigToInput(inputIndex, partialSig);
+    });
   }
 }
 
diff --git a/tsconfig.json b/tsconfig.json
index 1de632d..f770a45 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
 {
   "compilerOptions": {
-    "target": "ES2017",
+    "target": "ES2015",
     "module": "commonjs",
     "outDir": "./src",
     "declaration": true,

From 48fc75c4f016f199c734e7433a9622027e3f81ad Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Wed, 3 Jul 2019 18:42:31 +0900
Subject: [PATCH 032/111] Fix p2sh and p2wsh not working

---
 src/psbt.js    | 60 ++++++++++++++++++++++++++++++++++++++------
 ts_src/psbt.ts | 67 ++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 111 insertions(+), 16 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 1ed7354..3cbd899 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -30,8 +30,9 @@ class Psbt extends bip174_1.Psbt {
     this.inputs.forEach((input, idx) => {
       if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
       if (input.finalScriptWitness) {
-        const decompiled = bscript.decompile(input.finalScriptWitness);
-        if (decompiled) tx.ins[idx].witness = bscript.toStack(decompiled);
+        tx.ins[idx].witness = scriptWitnessToWitnessStack(
+          input.finalScriptWitness,
+        );
       }
     });
     return tx;
@@ -148,23 +149,44 @@ function getFinalScripts(
       finalScriptWitness = witnessStackToScriptWitness(payment.witness);
     }
     if (p2sh) {
-      finalScriptSig = bscript.compile([p2sh.redeem.output]);
+      finalScriptSig = p2sh.input;
     }
   } else {
-    finalScriptSig = payment.input;
+    if (p2sh) {
+      finalScriptSig = p2sh.input;
+    } else {
+      finalScriptSig = payment.input;
+    }
   }
   return {
     finalScriptSig,
     finalScriptWitness,
   };
 }
+function getSortedSigs(script, partialSig) {
+  const p2ms = payments.p2ms({ output: script });
+  // for each pubkey in order of p2ms script
+  return p2ms.pubkeys
+    .map(pk => {
+      // filter partialSig array by pubkey being equal
+      return (
+        partialSig.filter(ps => {
+          return ps.pubkey.equals(pk);
+        })[0] || {}
+      ).signature;
+      // Any pubkey without a match will return undefined
+      // this last filter removes all the undefined items in the array.
+    })
+    .filter(v => !!v);
+}
 function getPayment(script, scriptType, partialSig) {
   let payment;
   switch (scriptType) {
     case 'multisig':
+      const sigs = getSortedSigs(script, partialSig);
       payment = payments.p2ms({
         output: script,
-        signatures: partialSig.map(ps => ps.signature),
+        signatures: sigs,
       });
       break;
     case 'pubkey':
@@ -283,7 +305,7 @@ const getHashForSig = (inputIndex, input, txBuf) => {
       checkWitnessScript(inputIndex, _script, input.witnessScript);
       hash = unsignedTx.hashForWitnessV0(
         inputIndex,
-        _script,
+        input.witnessScript,
         input.witnessUtxo.value,
         sighashType,
       );
@@ -363,11 +385,11 @@ function getScriptFromInput(inputIndex, input, _unsignedTx) {
     if (input.witnessScript) {
       res.script = input.witnessScript;
     } else if (input.redeemScript) {
-      res.script = payments.p2pkh({
+      res.script = payments.p2wpkh({
         hash: input.redeemScript.slice(2),
       }).output;
     } else {
-      res.script = payments.p2pkh({
+      res.script = payments.p2wpkh({
         hash: input.witnessUtxo.script.slice(2),
       }).output;
     }
@@ -401,4 +423,26 @@ function witnessStackToScriptWitness(witness) {
   writeVector(witness);
   return buffer;
 }
+function scriptWitnessToWitnessStack(buffer) {
+  let offset = 0;
+  function readSlice(n) {
+    offset += n;
+    return buffer.slice(offset - n, offset);
+  }
+  function readVarInt() {
+    const vi = varuint.decode(buffer, offset);
+    offset += varuint.decode.bytes;
+    return vi;
+  }
+  function readVarSlice() {
+    return readSlice(readVarInt());
+  }
+  function readVector() {
+    const count = readVarInt();
+    const vector = [];
+    for (let i = 0; i < count; i++) vector.push(readVarSlice());
+    return vector;
+  }
+  return readVector();
+}
 const range = n => [...Array(n).keys()];
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 680ea92..7c60a1d 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -32,8 +32,9 @@ export class Psbt extends PsbtBase {
     this.inputs.forEach((input, idx) => {
       if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
       if (input.finalScriptWitness) {
-        const decompiled = bscript.decompile(input.finalScriptWitness);
-        if (decompiled) tx.ins[idx].witness = bscript.toStack(decompiled);
+        tx.ins[idx].witness = scriptWitnessToWitnessStack(
+          input.finalScriptWitness,
+        );
       }
     });
     return tx;
@@ -181,10 +182,14 @@ function getFinalScripts(
       finalScriptWitness = witnessStackToScriptWitness(payment.witness!);
     }
     if (p2sh) {
-      finalScriptSig = bscript.compile([p2sh.redeem!.output!]);
+      finalScriptSig = p2sh.input;
     }
   } else {
-    finalScriptSig = payment.input;
+    if (p2sh) {
+      finalScriptSig = p2sh.input;
+    } else {
+      finalScriptSig = payment.input;
+    }
   }
   return {
     finalScriptSig,
@@ -192,6 +197,23 @@ function getFinalScripts(
   };
 }
 
+function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] {
+  const p2ms = payments.p2ms({ output: script });
+  // for each pubkey in order of p2ms script
+  return p2ms
+    .pubkeys!.map(pk => {
+      // filter partialSig array by pubkey being equal
+      return (
+        partialSig.filter(ps => {
+          return ps.pubkey.equals(pk);
+        })[0] || {}
+      ).signature;
+      // Any pubkey without a match will return undefined
+      // this last filter removes all the undefined items in the array.
+    })
+    .filter(v => !!v);
+}
+
 function getPayment(
   script: Buffer,
   scriptType: string,
@@ -200,9 +222,10 @@ function getPayment(
   let payment: payments.Payment;
   switch (scriptType) {
     case 'multisig':
+      const sigs = getSortedSigs(script, partialSig);
       payment = payments.p2ms({
         output: script,
-        signatures: partialSig.map(ps => ps.signature),
+        signatures: sigs,
       });
       break;
     case 'pubkey':
@@ -343,7 +366,7 @@ const getHashForSig = (
       checkWitnessScript(inputIndex, _script, input.witnessScript);
       hash = unsignedTx.hashForWitnessV0(
         inputIndex,
-        _script,
+        input.witnessScript,
         input.witnessUtxo.value,
         sighashType,
       );
@@ -446,11 +469,11 @@ function getScriptFromInput(
     if (input.witnessScript) {
       res.script = input.witnessScript;
     } else if (input.redeemScript) {
-      res.script = payments.p2pkh({
+      res.script = payments.p2wpkh({
         hash: input.redeemScript.slice(2),
       }).output!;
     } else {
-      res.script = payments.p2pkh({
+      res.script = payments.p2wpkh({
         hash: input.witnessUtxo.script.slice(2),
       }).output!;
     }
@@ -494,4 +517,32 @@ function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
   return buffer;
 }
 
+function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
+  let offset = 0;
+
+  function readSlice(n: number): Buffer {
+    offset += n;
+    return buffer.slice(offset - n, offset);
+  }
+
+  function readVarInt(): number {
+    const vi = varuint.decode(buffer, offset);
+    offset += varuint.decode.bytes;
+    return vi;
+  }
+
+  function readVarSlice(): Buffer {
+    return readSlice(readVarInt());
+  }
+
+  function readVector(): Buffer[] {
+    const count = readVarInt();
+    const vector: Buffer[] = [];
+    for (let i = 0; i < count; i++) vector.push(readVarSlice());
+    return vector;
+  }
+
+  return readVector();
+}
+
 const range = (n: number): number[] => [...Array(n).keys()];

From 1fc2e146ea461feb26fb3872f02b26dd91ff7e18 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 3 Jul 2019 16:40:37 +0700
Subject: [PATCH 033/111] Test BIP174 invalid test cases

---
 test/fixtures/psbt.json | 76 +++++++++++++++++++++++++++++++++++++++++
 test/psbt.js            | 10 ++++++
 2 files changed, 86 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 371a8b7..b2104e8 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -1,4 +1,80 @@
 {
+  "bip174": {
+    "invalid": [
+      {
+        "errorMessage": "Format Error: Invalid Magic Number",
+        "psbt": "AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA=="
+      },
+      {
+        "errorMessage": "Format Error: Unexpected End of PSBT",
+        "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA=="
+      },
+      {
+        "errorMessage": "Format Error: Transaction ScriptSigs are not empty",
+        "psbt": "cHNidP8BAP0KAQIAAAACqwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QAAAAAakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpL+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAA="
+      },
+      {
+        "errorMessage": "Format Error: Only one UNSIGNED_TX allowed",
+        "psbt": "cHNidP8AAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA=="
+      },
+      {
+        "errorMessage": "Format Error: Keys must be unique for each input: input index 0 key 00",
+        "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQA/AgAAAAH//////////////////////////////////////////wAAAAAA/////wEAAAAAAAAAAANqAQAAAAAAAAAA"
+      },
+      {
+        "errorMessage": "Format Error: Invalid global key: 0001",
+        "psbt": "cHNidP8CAAFVAgAAAAEnmiMjpd+1H8RfIg+liw/BPh4zQnkqhdfjbNYzO1y8OQAAAAAA/////wGgWuoLAAAAABl2qRT/6cAGEJfMO2NvLLBGD6T8Qn0rRYisAAAAAAABASCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"
+      },
+      {
+        "errorMessage": "Format Error: Invalid input key: 0100",
+        "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAIBACCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"
+      },
+      {
+        "errorMessage": "Format Error: invalid pubkey in key 0x0203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd",
+        "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIQIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYwQwIgBCS1jv+qppThVZ6lyTu/1KiQZCJAVc3wcLZ3FGlELQcCH1yOsP6mUW1guKyzOtZO3mDoeFv7OqlLmb34YVHbmpoBAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GIQPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvVKuIgYDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYQtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA=="
+      },
+      {
+        "errorMessage": "Format Error: Invalid input key: 0400",
+        "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQIEACIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"
+      },
+      {
+        "errorMessage": "Format Error: Invalid input key: 0500",
+        "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoECBQBHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"
+      },
+      {
+        "errorMessage": "Format Error: invalid pubkey in key 0x0603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd",
+        "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriEGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb0QtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA=="
+      },
+      {
+        "errorMessage": "Format Error: Invalid input key: 0000",
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAIAALsCAAAAAarXOTEBi9JfhK5AC2iEi+CdtwbqwqwYKYur7nGrZW+LAAAAAEhHMEQCIFj2/HxqM+GzFUjUgcgmwBW9MBNarULNZ3kNq2bSrSQ7AiBKHO0mBMZzW2OT5bQWkd14sA8MWUL7n3UYVvqpOBV9ugH+////AoDw+gIAAAAAF6kUD7lGNCFpa4LIM68kHHjBfdveSTSH0PIKJwEAAAAXqRQpynT4oI+BmZQoGFyXtdhS5AY/YYdlAAAAAQfaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
+      },
+      {
+        "errorMessage": "Format Error: Invalid input key: 0700",
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAACBwDaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
+      },
+      {
+        "errorMessage": "Format Error: Invalid input key: 0800",
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAggA2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
+      },
+      {
+        "errorMessage": "Format Error: invalid pubkey in key 0x0203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca587",
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIQIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1PtnuylhxDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
+      },
+      {
+        "errorMessage": "Format Error: Invalid input key: 0300",
+        "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"
+      },
+      {
+        "errorMessage": "Format Error: Invalid output key: 0000",
+        "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"
+      },
+      {
+        "errorMessage": "Format Error: Unexpected End of PSBT",
+        "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"
+      }
+    ]
+  },
   "signInput": {
     "checks": [
       {
diff --git a/test/psbt.js b/test/psbt.js
index 8584bd6..b26e5cb 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -7,6 +7,16 @@ const Psbt = require('..').Psbt
 const fixtures = require('./fixtures/psbt')
 
 describe(`Psbt`, () => {
+  describe('BIP174 Test Vectors', () => {
+    fixtures.bip174.invalid.forEach(f => {
+      it(`Invalid: "${f.errorMessage}"`, () => {    
+        assert.throws(() => {
+          Psbt.fromBase64(f.psbt)
+        }, {message: f.errorMessage})
+      })
+    })
+  })
+
   describe('signInput', () => {
     fixtures.signInput.checks.forEach(f => {
       it(f.description, () => {

From c24a6e1ad31ebcf06f286912e2a7f7939d71b65f Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 3 Jul 2019 17:01:47 +0700
Subject: [PATCH 034/111] Include test case number in test output

---
 test/psbt.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/psbt.js b/test/psbt.js
index b26e5cb..c75bce8 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -8,8 +8,8 @@ const fixtures = require('./fixtures/psbt')
 
 describe(`Psbt`, () => {
   describe('BIP174 Test Vectors', () => {
-    fixtures.bip174.invalid.forEach(f => {
-      it(`Invalid: "${f.errorMessage}"`, () => {    
+    fixtures.bip174.invalid.forEach((f, i) => {
+      it(`Invalid #${i + 1}: "${f.errorMessage}"`, () => {
         assert.throws(() => {
           Psbt.fromBase64(f.psbt)
         }, {message: f.errorMessage})

From 2662e46987313e88b17d1f1e1d2590511204294c Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 3 Jul 2019 17:02:02 +0700
Subject: [PATCH 035/111] Test BIP174 valid test cases

---
 test/fixtures/psbt.json | 8 ++++++++
 test/psbt.js            | 8 ++++++++
 2 files changed, 16 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index b2104e8..451fe91 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -73,6 +73,14 @@
         "errorMessage": "Format Error: Unexpected End of PSBT",
         "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"
       }
+    ],
+    "valid": [
+      "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
+      "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA",
+      "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA==",
+      "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=",
+      "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=",
+      "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA="
     ]
   },
   "signInput": {
diff --git a/test/psbt.js b/test/psbt.js
index c75bce8..6ae59cd 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -15,6 +15,14 @@ describe(`Psbt`, () => {
         }, {message: f.errorMessage})
       })
     })
+
+    fixtures.bip174.valid.forEach((psbt, i) => {
+      it(`Valid #${i + 1}`, () => {
+        assert.doesNotThrow(() => {
+          Psbt.fromBase64(psbt)
+        })
+      })
+    })
   })
 
   describe('signInput', () => {

From 336c76bfda711b9171110abcbc6d933e94d114ab Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 3 Jul 2019 17:20:27 +0700
Subject: [PATCH 036/111] Add descriptions to invalid test cases from BIP174
 spec

---
 test/fixtures/psbt.json | 18 ++++++++++++++++++
 test/psbt.js            |  4 ++--
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 451fe91..a758eb2 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -2,74 +2,92 @@
   "bip174": {
     "invalid": [
       {
+        "description": "Network transaction, not PSBT format",
         "errorMessage": "Format Error: Invalid Magic Number",
         "psbt": "AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA=="
       },
       {
+        "description": "PSBT missing outputs",
         "errorMessage": "Format Error: Unexpected End of PSBT",
         "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA=="
       },
       {
+        "description": "PSBT where one input has a filled scriptSig in the unsigned tx",
         "errorMessage": "Format Error: Transaction ScriptSigs are not empty",
         "psbt": "cHNidP8BAP0KAQIAAAACqwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QAAAAAakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpL+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAA="
       },
       {
+        "description": "PSBT where inputs and outputs are provided but without an unsigned tx",
         "errorMessage": "Format Error: Only one UNSIGNED_TX allowed",
         "psbt": "cHNidP8AAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA=="
       },
       {
+        "description": "PSBT with duplicate keys in an input",
         "errorMessage": "Format Error: Keys must be unique for each input: input index 0 key 00",
         "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQA/AgAAAAH//////////////////////////////////////////wAAAAAA/////wEAAAAAAAAAAANqAQAAAAAAAAAA"
       },
       {
+        "description": "PSBT With invalid global transaction typed key",
         "errorMessage": "Format Error: Invalid global key: 0001",
         "psbt": "cHNidP8CAAFVAgAAAAEnmiMjpd+1H8RfIg+liw/BPh4zQnkqhdfjbNYzO1y8OQAAAAAA/////wGgWuoLAAAAABl2qRT/6cAGEJfMO2NvLLBGD6T8Qn0rRYisAAAAAAABASCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"
       },
       {
+        "description": "PSBT With invalid input witness utxo typed key",
         "errorMessage": "Format Error: Invalid input key: 0100",
         "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAIBACCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"
       },
       {
+        "description": "PSBT With invalid pubkey length for input partial signature typed key",
         "errorMessage": "Format Error: invalid pubkey in key 0x0203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd",
         "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIQIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYwQwIgBCS1jv+qppThVZ6lyTu/1KiQZCJAVc3wcLZ3FGlELQcCH1yOsP6mUW1guKyzOtZO3mDoeFv7OqlLmb34YVHbmpoBAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GIQPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvVKuIgYDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYQtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA=="
       },
       {
+        "description": "PSBT With invalid redeemscript typed key",
         "errorMessage": "Format Error: Invalid input key: 0400",
         "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQIEACIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"
       },
       {
+        "description": "PSBT With invalid witnessscript typed key",
         "errorMessage": "Format Error: Invalid input key: 0500",
         "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoECBQBHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"
       },
       {
+        "description": "PSBT With invalid bip32 typed key",
         "errorMessage": "Format Error: invalid pubkey in key 0x0603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd",
         "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriEGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb0QtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA=="
       },
       {
+        "description": "PSBT With invalid non-witness utxo typed key",
         "errorMessage": "Format Error: Invalid input key: 0000",
         "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAIAALsCAAAAAarXOTEBi9JfhK5AC2iEi+CdtwbqwqwYKYur7nGrZW+LAAAAAEhHMEQCIFj2/HxqM+GzFUjUgcgmwBW9MBNarULNZ3kNq2bSrSQ7AiBKHO0mBMZzW2OT5bQWkd14sA8MWUL7n3UYVvqpOBV9ugH+////AoDw+gIAAAAAF6kUD7lGNCFpa4LIM68kHHjBfdveSTSH0PIKJwEAAAAXqRQpynT4oI+BmZQoGFyXtdhS5AY/YYdlAAAAAQfaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
       },
       {
+        "description": "PSBT With invalid final scriptsig typed key",
         "errorMessage": "Format Error: Invalid input key: 0700",
         "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAACBwDaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
       },
       {
+        "description": "PSBT With invalid final script witness typed key",
         "errorMessage": "Format Error: Invalid input key: 0800",
         "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAggA2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
       },
       {
+        "description": "PSBT With invalid pubkey in output BIP 32 derivation paths typed key",
         "errorMessage": "Format Error: invalid pubkey in key 0x0203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca587",
         "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIQIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1PtnuylhxDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
       },
       {
+        "description": "PSBT With invalid input sighash type typed key",
         "errorMessage": "Format Error: Invalid input key: 0300",
         "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"
       },
       {
+        "description": "PSBT With invalid output redeemScript typed key",
         "errorMessage": "Format Error: Invalid output key: 0000",
         "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"
       },
       {
+        "description": "PSBT With invalid output witnessScript typed key",
         "errorMessage": "Format Error: Unexpected End of PSBT",
         "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"
       }
diff --git a/test/psbt.js b/test/psbt.js
index 6ae59cd..f0d8846 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -8,8 +8,8 @@ const fixtures = require('./fixtures/psbt')
 
 describe(`Psbt`, () => {
   describe('BIP174 Test Vectors', () => {
-    fixtures.bip174.invalid.forEach((f, i) => {
-      it(`Invalid #${i + 1}: "${f.errorMessage}"`, () => {
+    fixtures.bip174.invalid.forEach(f => {
+      it(`Invalid: ${f.description}`, () => {
         assert.throws(() => {
           Psbt.fromBase64(f.psbt)
         }, {message: f.errorMessage})

From 54e2e55ef73247b6b489430dd15e015cd7312620 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 3 Jul 2019 17:24:43 +0700
Subject: [PATCH 037/111] Add descriptions to valid test cases from BIP174 spec

---
 test/fixtures/psbt.json | 30 ++++++++++++++++++++++++------
 test/psbt.js            |  6 +++---
 2 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index a758eb2..3eaed01 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -93,12 +93,30 @@
       }
     ],
     "valid": [
-      "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
-      "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA",
-      "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA==",
-      "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=",
-      "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=",
-      "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA="
+      {
+        "description": "PSBT with one P2PKH input. Outputs are empty",
+        "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA"
+      },
+      {
+        "description": "PSBT with one P2PKH input and one P2SH-P2WPKH input. First input is signed and finalized. Outputs are empty",
+        "psbt": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA"
+      },
+      {
+        "description": "PSBT with one P2PKH input which has a non-final scriptSig and has a sighash type specified. Outputs are empty",
+        "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA=="
+      },
+      {
+        "description": "PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled.",
+        "psbt": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="
+      },
+      {
+        "description": "PSBT with one P2SH-P2WSH input of a 2-of-2 multisig, redeemScript, witnessScript, and keypaths are available. Contains one signature.",
+        "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA="
+      },
+      {
+        "description": "PSBT with unknown types in the inputs.",
+        "psbt": "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA="
+      }
     ]
   },
   "signInput": {
diff --git a/test/psbt.js b/test/psbt.js
index f0d8846..ea9131f 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -16,10 +16,10 @@ describe(`Psbt`, () => {
       })
     })
 
-    fixtures.bip174.valid.forEach((psbt, i) => {
-      it(`Valid #${i + 1}`, () => {
+    fixtures.bip174.valid.forEach(f => {
+      it(`Valid: ${f.description}`, () => {
         assert.doesNotThrow(() => {
-          Psbt.fromBase64(psbt)
+          Psbt.fromBase64(f.psbt)
         })
       })
     })

From a876698d15be214a5a6424446e553151473887a2 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 3 Jul 2019 17:38:09 +0700
Subject: [PATCH 038/111] Test BIP174 signer check test cases

---
 test/fixtures/psbt.json | 26 ++++++++++++++++++++++++++
 test/psbt.js            | 10 ++++++++++
 2 files changed, 36 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 3eaed01..d57894a 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -117,6 +117,32 @@
         "description": "PSBT with unknown types in the inputs.",
         "psbt": "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA="
       }
+    ],
+    "failSignChecks": [
+      {
+        "description": "A Witness UTXO is provided for a non-witness input",
+        "errorMessage": "Segwit input needs witnessScript if not P2WPKH",
+        "psbt": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEBItPf9QUAAAAAGXapFNSO0xELlAFMsRS9Mtb00GbcdCVriKwAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=",
+        "inputToCheck": 0
+      },
+      {
+        "description": "redeemScript with non-witness UTXO does not match the scriptPubKey",
+        "errorMessage": "Redeem script for input #0 doesn't match the scriptPubKey in the prevout",
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq8iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=",
+        "inputToCheck": 0
+      },
+      {
+        "description": "redeemScript with witness UTXO does not match the scriptPubKey",
+        "errorMessage": "Redeem script for input #1 doesn't match the scriptPubKey in the prevout",
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQABBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=",
+        "inputToCheck": 1
+      },
+      {
+        "description": "witnessScript with witness UTXO does not match the redeemScript",
+        "errorMessage": "Witness script for input #1 doesn't match the scriptPubKey in the prevout",
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSrSIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=",
+        "inputToCheck": 1
+      }
     ]
   },
   "signInput": {
diff --git a/test/psbt.js b/test/psbt.js
index ea9131f..e99e592 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -23,6 +23,16 @@ describe(`Psbt`, () => {
         })
       })
     })
+
+    fixtures.bip174.failSignChecks.forEach(f => {
+      const keyPair = ECPair.makeRandom()
+      it(`Fails Signer checks: ${f.description}`, () => {
+        const psbt =  Psbt.fromBase64(f.psbt)
+        assert.throws(() => {
+          psbt.signInput(f.inputToCheck, keyPair)
+        }, {message: f.errorMessage})
+      })
+    })
   })
 
   describe('signInput', () => {

From df9008bae70981109201d4a565bb6a47de406c53 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 4 Jul 2019 10:57:23 +0900
Subject: [PATCH 039/111] Update bip174

---
 package-lock.json | 6 +++---
 package.json      | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 4195bf2..0eb81d0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -200,9 +200,9 @@
       }
     },
     "bip174": {
-      "version": "0.0.10",
-      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.10.tgz",
-      "integrity": "sha512-gFtSEMayg7HPKGnIQcEx5CqD/qHWuMlxLJ/+VV4k4Q2mcA0rY040JbNpFuCGVI6rJYv211f0NA7nkU4xkPX4nQ=="
+      "version": "0.0.11",
+      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.11.tgz",
+      "integrity": "sha512-/WuRQOwgT0cGKDrNROOhOHpdtXyeuBJMqEgsBUdlCeFRLXIA8g5pHzbFmVm/v+OcYdenHfwzW/hOEf1xJOI27Q=="
     },
     "bip32": {
       "version": "2.0.3",
diff --git a/package.json b/package.json
index 1346928..54d94f4 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
-    "bip174": "0.0.10",
+    "bip174": "0.0.11",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",

From 2b8e8001bc6d8ebbafa83067119e4b229f5d8ef9 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 4 Jul 2019 11:26:23 +0900
Subject: [PATCH 040/111] Support Addresses for outputs

---
 src/psbt.js     | 28 ++++++++++++++++------------
 ts_src/psbt.ts  | 48 +++++++++++++++++++++++++++++++++++++++++++++---
 types/psbt.d.ts | 12 ++++++++++--
 3 files changed, 71 insertions(+), 17 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 3cbd899..d47bdb2 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -2,16 +2,17 @@
 Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
 const utils_1 = require('bip174/src/lib/utils');
+const address_1 = require('./address');
 const crypto_1 = require('./crypto');
+const networks_1 = require('./networks');
 const payments = require('./payments');
 const bscript = require('./script');
 const transaction_1 = require('./transaction');
 const varuint = require('varuint-bitcoin');
 class Psbt extends bip174_1.Psbt {
-  // protected __TX: Transaction;
-  constructor(network) {
+  constructor(opts = {}) {
     super();
-    this.network = network;
+    this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     // // TODO: figure out a way to use a Transaction Object instead of a Buffer
     // // TODO: Caching, since .toBuffer() calls every time we get is lame.
     // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
@@ -24,6 +25,15 @@ class Psbt extends bip174_1.Psbt {
     //   }
     // });
   }
+  addOutput(outputData, allowNoInput = false, transactionOutputAdder) {
+    const { address } = outputData;
+    if (typeof address === 'string') {
+      const { network } = this.opts;
+      const script = address_1.toOutputScript(address, network);
+      outputData = Object.assign(outputData, { script });
+    }
+    return super.addOutput(outputData, allowNoInput, transactionOutputAdder);
+  }
   extractTransaction() {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
     const tx = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
@@ -107,15 +117,9 @@ class Psbt extends bip174_1.Psbt {
   }
 }
 exports.Psbt = Psbt;
-//
-//
-//
-//
-// Helper functions
-//
-//
-//
-//
+const DEFAULT_OPTS = {
+  network: networks_1.bitcoin,
+};
 function isFinalized(input) {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 7c60a1d..eff576b 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,9 +1,14 @@
 import { Psbt as PsbtBase } from 'bip174';
-import { PartialSig, PsbtInput } from 'bip174/src/lib/interfaces';
+import {
+  PartialSig,
+  PsbtInput,
+  TransactionOutput,
+} from 'bip174/src/lib/interfaces';
 import { checkForInput } from 'bip174/src/lib/utils';
+import { toOutputScript } from './address';
 import { hash160 } from './crypto';
 import { Signer, SignerAsync } from './ecpair';
-import { Network } from './networks';
+import { bitcoin as btcNetwork, Network } from './networks';
 import * as payments from './payments';
 import * as bscript from './script';
 import { Transaction } from './transaction';
@@ -11,8 +16,10 @@ const varuint = require('varuint-bitcoin');
 
 export class Psbt extends PsbtBase {
   // protected __TX: Transaction;
-  constructor(public network?: Network) {
+  private opts: PsbtOpts;
+  constructor(opts: PsbtOptsOptional = {}) {
     super();
+    this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     // // TODO: figure out a way to use a Transaction Object instead of a Buffer
     // // TODO: Caching, since .toBuffer() calls every time we get is lame.
     // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
@@ -26,6 +33,29 @@ export class Psbt extends PsbtBase {
     // });
   }
 
+  addOutput(outputData: TransactionOutput, allowNoInput?: boolean): this;
+  addOutput<T>(
+    outputData: T,
+    allowNoInput?: boolean,
+    transactionOutputAdder?: (output: T, txBuffer: Buffer) => Buffer,
+  ): this;
+  addOutput<T>(
+    outputData: T | TransactionOutput,
+    allowNoInput: boolean = false,
+    transactionOutputAdder?: (
+      output: T | TransactionOutput,
+      txBuffer: Buffer,
+    ) => Buffer,
+  ): this {
+    const { address } = outputData as any;
+    if (typeof address === 'string') {
+      const { network } = this.opts;
+      const script = toOutputScript(address, network);
+      outputData = Object.assign(outputData, { script });
+    }
+    return super.addOutput(outputData, allowNoInput, transactionOutputAdder);
+  }
+
   extractTransaction(): Transaction {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
     const tx = Transaction.fromBuffer(this.globalMap.unsignedTx!);
@@ -134,6 +164,18 @@ export class Psbt extends PsbtBase {
 //
 //
 
+interface PsbtOptsOptional {
+  network?: Network;
+}
+
+interface PsbtOpts {
+  network: Network;
+}
+
+const DEFAULT_OPTS = {
+  network: btcNetwork,
+};
+
 function isFinalized(input: PsbtInput): boolean {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 26ae0a7..6275ee3 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -1,10 +1,14 @@
+/// <reference types="node" />
 import { Psbt as PsbtBase } from 'bip174';
+import { TransactionOutput } from 'bip174/src/lib/interfaces';
 import { Signer, SignerAsync } from './ecpair';
 import { Network } from './networks';
 import { Transaction } from './transaction';
 export declare class Psbt extends PsbtBase {
-    network?: Network | undefined;
-    constructor(network?: Network | undefined);
+    private opts;
+    constructor(opts?: PsbtOptsOptional);
+    addOutput(outputData: TransactionOutput, allowNoInput?: boolean): this;
+    addOutput<T>(outputData: T, allowNoInput?: boolean, transactionOutputAdder?: (output: T, txBuffer: Buffer) => Buffer): this;
     extractTransaction(): Transaction;
     finalizeAllInputs(): {
         result: boolean;
@@ -14,3 +18,7 @@ export declare class Psbt extends PsbtBase {
     signInput(inputIndex: number, keyPair: Signer): Psbt;
     signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>;
 }
+interface PsbtOptsOptional {
+    network?: Network;
+}
+export {};

From b28c96d228ccfa9aadca3b53fd96b4a6e8bac4bd Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 4 Jul 2019 12:03:48 +0900
Subject: [PATCH 041/111] Set to version 2 by default

---
 package-lock.json | 6 +++---
 package.json      | 2 +-
 src/psbt.js       | 1 +
 ts_src/psbt.ts    | 1 +
 4 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 0eb81d0..a6aa1a4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -200,9 +200,9 @@
       }
     },
     "bip174": {
-      "version": "0.0.11",
-      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.11.tgz",
-      "integrity": "sha512-/WuRQOwgT0cGKDrNROOhOHpdtXyeuBJMqEgsBUdlCeFRLXIA8g5pHzbFmVm/v+OcYdenHfwzW/hOEf1xJOI27Q=="
+      "version": "0.0.12",
+      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.12.tgz",
+      "integrity": "sha512-rYVuFTSCROv8iI07BhEddap+iXiFz2aiYEUSQR8rqv7JKWbMO2QQqCJMvr2E0df8wu5ySysd/CupIP4eG4ukqQ=="
     },
     "bip32": {
       "version": "2.0.3",
diff --git a/package.json b/package.json
index 54d94f4..93d2188 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
-    "bip174": "0.0.11",
+    "bip174": "0.0.12",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",
diff --git a/src/psbt.js b/src/psbt.js
index d47bdb2..0157e9f 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -12,6 +12,7 @@ const varuint = require('varuint-bitcoin');
 class Psbt extends bip174_1.Psbt {
   constructor(opts = {}) {
     super();
+    this.setVersion(2);
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     // // TODO: figure out a way to use a Transaction Object instead of a Buffer
     // // TODO: Caching, since .toBuffer() calls every time we get is lame.
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index eff576b..0752df2 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -19,6 +19,7 @@ export class Psbt extends PsbtBase {
   private opts: PsbtOpts;
   constructor(opts: PsbtOptsOptional = {}) {
     super();
+    this.setVersion(2);
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     // // TODO: figure out a way to use a Transaction Object instead of a Buffer
     // // TODO: Caching, since .toBuffer() calls every time we get is lame.

From f7e726a8ebecb3e0cafe573f2b95258c12d3e4b7 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 4 Jul 2019 13:33:08 +0900
Subject: [PATCH 042/111] Add TX cache and addInput addOutput

---
 package-lock.json |   6 +--
 package.json      |   2 +-
 src/psbt.js       |  91 +++++++++++++++++++++++++++++-----
 ts_src/psbt.ts    | 121 +++++++++++++++++++++++++++++++++++-----------
 types/psbt.d.ts   |   9 ++--
 5 files changed, 181 insertions(+), 48 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index a6aa1a4..768c79f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -200,9 +200,9 @@
       }
     },
     "bip174": {
-      "version": "0.0.12",
-      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.12.tgz",
-      "integrity": "sha512-rYVuFTSCROv8iI07BhEddap+iXiFz2aiYEUSQR8rqv7JKWbMO2QQqCJMvr2E0df8wu5ySysd/CupIP4eG4ukqQ=="
+      "version": "0.0.13",
+      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.13.tgz",
+      "integrity": "sha512-jWP7Lb27Nmbk6gaZKhJZOyk5LqRWs9z+R2xzgu3W8/iZXIIP2kcR6fh5lNg7GGOiWUaqanWC9rjrDVrBVbXKww=="
     },
     "bip32": {
       "version": "2.0.3",
diff --git a/package.json b/package.json
index 93d2188..1fadfcb 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
-    "bip174": "0.0.12",
+    "bip174": "0.0.13",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",
diff --git a/src/psbt.js b/src/psbt.js
index 0157e9f..df1d491 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -3,6 +3,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
 const utils_1 = require('bip174/src/lib/utils');
 const address_1 = require('./address');
+const bufferutils_1 = require('./bufferutils');
 const crypto_1 = require('./crypto');
 const networks_1 = require('./networks');
 const payments = require('./payments');
@@ -12,28 +13,92 @@ const varuint = require('varuint-bitcoin');
 class Psbt extends bip174_1.Psbt {
   constructor(opts = {}) {
     super();
+    // set defaults
     this.setVersion(2);
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
-    // // TODO: figure out a way to use a Transaction Object instead of a Buffer
-    // // TODO: Caching, since .toBuffer() calls every time we get is lame.
-    // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
-    // delete this.globalMap.unsignedTx;
-    // Object.defineProperty(this.globalMap, 'unsignedTx', {
-    //   enumerable: true,
-    //   writable: false,
-    //   get(): Buffer {
-    //     return this.__TX.toBuffer();
-    //   }
-    // });
+    this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
+    // set cache
+    const self = this;
+    delete this.globalMap.unsignedTx;
+    Object.defineProperty(this.globalMap, 'unsignedTx', {
+      enumerable: true,
+      get() {
+        if (self.__TX_BUF_CACHE !== undefined) {
+          return self.__TX_BUF_CACHE;
+        } else {
+          self.__TX_BUF_CACHE = self.__TX.toBuffer();
+          return self.__TX_BUF_CACHE;
+        }
+      },
+      set(data) {
+        self.__TX_BUF_CACHE = data;
+      },
+    });
+    // Make data hidden when enumerating
+    const dpew = (obj, attr, enumerable, writable) =>
+      Object.defineProperty(obj, attr, {
+        enumerable,
+        writable,
+      });
+    dpew(this, '__TX', false, false);
+    dpew(this, '__TX_BUF_CACHE', false, true);
+    dpew(this, 'opts', false, true);
   }
-  addOutput(outputData, allowNoInput = false, transactionOutputAdder) {
+  addInput(inputData) {
+    const self = this;
+    const inputAdder = (_inputData, txBuf) => {
+      if (
+        !txBuf ||
+        _inputData.hash === undefined ||
+        _inputData.index === undefined ||
+        (!Buffer.isBuffer(_inputData.hash) &&
+          typeof _inputData.hash !== 'string') ||
+        typeof _inputData.index !== 'number'
+      ) {
+        throw new Error('Error adding input.');
+      }
+      const prevHash = Buffer.isBuffer(_inputData.hash)
+        ? _inputData.hash
+        : bufferutils_1.reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
+      self.__TX.ins.push({
+        hash: prevHash,
+        index: _inputData.index,
+        script: Buffer.alloc(0),
+        sequence:
+          _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE,
+        witness: [],
+      });
+      console.log(self.__TX);
+      return self.__TX.toBuffer();
+    };
+    return super.addInput(inputData, inputAdder);
+  }
+  addOutput(outputData) {
     const { address } = outputData;
     if (typeof address === 'string') {
       const { network } = this.opts;
       const script = address_1.toOutputScript(address, network);
       outputData = Object.assign(outputData, { script });
     }
-    return super.addOutput(outputData, allowNoInput, transactionOutputAdder);
+    const self = this;
+    const outputAdder = (_outputData, txBuf) => {
+      if (
+        !txBuf ||
+        _outputData.script === undefined ||
+        _outputData.value === undefined ||
+        !Buffer.isBuffer(_outputData.script) ||
+        typeof _outputData.value !== 'number'
+      ) {
+        throw new Error('Error adding output.');
+      }
+      self.__TX.outs.push({
+        script: _outputData.script,
+        value: _outputData.value,
+      });
+      console.log(self.__TX);
+      return self.__TX.toBuffer();
+    };
+    return super.addOutput(outputData, true, outputAdder);
   }
   extractTransaction() {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 0752df2..58988f2 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -2,10 +2,12 @@ import { Psbt as PsbtBase } from 'bip174';
 import {
   PartialSig,
   PsbtInput,
+  TransactionInput,
   TransactionOutput,
 } from 'bip174/src/lib/interfaces';
 import { checkForInput } from 'bip174/src/lib/utils';
 import { toOutputScript } from './address';
+import { reverseBuffer } from './bufferutils';
 import { hash160 } from './crypto';
 import { Signer, SignerAsync } from './ecpair';
 import { bitcoin as btcNetwork, Network } from './networks';
@@ -15,46 +17,111 @@ import { Transaction } from './transaction';
 const varuint = require('varuint-bitcoin');
 
 export class Psbt extends PsbtBase {
-  // protected __TX: Transaction;
+  private __TX: Transaction;
+  private __TX_BUF_CACHE?: Buffer;
   private opts: PsbtOpts;
   constructor(opts: PsbtOptsOptional = {}) {
     super();
+    // set defaults
     this.setVersion(2);
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
-    // // TODO: figure out a way to use a Transaction Object instead of a Buffer
-    // // TODO: Caching, since .toBuffer() calls every time we get is lame.
-    // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
-    // delete this.globalMap.unsignedTx;
-    // Object.defineProperty(this.globalMap, 'unsignedTx', {
-    //   enumerable: true,
-    //   writable: false,
-    //   get(): Buffer {
-    //     return this.__TX.toBuffer();
-    //   }
-    // });
+    this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
+
+    // set cache
+    const self = this;
+    delete this.globalMap.unsignedTx;
+    Object.defineProperty(this.globalMap, 'unsignedTx', {
+      enumerable: true,
+      get(): Buffer {
+        if (self.__TX_BUF_CACHE !== undefined) {
+          return self.__TX_BUF_CACHE;
+        } else {
+          self.__TX_BUF_CACHE = self.__TX.toBuffer();
+          return self.__TX_BUF_CACHE;
+        }
+      },
+      set(data: Buffer): void {
+        self.__TX_BUF_CACHE = data;
+      },
+    });
+
+    // Make data hidden when enumerating
+    const dpew = (
+      obj: any,
+      attr: string,
+      enumerable: boolean,
+      writable: boolean,
+    ): any =>
+      Object.defineProperty(obj, attr, {
+        enumerable,
+        writable,
+      });
+    dpew(this, '__TX', false, false);
+    dpew(this, '__TX_BUF_CACHE', false, true);
+    dpew(this, 'opts', false, true);
   }
 
-  addOutput(outputData: TransactionOutput, allowNoInput?: boolean): this;
-  addOutput<T>(
-    outputData: T,
-    allowNoInput?: boolean,
-    transactionOutputAdder?: (output: T, txBuffer: Buffer) => Buffer,
-  ): this;
-  addOutput<T>(
-    outputData: T | TransactionOutput,
-    allowNoInput: boolean = false,
-    transactionOutputAdder?: (
-      output: T | TransactionOutput,
-      txBuffer: Buffer,
-    ) => Buffer,
-  ): this {
+  addInput(inputData: TransactionInput): this {
+    const self = this;
+    const inputAdder = (
+      _inputData: TransactionInput,
+      txBuf: Buffer,
+    ): Buffer => {
+      if (
+        !txBuf ||
+        (_inputData as any).hash === undefined ||
+        (_inputData as any).index === undefined ||
+        (!Buffer.isBuffer((_inputData as any).hash) &&
+          typeof (_inputData as any).hash !== 'string') ||
+        typeof (_inputData as any).index !== 'number'
+      ) {
+        throw new Error('Error adding input.');
+      }
+      const prevHash = Buffer.isBuffer(_inputData.hash)
+        ? _inputData.hash
+        : reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
+      self.__TX.ins.push({
+        hash: prevHash,
+        index: _inputData.index,
+        script: Buffer.alloc(0),
+        sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE,
+        witness: [],
+      });
+      console.log(self.__TX);
+      return self.__TX.toBuffer();
+    };
+    return super.addInput(inputData, inputAdder);
+  }
+
+  addOutput(outputData: TransactionOutput): this {
     const { address } = outputData as any;
     if (typeof address === 'string') {
       const { network } = this.opts;
       const script = toOutputScript(address, network);
       outputData = Object.assign(outputData, { script });
     }
-    return super.addOutput(outputData, allowNoInput, transactionOutputAdder);
+    const self = this;
+    const outputAdder = (
+      _outputData: TransactionOutput,
+      txBuf: Buffer,
+    ): Buffer => {
+      if (
+        !txBuf ||
+        (_outputData as any).script === undefined ||
+        (_outputData as any).value === undefined ||
+        !Buffer.isBuffer((_outputData as any).script) ||
+        typeof (_outputData as any).value !== 'number'
+      ) {
+        throw new Error('Error adding output.');
+      }
+      self.__TX.outs.push({
+        script: (_outputData as any).script!,
+        value: _outputData.value,
+      });
+      console.log(self.__TX);
+      return self.__TX.toBuffer();
+    };
+    return super.addOutput(outputData, true, outputAdder);
   }
 
   extractTransaction(): Transaction {
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 6275ee3..a530cd0 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -1,14 +1,15 @@
-/// <reference types="node" />
 import { Psbt as PsbtBase } from 'bip174';
-import { TransactionOutput } from 'bip174/src/lib/interfaces';
+import { TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces';
 import { Signer, SignerAsync } from './ecpair';
 import { Network } from './networks';
 import { Transaction } from './transaction';
 export declare class Psbt extends PsbtBase {
+    private __TX;
+    private __TX_BUF_CACHE?;
     private opts;
     constructor(opts?: PsbtOptsOptional);
-    addOutput(outputData: TransactionOutput, allowNoInput?: boolean): this;
-    addOutput<T>(outputData: T, allowNoInput?: boolean, transactionOutputAdder?: (output: T, txBuffer: Buffer) => Buffer): this;
+    addInput(inputData: TransactionInput): this;
+    addOutput(outputData: TransactionOutput): this;
     extractTransaction(): Transaction;
     finalizeAllInputs(): {
         result: boolean;

From 539c88596a4ae9f4ee4fac0a6adb3db6bb60708a Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 4 Jul 2019 13:42:34 +0900
Subject: [PATCH 043/111] Add version and locktime setters

---
 src/psbt.js     | 12 +++++++++++-
 ts_src/psbt.ts  | 14 +++++++++++++-
 types/psbt.d.ts |  2 ++
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index df1d491..ec91bd9 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -14,9 +14,9 @@ class Psbt extends bip174_1.Psbt {
   constructor(opts = {}) {
     super();
     // set defaults
-    this.setVersion(2);
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
+    this.setVersion(2);
     // set cache
     const self = this;
     delete this.globalMap.unsignedTx;
@@ -44,6 +44,16 @@ class Psbt extends bip174_1.Psbt {
     dpew(this, '__TX_BUF_CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
+  setVersion(version) {
+    this.__TX.version = version;
+    this.__TX_BUF_CACHE = undefined;
+    return this;
+  }
+  setLocktime(locktime) {
+    this.__TX.locktime = locktime;
+    this.__TX_BUF_CACHE = undefined;
+    return this;
+  }
   addInput(inputData) {
     const self = this;
     const inputAdder = (_inputData, txBuf) => {
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 58988f2..46e9487 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -23,9 +23,9 @@ export class Psbt extends PsbtBase {
   constructor(opts: PsbtOptsOptional = {}) {
     super();
     // set defaults
-    this.setVersion(2);
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
+    this.setVersion(2);
 
     // set cache
     const self = this;
@@ -61,6 +61,18 @@ export class Psbt extends PsbtBase {
     dpew(this, 'opts', false, true);
   }
 
+  setVersion(version: number): this {
+    this.__TX.version = version;
+    this.__TX_BUF_CACHE = undefined;
+    return this;
+  }
+
+  setLocktime(locktime: number): this {
+    this.__TX.locktime = locktime;
+    this.__TX_BUF_CACHE = undefined;
+    return this;
+  }
+
   addInput(inputData: TransactionInput): this {
     const self = this;
     const inputAdder = (
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index a530cd0..213c31b 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -8,6 +8,8 @@ export declare class Psbt extends PsbtBase {
     private __TX_BUF_CACHE?;
     private opts;
     constructor(opts?: PsbtOptsOptional);
+    setVersion(version: number): this;
+    setLocktime(locktime: number): this;
     addInput(inputData: TransactionInput): this;
     addOutput(outputData: TransactionOutput): this;
     extractTransaction(): Transaction;

From b98761a28329897ed400f8686bf87fa5c8489851 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 4 Jul 2019 13:52:48 +0900
Subject: [PATCH 044/111] Promise fixes for async

---
 src/psbt.js    | 31 +++++++++++++++++--------------
 ts_src/psbt.ts | 37 +++++++++++++++++++++----------------
 2 files changed, 38 insertions(+), 30 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index ec91bd9..b9ed061 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -175,20 +175,23 @@ class Psbt extends bip174_1.Psbt {
     return this.addPartialSigToInput(inputIndex, partialSig);
   }
   signInputAsync(inputIndex, keyPair) {
-    if (!keyPair || !keyPair.publicKey)
-      throw new Error('Need Signer to sign input');
-    const { hash, sighashType } = getHashAndSighashType(
-      this.inputs,
-      inputIndex,
-      keyPair.publicKey,
-      this.globalMap.unsignedTx,
-    );
-    return keyPair.sign(hash).then(signature => {
-      const partialSig = {
-        pubkey: keyPair.publicKey,
-        signature: bscript.signature.encode(signature, sighashType),
-      };
-      this.addPartialSigToInput(inputIndex, partialSig);
+    return new Promise((resolve, reject) => {
+      if (!keyPair || !keyPair.publicKey)
+        return reject(new Error('Need Signer to sign input'));
+      const { hash, sighashType } = getHashAndSighashType(
+        this.inputs,
+        inputIndex,
+        keyPair.publicKey,
+        this.globalMap.unsignedTx,
+      );
+      Promise.resolve(keyPair.sign(hash)).then(signature => {
+        const partialSig = {
+          pubkey: keyPair.publicKey,
+          signature: bscript.signature.encode(signature, sighashType),
+        };
+        this.addPartialSigToInput(inputIndex, partialSig);
+        resolve();
+      });
     });
   }
 }
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 46e9487..2c36130 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -214,23 +214,28 @@ export class Psbt extends PsbtBase {
   }
 
   signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void> {
-    if (!keyPair || !keyPair.publicKey)
-      throw new Error('Need Signer to sign input');
-    const { hash, sighashType } = getHashAndSighashType(
-      this.inputs,
-      inputIndex,
-      keyPair.publicKey,
-      this.globalMap.unsignedTx!,
+    return new Promise(
+      (resolve, reject): void => {
+        if (!keyPair || !keyPair.publicKey)
+          return reject(new Error('Need Signer to sign input'));
+        const { hash, sighashType } = getHashAndSighashType(
+          this.inputs,
+          inputIndex,
+          keyPair.publicKey,
+          this.globalMap.unsignedTx!,
+        );
+
+        Promise.resolve(keyPair.sign(hash)).then(signature => {
+          const partialSig = {
+            pubkey: keyPair.publicKey,
+            signature: bscript.signature.encode(signature, sighashType),
+          };
+
+          this.addPartialSigToInput(inputIndex, partialSig);
+          resolve();
+        });
+      },
     );
-
-    return keyPair.sign(hash).then(signature => {
-      const partialSig = {
-        pubkey: keyPair.publicKey,
-        signature: bscript.signature.encode(signature, sighashType),
-      };
-
-      this.addPartialSigToInput(inputIndex, partialSig);
-    });
   }
 }
 

From 5b5daf84dd7deeffa3f933fa03a29001fcc6270d Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 4 Jul 2019 14:33:36 +0900
Subject: [PATCH 045/111] Remove unnecessary extra Transaction Buffer parsing

---
 src/psbt.js     | 57 ++++++++++++++++++++++++++++++++--------
 ts_src/psbt.ts  | 70 ++++++++++++++++++++++++++++++++++++++++---------
 types/psbt.d.ts |  5 +++-
 3 files changed, 108 insertions(+), 24 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index b9ed061..4a6306f 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -11,6 +11,39 @@ const bscript = require('./script');
 const transaction_1 = require('./transaction');
 const varuint = require('varuint-bitcoin');
 class Psbt extends bip174_1.Psbt {
+  static fromTransaction(txBuf) {
+    const tx = transaction_1.Transaction.fromBuffer(txBuf);
+    const psbt = new this();
+    psbt.__TX = tx;
+    let inputCount = tx.ins.length;
+    let outputCount = tx.outs.length;
+    while (inputCount > 0) {
+      psbt.inputs.push({
+        keyVals: [],
+      });
+      inputCount--;
+    }
+    while (outputCount > 0) {
+      psbt.outputs.push({
+        keyVals: [],
+      });
+      outputCount--;
+    }
+    return psbt;
+  }
+  static fromBuffer(buffer) {
+    let tx;
+    const txCountGetter = txBuf => {
+      tx = transaction_1.Transaction.fromBuffer(txBuf);
+      return {
+        inputCount: tx.ins.length,
+        outputCount: tx.outs.length,
+      };
+    };
+    const psbt = super.fromBuffer(buffer, txCountGetter);
+    psbt.__TX = tx;
+    return psbt;
+  }
   constructor(opts = {}) {
     super();
     // set defaults
@@ -40,7 +73,7 @@ class Psbt extends bip174_1.Psbt {
         enumerable,
         writable,
       });
-    dpew(this, '__TX', false, false);
+    dpew(this, '__TX', false, true);
     dpew(this, '__TX_BUF_CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
@@ -112,7 +145,7 @@ class Psbt extends bip174_1.Psbt {
   }
   extractTransaction() {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
-    const tx = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
+    const tx = this.__TX.clone();
     this.inputs.forEach((input, idx) => {
       if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
       if (input.finalScriptWitness) {
@@ -138,7 +171,7 @@ class Psbt extends bip174_1.Psbt {
     const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
-      this.globalMap.unsignedTx,
+      this.__TX,
     );
     if (!script) return false;
     const scriptType = classifyScript(script);
@@ -166,7 +199,7 @@ class Psbt extends bip174_1.Psbt {
       this.inputs,
       inputIndex,
       keyPair.publicKey,
-      this.globalMap.unsignedTx,
+      this.__TX,
     );
     const partialSig = {
       pubkey: keyPair.publicKey,
@@ -182,7 +215,7 @@ class Psbt extends bip174_1.Psbt {
         this.inputs,
         inputIndex,
         keyPair.publicKey,
-        this.globalMap.unsignedTx,
+        this.__TX,
       );
       Promise.resolve(keyPair.sign(hash)).then(signature => {
         const partialSig = {
@@ -202,9 +235,13 @@ const DEFAULT_OPTS = {
 function isFinalized(input) {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
-function getHashAndSighashType(inputs, inputIndex, pubkey, txBuf) {
+function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx) {
   const input = utils_1.checkForInput(inputs, inputIndex);
-  const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf);
+  const { hash, sighashType, script } = getHashForSig(
+    inputIndex,
+    input,
+    unsignedTx,
+  );
   checkScriptForPubkey(pubkey, script);
   return {
     hash,
@@ -322,8 +359,7 @@ function checkScriptForPubkey(pubkey, script) {
     );
   }
 }
-const getHashForSig = (inputIndex, input, txBuf) => {
-  const unsignedTx = transaction_1.Transaction.fromBuffer(txBuf);
+const getHashForSig = (inputIndex, input, unsignedTx) => {
   const sighashType =
     input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
   let hash;
@@ -442,7 +478,7 @@ const classifyScript = script => {
   if (isP2PK(script)) return 'pubkey';
   return 'nonstandard';
 };
-function getScriptFromInput(inputIndex, input, _unsignedTx) {
+function getScriptFromInput(inputIndex, input, unsignedTx) {
   const res = {
     script: null,
     isSegwit: false,
@@ -454,7 +490,6 @@ function getScriptFromInput(inputIndex, input, _unsignedTx) {
       res.isP2SH = true;
       res.script = input.redeemScript;
     } else {
-      const unsignedTx = transaction_1.Transaction.fromBuffer(_unsignedTx);
       const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
         input.nonWitnessUtxo,
       );
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 2c36130..0aee319 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -17,6 +17,50 @@ import { Transaction } from './transaction';
 const varuint = require('varuint-bitcoin');
 
 export class Psbt extends PsbtBase {
+  static fromTransaction<T extends typeof PsbtBase>(
+    this: T,
+    txBuf: Buffer,
+  ): InstanceType<T> {
+    const tx = Transaction.fromBuffer(txBuf);
+    const psbt = new this() as Psbt;
+    psbt.__TX = tx;
+    let inputCount = tx.ins.length;
+    let outputCount = tx.outs.length;
+    while (inputCount > 0) {
+      psbt.inputs.push({
+        keyVals: [],
+      });
+      inputCount--;
+    }
+    while (outputCount > 0) {
+      psbt.outputs.push({
+        keyVals: [],
+      });
+      outputCount--;
+    }
+    return psbt as InstanceType<T>;
+  }
+  static fromBuffer<T extends typeof PsbtBase>(
+    this: T,
+    buffer: Buffer,
+  ): InstanceType<T> {
+    let tx: Transaction | undefined;
+    const txCountGetter = (
+      txBuf: Buffer,
+    ): {
+      inputCount: number;
+      outputCount: number;
+    } => {
+      tx = Transaction.fromBuffer(txBuf);
+      return {
+        inputCount: tx.ins.length,
+        outputCount: tx.outs.length,
+      };
+    };
+    const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt;
+    psbt.__TX = tx!;
+    return psbt as InstanceType<T>;
+  }
   private __TX: Transaction;
   private __TX_BUF_CACHE?: Buffer;
   private opts: PsbtOpts;
@@ -56,7 +100,7 @@ export class Psbt extends PsbtBase {
         enumerable,
         writable,
       });
-    dpew(this, '__TX', false, false);
+    dpew(this, '__TX', false, true);
     dpew(this, '__TX_BUF_CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
@@ -138,7 +182,7 @@ export class Psbt extends PsbtBase {
 
   extractTransaction(): Transaction {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
-    const tx = Transaction.fromBuffer(this.globalMap.unsignedTx!);
+    const tx = this.__TX.clone();
     this.inputs.forEach((input, idx) => {
       if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
       if (input.finalScriptWitness) {
@@ -169,7 +213,7 @@ export class Psbt extends PsbtBase {
     const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
-      this.globalMap.unsignedTx!,
+      this.__TX,
     );
     if (!script) return false;
 
@@ -195,14 +239,14 @@ export class Psbt extends PsbtBase {
     return true;
   }
 
-  signInput(inputIndex: number, keyPair: Signer): Psbt {
+  signInput(inputIndex: number, keyPair: Signer): this {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
     const { hash, sighashType } = getHashAndSighashType(
       this.inputs,
       inputIndex,
       keyPair.publicKey,
-      this.globalMap.unsignedTx!,
+      this.__TX,
     );
 
     const partialSig = {
@@ -222,7 +266,7 @@ export class Psbt extends PsbtBase {
           this.inputs,
           inputIndex,
           keyPair.publicKey,
-          this.globalMap.unsignedTx!,
+          this.__TX,
         );
 
         Promise.resolve(keyPair.sign(hash)).then(signature => {
@@ -269,13 +313,17 @@ function getHashAndSighashType(
   inputs: PsbtInput[],
   inputIndex: number,
   pubkey: Buffer,
-  txBuf: Buffer,
+  unsignedTx: Transaction,
 ): {
   hash: Buffer;
   sighashType: number;
 } {
   const input = checkForInput(inputs, inputIndex);
-  const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf);
+  const { hash, sighashType, script } = getHashForSig(
+    inputIndex,
+    input,
+    unsignedTx,
+  );
   checkScriptForPubkey(pubkey, script);
   return {
     hash,
@@ -424,9 +472,8 @@ interface HashForSigData {
 const getHashForSig = (
   inputIndex: number,
   input: PsbtInput,
-  txBuf: Buffer,
+  unsignedTx: Transaction,
 ): HashForSigData => {
-  const unsignedTx = Transaction.fromBuffer(txBuf);
   const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
   let hash: Buffer;
   let script: Buffer;
@@ -571,7 +618,7 @@ interface GetScriptReturn {
 function getScriptFromInput(
   inputIndex: number,
   input: PsbtInput,
-  _unsignedTx: Buffer,
+  unsignedTx: Transaction,
 ): GetScriptReturn {
   const res: GetScriptReturn = {
     script: null,
@@ -584,7 +631,6 @@ function getScriptFromInput(
       res.isP2SH = true;
       res.script = input.redeemScript;
     } else {
-      const unsignedTx = Transaction.fromBuffer(_unsignedTx);
       const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
       const prevoutIndex = unsignedTx.ins[inputIndex].index;
       res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 213c31b..a203854 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -1,9 +1,12 @@
+/// <reference types="node" />
 import { Psbt as PsbtBase } from 'bip174';
 import { TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces';
 import { Signer, SignerAsync } from './ecpair';
 import { Network } from './networks';
 import { Transaction } from './transaction';
 export declare class Psbt extends PsbtBase {
+    static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>;
+    static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>;
     private __TX;
     private __TX_BUF_CACHE?;
     private opts;
@@ -18,7 +21,7 @@ export declare class Psbt extends PsbtBase {
         inputResults: boolean[];
     };
     finalizeInput(inputIndex: number): boolean;
-    signInput(inputIndex: number, keyPair: Signer): Psbt;
+    signInput(inputIndex: number, keyPair: Signer): this;
     signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>;
 }
 interface PsbtOptsOptional {

From 3e7f490093303417e069a8a629103a774be97fad Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 4 Jul 2019 14:45:50 +0900
Subject: [PATCH 046/111] Check for input empty on parse

---
 src/psbt.js    | 14 ++++++++++++++
 ts_src/psbt.ts | 15 +++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/src/psbt.js b/src/psbt.js
index 4a6306f..a85a23a 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -13,6 +13,7 @@ const varuint = require('varuint-bitcoin');
 class Psbt extends bip174_1.Psbt {
   static fromTransaction(txBuf) {
     const tx = transaction_1.Transaction.fromBuffer(txBuf);
+    checkTxEmpty(tx);
     const psbt = new this();
     psbt.__TX = tx;
     let inputCount = tx.ins.length;
@@ -35,6 +36,7 @@ class Psbt extends bip174_1.Psbt {
     let tx;
     const txCountGetter = txBuf => {
       tx = transaction_1.Transaction.fromBuffer(txBuf);
+      checkTxEmpty(tx);
       return {
         inputCount: tx.ins.length,
         outputCount: tx.outs.length,
@@ -564,3 +566,15 @@ function scriptWitnessToWitnessStack(buffer) {
   return readVector();
 }
 const range = n => [...Array(n).keys()];
+function checkTxEmpty(tx) {
+  const isEmpty = tx.ins.every(
+    input =>
+      input.script &&
+      input.script.length === 0 &&
+      input.witness &&
+      input.witness.length === 0,
+  );
+  if (!isEmpty) {
+    throw new Error('Format Error: Transaction ScriptSigs are not empty');
+  }
+}
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 0aee319..14f0c08 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -22,6 +22,7 @@ export class Psbt extends PsbtBase {
     txBuf: Buffer,
   ): InstanceType<T> {
     const tx = Transaction.fromBuffer(txBuf);
+    checkTxEmpty(tx);
     const psbt = new this() as Psbt;
     psbt.__TX = tx;
     let inputCount = tx.ins.length;
@@ -52,6 +53,7 @@ export class Psbt extends PsbtBase {
       outputCount: number;
     } => {
       tx = Transaction.fromBuffer(txBuf);
+      checkTxEmpty(tx);
       return {
         inputCount: tx.ins.length,
         outputCount: tx.outs.length,
@@ -719,3 +721,16 @@ function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
 }
 
 const range = (n: number): number[] => [...Array(n).keys()];
+
+function checkTxEmpty(tx: Transaction): void {
+  const isEmpty = tx.ins.every(
+    input =>
+      input.script &&
+      input.script.length === 0 &&
+      input.witness &&
+      input.witness.length === 0,
+  );
+  if (!isEmpty) {
+    throw new Error('Format Error: Transaction ScriptSigs are not empty');
+  }
+}

From 45bd5b47516dad0a1f1ba6d37fba9cf1218f2a8f Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 4 Jul 2019 17:35:39 +0900
Subject: [PATCH 047/111] Check for signatures, add setSequence

---
 src/psbt.js     | 60 ++++++++++++++++++++++++++++++++++++++++++++++
 ts_src/psbt.ts  | 63 +++++++++++++++++++++++++++++++++++++++++++++++++
 types/psbt.d.ts |  1 +
 3 files changed, 124 insertions(+)

diff --git a/src/psbt.js b/src/psbt.js
index a85a23a..eaa8d62 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -80,16 +80,31 @@ class Psbt extends bip174_1.Psbt {
     dpew(this, 'opts', false, true);
   }
   setVersion(version) {
+    check32Bit(version);
+    checkInputsForPartialSig(this.inputs, 'setVersion');
     this.__TX.version = version;
     this.__TX_BUF_CACHE = undefined;
     return this;
   }
   setLocktime(locktime) {
+    check32Bit(locktime);
+    checkInputsForPartialSig(this.inputs, 'setLocktime');
     this.__TX.locktime = locktime;
     this.__TX_BUF_CACHE = undefined;
     return this;
   }
+  setSequence(inputIndex, sequence) {
+    check32Bit(sequence);
+    checkInputsForPartialSig(this.inputs, 'setSequence');
+    if (this.__TX.ins.length <= inputIndex) {
+      throw new Error('Input index too high');
+    }
+    this.__TX.ins[inputIndex].sequence = sequence;
+    this.__TX_BUF_CACHE = undefined;
+    return this;
+  }
   addInput(inputData) {
+    checkInputsForPartialSig(this.inputs, 'addInput');
     const self = this;
     const inputAdder = (_inputData, txBuf) => {
       if (
@@ -119,6 +134,7 @@ class Psbt extends bip174_1.Psbt {
     return super.addInput(inputData, inputAdder);
   }
   addOutput(outputData) {
+    checkInputsForPartialSig(this.inputs, 'addOutput');
     const { address } = outputData;
     if (typeof address === 'string') {
       const { network } = this.opts;
@@ -578,3 +594,47 @@ function checkTxEmpty(tx) {
     throw new Error('Format Error: Transaction ScriptSigs are not empty');
   }
 }
+function checkInputsForPartialSig(inputs, action) {
+  inputs.forEach(input => {
+    let throws = false;
+    if ((input.partialSig || []).length > 0) {
+      if (input.sighashType !== undefined) {
+        const whitelist = [];
+        const isAnyoneCanPay =
+          input.sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
+        if (isAnyoneCanPay) whitelist.push('addInput');
+        if (!isAnyoneCanPay && action === 'addInput') {
+          throws = true;
+        }
+        const hashType = input.sighashType & 0x1f;
+        switch (hashType) {
+          case transaction_1.Transaction.SIGHASH_ALL:
+            break;
+          case transaction_1.Transaction.SIGHASH_SINGLE:
+          case transaction_1.Transaction.SIGHASH_NONE:
+            whitelist.push('addOutput');
+            whitelist.push('setSequence');
+            break;
+        }
+        if (whitelist.indexOf(action) === -1) {
+          throws = true;
+        }
+      } else {
+        throws = true;
+      }
+    }
+    if (throws) {
+      throw new Error('Can not modify transaction, signatures exist.');
+    }
+  });
+}
+function check32Bit(num) {
+  if (
+    typeof num !== 'number' ||
+    num !== Math.floor(num) ||
+    num > 0xffffffff ||
+    num < 0
+  ) {
+    throw new Error('Invalid 32 bit integer');
+  }
+}
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 14f0c08..0b019a3 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -108,18 +108,34 @@ export class Psbt extends PsbtBase {
   }
 
   setVersion(version: number): this {
+    check32Bit(version);
+    checkInputsForPartialSig(this.inputs, 'setVersion');
     this.__TX.version = version;
     this.__TX_BUF_CACHE = undefined;
     return this;
   }
 
   setLocktime(locktime: number): this {
+    check32Bit(locktime);
+    checkInputsForPartialSig(this.inputs, 'setLocktime');
     this.__TX.locktime = locktime;
     this.__TX_BUF_CACHE = undefined;
     return this;
   }
 
+  setSequence(inputIndex: number, sequence: number): this {
+    check32Bit(sequence);
+    checkInputsForPartialSig(this.inputs, 'setSequence');
+    if (this.__TX.ins.length <= inputIndex) {
+      throw new Error('Input index too high');
+    }
+    this.__TX.ins[inputIndex].sequence = sequence;
+    this.__TX_BUF_CACHE = undefined;
+    return this;
+  }
+
   addInput(inputData: TransactionInput): this {
+    checkInputsForPartialSig(this.inputs, 'addInput');
     const self = this;
     const inputAdder = (
       _inputData: TransactionInput,
@@ -152,6 +168,7 @@ export class Psbt extends PsbtBase {
   }
 
   addOutput(outputData: TransactionOutput): this {
+    checkInputsForPartialSig(this.inputs, 'addOutput');
     const { address } = outputData as any;
     if (typeof address === 'string') {
       const { network } = this.opts;
@@ -734,3 +751,49 @@ function checkTxEmpty(tx: Transaction): void {
     throw new Error('Format Error: Transaction ScriptSigs are not empty');
   }
 }
+
+function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
+  inputs.forEach(input => {
+    let throws = false;
+    if ((input.partialSig || []).length > 0) {
+      if (input.sighashType !== undefined) {
+        const whitelist: string[] = [];
+        const isAnyoneCanPay =
+          input.sighashType & Transaction.SIGHASH_ANYONECANPAY;
+        if (isAnyoneCanPay) whitelist.push('addInput');
+        if (!isAnyoneCanPay && action === 'addInput') {
+          throws = true;
+        }
+        const hashType = input.sighashType & 0x1f;
+        switch (hashType) {
+          case Transaction.SIGHASH_ALL:
+            break;
+          case Transaction.SIGHASH_SINGLE:
+          case Transaction.SIGHASH_NONE:
+            whitelist.push('addOutput');
+            whitelist.push('setSequence');
+            break;
+        }
+        if (whitelist.indexOf(action) === -1) {
+          throws = true;
+        }
+      } else {
+        throws = true;
+      }
+    }
+    if (throws) {
+      throw new Error('Can not modify transaction, signatures exist.');
+    }
+  });
+}
+
+function check32Bit(num: number): void {
+  if (
+    typeof num !== 'number' ||
+    num !== Math.floor(num) ||
+    num > 0xffffffff ||
+    num < 0
+  ) {
+    throw new Error('Invalid 32 bit integer');
+  }
+}
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index a203854..1aa6f28 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -13,6 +13,7 @@ export declare class Psbt extends PsbtBase {
     constructor(opts?: PsbtOptsOptional);
     setVersion(version: number): this;
     setLocktime(locktime: number): this;
+    setSequence(inputIndex: number, sequence: number): this;
     addInput(inputData: TransactionInput): this;
     addOutput(outputData: TransactionOutput): this;
     extractTransaction(): Transaction;

From 2501fc92bcd8f2467e715c36b1be554186b35cee Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 13:47:18 +0700
Subject: [PATCH 048/111] Test BIP174 creator check test cases

---
 test/fixtures/psbt.json | 25 +++++++++++++++++++++++++
 test/psbt.js            | 14 ++++++++++++++
 2 files changed, 39 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index d57894a..49d4f1f 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -143,6 +143,31 @@
         "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSrSIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=",
         "inputToCheck": 1
       }
+    ],
+    "creator": [
+      {
+        "inputs": [
+          {
+            "hash": "75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858",
+            "index": 0
+          },
+          {
+            "hash": "1dea7cd05979072a3578cab271c02244ea8a090bbb46aa680a65ecd027048d83",
+            "index": 1
+          }
+        ],
+        "outputs": [
+          {
+            "script": "0014d85c2b71d0060b09c9886aeb815e50991dda124d",
+            "value": 149990000
+          },
+          {
+            "script": "001400aea9a2e5f0f876a588df5546e8742d1d87008f",
+            "value": 100000000
+          }
+        ],
+        "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA="
+      }
     ]
   },
   "signInput": {
diff --git a/test/psbt.js b/test/psbt.js
index e99e592..98f407b 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -33,6 +33,20 @@ describe(`Psbt`, () => {
         }, {message: f.errorMessage})
       })
     })
+
+    fixtures.bip174.creator.forEach(f => {
+      it('Creates expected PSBT', () => {
+        const psbt = new Psbt()
+        for (const input of f.inputs) {
+          psbt.addInput(input)
+        }
+        for (const output of f.outputs) {
+          const script = Buffer.from(output.script, 'hex');
+          psbt.addOutput({...output, script})
+        }
+        assert.strictEqual(psbt.toBase64(), f.result)
+      })
+    })
   })
 
   describe('signInput', () => {

From 275618ed43e1eb533c37fdbc2967cf1bfc916b93 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 13:52:19 +0700
Subject: [PATCH 049/111] Remove console.log

---
 src/psbt.js    | 2 --
 ts_src/psbt.ts | 2 --
 2 files changed, 4 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index eaa8d62..b2bbeda 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -128,7 +128,6 @@ class Psbt extends bip174_1.Psbt {
           _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE,
         witness: [],
       });
-      console.log(self.__TX);
       return self.__TX.toBuffer();
     };
     return super.addInput(inputData, inputAdder);
@@ -156,7 +155,6 @@ class Psbt extends bip174_1.Psbt {
         script: _outputData.script,
         value: _outputData.value,
       });
-      console.log(self.__TX);
       return self.__TX.toBuffer();
     };
     return super.addOutput(outputData, true, outputAdder);
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 0b019a3..fea9a67 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -161,7 +161,6 @@ export class Psbt extends PsbtBase {
         sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE,
         witness: [],
       });
-      console.log(self.__TX);
       return self.__TX.toBuffer();
     };
     return super.addInput(inputData, inputAdder);
@@ -193,7 +192,6 @@ export class Psbt extends PsbtBase {
         script: (_outputData as any).script!,
         value: _outputData.value,
       });
-      console.log(self.__TX);
       return self.__TX.toBuffer();
     };
     return super.addOutput(outputData, true, outputAdder);

From a32d1c3eac4e4fa6e2a3ddfcfa59008b98e206e6 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 14:53:51 +0700
Subject: [PATCH 050/111] Test BIP174 updater check test cases

---
 test/fixtures/psbt.json | 76 +++++++++++++++++++++++++++++++++++++++++
 test/psbt.js            | 51 +++++++++++++++++++++++++++
 2 files changed, 127 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 49d4f1f..ab9a470 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -168,6 +168,82 @@
         ],
         "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA="
       }
+    ],
+    "updater": [
+      {
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=",
+        "inputData": [
+          {
+            "nonWitnessUtxo": "0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000",
+            "redeemScript": "5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae",
+            "bip32Derivation": [
+              {
+                "masterFingerprint": "d90c6a4f",
+                "pubkey": "029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f",
+                "path": "m/0'/0'/0'"
+              },
+              {
+                "masterFingerprint": "d90c6a4f",
+                "pubkey": "02dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7",
+                "path": "m/0'/0'/1'"
+              }
+            ]
+          },
+          {
+            "witnessUtxo": {
+              "script": "a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887",
+              "value": 200000000
+            },
+            "redeemScript": "00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903",
+            "witnessScript": "522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae",
+            "bip32Derivation": [
+              {
+                "masterFingerprint": "d90c6a4f",
+                "pubkey": "023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73",
+                "path": "m/0'/0'/3'"
+              },
+              {
+                "masterFingerprint": "d90c6a4f",
+                "pubkey": "03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc",
+                "path": "m/0'/0'/2'"
+              }
+            ]
+          }
+        ],
+        "outputData": [
+          {
+            "bip32Derivation": [
+              {
+                "masterFingerprint": "d90c6a4f",
+                "pubkey": "03a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58771",
+                "path": "m/0'/0'/4'"
+              }
+            ]
+          },
+          {
+            "bip32Derivation": [
+              {
+                "masterFingerprint": "d90c6a4f",
+                "pubkey": "027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b50051096",
+                "path": "m/0'/0'/5'"
+              }
+            ]
+          }
+        ],
+        "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA=="
+      },
+      {
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==",
+        "inputData": [
+          {
+            "sighashType": 1
+          },
+          {
+            "sighashType": 1
+          }
+        ],
+        "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
+      }
     ]
   },
   "signInput": {
diff --git a/test/psbt.js b/test/psbt.js
index 98f407b..cd80d73 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -6,6 +6,27 @@ const Psbt = require('..').Psbt
 
 const fixtures = require('./fixtures/psbt')
 
+const upperCaseFirstLetter = str => str.replace(/^./, s => s.toUpperCase())
+
+const b = hex => Buffer.from(hex, 'hex');
+
+const initBuffers = (attr, data) => {
+  if ([
+    'nonWitnessUtxo',
+    'redeemScript',
+    'witnessScript'
+  ].includes(attr)) {
+    data = b(data)
+  } else if (attr === 'bip32Derivation') {
+    data.masterFingerprint = b(data.masterFingerprint)
+    data.pubkey = b(data.pubkey)
+  }  else if (attr === 'witnessUtxo') {
+    data.script = b(data.script)
+  }
+
+  return data
+};
+
 describe(`Psbt`, () => {
   describe('BIP174 Test Vectors', () => {
     fixtures.bip174.invalid.forEach(f => {
@@ -47,6 +68,36 @@ describe(`Psbt`, () => {
         assert.strictEqual(psbt.toBase64(), f.result)
       })
     })
+
+    fixtures.bip174.updater.forEach(f => {
+      it('Updates PSBT to the expected result', () => {
+        const psbt = Psbt.fromBase64(f.psbt)
+
+        for (const inputOrOutput of ['input', 'output']) {
+          const fixtureData = f[`${inputOrOutput}Data`]
+          if (fixtureData) {
+            for (const [i, data] of fixtureData.entries()) {
+              const attrs = Object.keys(data)
+              for (const attr of attrs) {
+                const upperAttr = upperCaseFirstLetter(attr)
+                let adder = psbt[`add${upperAttr}To${upperCaseFirstLetter(inputOrOutput)}`]
+                if (adder !== undefined) {
+                  adder = adder.bind(psbt)
+                  const arg = data[attr]
+                  if (Array.isArray(arg)) {
+                    arg.forEach(a => adder(i, initBuffers(attr, a)))
+                  } else {
+                    adder(i, initBuffers(attr, arg))
+                  }
+                }
+              }
+            }
+          }
+        }
+
+        assert.strictEqual(psbt.toBase64(), f.result)
+      })
+    })
   })
 
   describe('signInput', () => {

From 30815e9e8fe5a9a74129800013a1193848b854b0 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 15:32:16 +0700
Subject: [PATCH 051/111] Test BIP174 signer test cases

---
 test/fixtures/psbt.json | 30 ++++++++++++++++++++++++++++++
 test/psbt.js            | 14 ++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index ab9a470..3e65598 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -244,6 +244,36 @@
         ],
         "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
       }
+    ],
+    "signer": [
+      {
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
+        "keys": [
+          {
+            "inputToSign": 0,
+            "WIF": "cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr"
+          },
+          {
+            "inputToSign": 1,
+            "WIF": "cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d"
+          }
+        ],
+        "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEBAwQBAAAAAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA=="
+      },
+      {
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
+        "keys": [
+          {
+            "inputToSign": 0,
+            "WIF": "cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au"
+          },
+          {
+            "inputToSign": 1,
+            "WIF": "cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE"
+          }
+        ],
+        "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
+      }
     ]
   },
   "signInput": {
diff --git a/test/psbt.js b/test/psbt.js
index cd80d73..00f50ba 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -3,6 +3,7 @@ const assert = require('assert')
 
 const ECPair = require('../src/ecpair')
 const Psbt = require('..').Psbt
+const NETWORKS = require('../src/networks')
 
 const fixtures = require('./fixtures/psbt')
 
@@ -100,6 +101,19 @@ describe(`Psbt`, () => {
     })
   })
 
+  fixtures.bip174.signer.forEach(f => {
+    it('Signs PSBT to the expected result', () => {
+      const psbt =  Psbt.fromBase64(f.psbt)
+
+      f.keys.forEach(({inputToSign, WIF}) => {
+        const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet);
+        psbt.signInput(inputToSign, keyPair);
+      })
+
+      assert.strictEqual(psbt.toBase64(), f.result)
+    })
+  })
+
   describe('signInput', () => {
     fixtures.signInput.checks.forEach(f => {
       it(f.description, () => {

From 4e55ab0f2057b796441f938efbfcd78f02028ef8 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 16:11:19 +0700
Subject: [PATCH 052/111] Test BIP174 combiner test cases

---
 test/fixtures/psbt.json |  9 +++++++++
 test/psbt.js            | 14 ++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 3e65598..5928ff6 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -274,6 +274,15 @@
         ],
         "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
       }
+    ],
+    "combiner": [
+      {
+        "psbts": [
+          "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEBAwQBAAAAAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==",
+          "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
+        ],
+        "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
+      }
     ]
   },
   "signInput": {
diff --git a/test/psbt.js b/test/psbt.js
index 00f50ba..f417079 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -114,6 +114,20 @@ describe(`Psbt`, () => {
     })
   })
 
+  fixtures.bip174.combiner.forEach(f => {
+    it('Combines two PSBTs to the expected result', () => {
+      const psbts =  f.psbts.map(psbt => Psbt.fromBase64(psbt))
+
+      psbts[0].combine(psbts[1])
+
+      // Produces a different Base64 string due to implemetation specific key-value ordering.
+      // That means this test will fail:
+      // assert.strictEqual(psbts[0].toBase64(), f.result)
+      // However, if we compare the actual PSBT properties we can see they are logically identical:
+      assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result))
+    })
+  })
+
   describe('signInput', () => {
     fixtures.signInput.checks.forEach(f => {
       it(f.description, () => {

From a80155dbdbd1ceff2466fb2054792ecc327a1f3a Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 16:16:19 +0700
Subject: [PATCH 053/111] Test BIP174 finalizer test cases

---
 test/fixtures/psbt.json |  6 ++++++
 test/psbt.js            | 10 ++++++++++
 2 files changed, 16 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 5928ff6..5344894 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -283,6 +283,12 @@
         ],
         "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
       }
+    ],
+    "finalizer": [
+      {
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
+        "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA=="
+      }
     ]
   },
   "signInput": {
diff --git a/test/psbt.js b/test/psbt.js
index f417079..213afc5 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -128,6 +128,16 @@ describe(`Psbt`, () => {
     })
   })
 
+  fixtures.bip174.finalizer.forEach(f => {
+    it('Finalizes inputs and gives the expected PSBT', () => {
+      const psbt =  Psbt.fromBase64(f.psbt)
+
+      psbt.finalizeAllInputs()
+
+      assert.strictEqual(psbt.toBase64(), f.result)
+    })
+  })
+
   describe('signInput', () => {
     fixtures.signInput.checks.forEach(f => {
       it(f.description, () => {

From 35cf120c33b64a57c396a6a1132a8d9b825d2078 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 16:17:50 +0700
Subject: [PATCH 054/111] Add extra combiner test case

---
 test/fixtures/psbt.json | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 5344894..37416cf 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -282,6 +282,13 @@
           "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
         ],
         "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
+      },
+      {
+        "psbts": [
+          "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcICQ8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgJDwECAwQFBgcICQoLDA0ODwA=",
+          "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcIEA8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCBAPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgQDwECAwQFBgcICQoLDA0ODwA="
+        ],
+        "result": "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcICQ8BAgMEBQYHCAkKCwwNDg8KDwECAwQFBgcIEA8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PCg8BAgMEBQYHCBAPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgJDwECAwQFBgcICQoLDA0ODwoPAQIDBAUGBwgQDwECAwQFBgcICQoLDA0ODwA="
       }
     ],
     "finalizer": [

From e3efdbdb99e904aba5d888dedced11ead9fddd1f Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 16:23:55 +0700
Subject: [PATCH 055/111] Test BIP174 extractor test cases

---
 test/fixtures/psbt.json |  6 ++++++
 test/psbt.js            | 10 ++++++++++
 2 files changed, 16 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 37416cf..7242b03 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -296,6 +296,12 @@
         "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
         "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA=="
       }
+    ],
+    "extractor": [
+      {
+        "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==",
+        "transaction": "0200000000010258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7500000000da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752aeffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d01000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000"
+      }
     ]
   },
   "signInput": {
diff --git a/test/psbt.js b/test/psbt.js
index 213afc5..5d59a88 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -138,6 +138,16 @@ describe(`Psbt`, () => {
     })
   })
 
+  fixtures.bip174.extractor.forEach(f => {
+    it('Extracts the expected transaction from a PSBT', () => {
+      const psbt =  Psbt.fromBase64(f.psbt)
+
+      const transaction = psbt.extractTransaction().toHex()
+
+      assert.strictEqual(transaction, f.transaction)
+    })
+  })
+
   describe('signInput', () => {
     fixtures.signInput.checks.forEach(f => {
       it(f.description, () => {

From dc23b8cce0b9a59e6f54046c32f8fe323dda363a Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 17:00:01 +0700
Subject: [PATCH 056/111] Test fromTransaction

---
 test/fixtures/psbt.json | 8 +++++++-
 test/psbt.js            | 9 +++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 7242b03..00877f9 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -391,5 +391,11 @@
         }
       }
     ]
-  }
+  },
+  "fromTransaction": [
+    {
+      "transaction": "020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000",
+      "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA="
+    }
+  ]
 }
diff --git a/test/psbt.js b/test/psbt.js
index 5d59a88..290c5e5 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -169,4 +169,13 @@ describe(`Psbt`, () => {
       })
     })
   })
+
+  describe('fromTransaction', () => {
+    fixtures.fromTransaction.forEach(f => {
+      it('Creates the expected PSBT from a transaction buffer', () => {
+        const psbt = Psbt.fromTransaction(Buffer.from(f.transaction, 'hex'))
+        assert.strictEqual(psbt.toBase64(), f.result)
+      })
+    })
+  })
 })

From 09a6c37430c76eb90a583f6e164dc6f98e7daee3 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 17:00:20 +0700
Subject: [PATCH 057/111] Test setVersion

---
 test/psbt.js | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/test/psbt.js b/test/psbt.js
index 290c5e5..0f9a2f8 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -178,4 +178,14 @@ describe(`Psbt`, () => {
       })
     })
   })
+
+  describe('setVersion', () => {
+    it('Sets the version value of the unsigned transaction', () => {
+      const psbt = new Psbt()
+
+      assert.strictEqual(psbt.extractTransaction().version, 2)
+      psbt.setVersion(1)
+      assert.strictEqual(psbt.extractTransaction().version, 1)
+    })
+  })
 })

From 871e5877117de551affeca88f2cbab84f3d3b240 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 17:00:34 +0700
Subject: [PATCH 058/111] Test setLocktime

---
 test/psbt.js | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/test/psbt.js b/test/psbt.js
index 0f9a2f8..d790b63 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -188,4 +188,14 @@ describe(`Psbt`, () => {
       assert.strictEqual(psbt.extractTransaction().version, 1)
     })
   })
+
+  describe('setLocktime', () => {
+    it('Sets the nLockTime value of the unsigned transaction', () => {
+      const psbt = new Psbt()
+
+      assert.strictEqual(psbt.extractTransaction().locktime, 0)
+      psbt.setLocktime(1)
+      assert.strictEqual(psbt.extractTransaction().locktime, 1)
+    })
+  })
 })

From ba5f336e02785560166d3812908264189cca765c Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 4 Jul 2019 17:20:16 +0700
Subject: [PATCH 059/111] Test setSequence

---
 test/psbt.js | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/test/psbt.js b/test/psbt.js
index d790b63..67f3611 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -198,4 +198,30 @@ describe(`Psbt`, () => {
       assert.strictEqual(psbt.extractTransaction().locktime, 1)
     })
   })
+
+  describe('setSequence', () => {
+    it('Sets the sequence number for a given input', () => {
+      const psbt = new Psbt()
+      psbt.addInput({
+        hash: '0000000000000000000000000000000000000000000000000000000000000000',
+        index: 0
+      });
+
+      assert.strictEqual(psbt.__TX.ins[0].sequence, 0xffffffff)
+      psbt.setSequence(0, 0)
+      assert.strictEqual(psbt.__TX.ins[0].sequence, 0)
+    })
+
+    it('throws if input index is too high', () => {
+      const psbt = new Psbt()
+      psbt.addInput({
+        hash: '0000000000000000000000000000000000000000000000000000000000000000',
+        index: 0
+      });
+
+      assert.throws(() => {
+        psbt.setSequence(1, 0)
+      }, {message: 'Input index too high'})
+    })
+  })
 })

From 14eeb309df25c59ebf01f05078036425c33b9a65 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 5 Jul 2019 12:28:04 +0900
Subject: [PATCH 060/111] Add fee checking before extract

---
 src/psbt.js     | 204 ++++++++++++++++++++++++++++++++++++++----------
 ts_src/psbt.ts  | 156 +++++++++++++++++++++++++++++++++++-
 types/psbt.d.ts |  12 ++-
 3 files changed, 324 insertions(+), 48 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index b2bbeda..272c716 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -11,6 +11,45 @@ const bscript = require('./script');
 const transaction_1 = require('./transaction');
 const varuint = require('varuint-bitcoin');
 class Psbt extends bip174_1.Psbt {
+  constructor(opts = {}) {
+    super();
+    this.__NON_WITNESS_UTXO_TX_CACHE = [];
+    this.__NON_WITNESS_UTXO_BUF_CACHE = [];
+    // set defaults
+    this.opts = Object.assign({}, DEFAULT_OPTS, opts);
+    this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
+    this.setVersion(2);
+    // set cache
+    const self = this;
+    delete this.globalMap.unsignedTx;
+    Object.defineProperty(this.globalMap, 'unsignedTx', {
+      enumerable: true,
+      get() {
+        if (self.__TX_BUF_CACHE !== undefined) {
+          return self.__TX_BUF_CACHE;
+        } else {
+          self.__TX_BUF_CACHE = self.__TX.toBuffer();
+          return self.__TX_BUF_CACHE;
+        }
+      },
+      set(data) {
+        self.__TX_BUF_CACHE = data;
+      },
+    });
+    // Make data hidden when enumerating
+    const dpew = (obj, attr, enumerable, writable) =>
+      Object.defineProperty(obj, attr, {
+        enumerable,
+        writable,
+      });
+    dpew(this, '__TX', false, true);
+    dpew(this, '__EXTRACTED_TX', false, true);
+    dpew(this, '__FEE_RATE', false, true);
+    dpew(this, '__TX_BUF_CACHE', false, true);
+    dpew(this, '__NON_WITNESS_UTXO_TX_CACHE', false, true);
+    dpew(this, '__NON_WITNESS_UTXO_BUF_CACHE', false, true);
+    dpew(this, 'opts', false, true);
+  }
   static fromTransaction(txBuf) {
     const tx = transaction_1.Transaction.fromBuffer(txBuf);
     checkTxEmpty(tx);
@@ -46,44 +85,16 @@ class Psbt extends bip174_1.Psbt {
     psbt.__TX = tx;
     return psbt;
   }
-  constructor(opts = {}) {
-    super();
-    // set defaults
-    this.opts = Object.assign({}, DEFAULT_OPTS, opts);
-    this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
-    this.setVersion(2);
-    // set cache
-    const self = this;
-    delete this.globalMap.unsignedTx;
-    Object.defineProperty(this.globalMap, 'unsignedTx', {
-      enumerable: true,
-      get() {
-        if (self.__TX_BUF_CACHE !== undefined) {
-          return self.__TX_BUF_CACHE;
-        } else {
-          self.__TX_BUF_CACHE = self.__TX.toBuffer();
-          return self.__TX_BUF_CACHE;
-        }
-      },
-      set(data) {
-        self.__TX_BUF_CACHE = data;
-      },
-    });
-    // Make data hidden when enumerating
-    const dpew = (obj, attr, enumerable, writable) =>
-      Object.defineProperty(obj, attr, {
-        enumerable,
-        writable,
-      });
-    dpew(this, '__TX', false, true);
-    dpew(this, '__TX_BUF_CACHE', false, true);
-    dpew(this, 'opts', false, true);
+  setMaximumFeeRate(satoshiPerByte) {
+    check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
+    this.opts.maximumFeeRate = satoshiPerByte;
   }
   setVersion(version) {
     check32Bit(version);
     checkInputsForPartialSig(this.inputs, 'setVersion');
     this.__TX.version = version;
     this.__TX_BUF_CACHE = undefined;
+    this.__EXTRACTED_TX = undefined;
     return this;
   }
   setLocktime(locktime) {
@@ -91,6 +102,7 @@ class Psbt extends bip174_1.Psbt {
     checkInputsForPartialSig(this.inputs, 'setLocktime');
     this.__TX.locktime = locktime;
     this.__TX_BUF_CACHE = undefined;
+    this.__EXTRACTED_TX = undefined;
     return this;
   }
   setSequence(inputIndex, sequence) {
@@ -101,6 +113,7 @@ class Psbt extends bip174_1.Psbt {
     }
     this.__TX.ins[inputIndex].sequence = sequence;
     this.__TX_BUF_CACHE = undefined;
+    this.__EXTRACTED_TX = undefined;
     return this;
   }
   addInput(inputData) {
@@ -159,8 +172,29 @@ class Psbt extends bip174_1.Psbt {
     };
     return super.addOutput(outputData, true, outputAdder);
   }
-  extractTransaction() {
+  addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
+    super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
+    const input = this.inputs[inputIndex];
+    addNonWitnessTxCache(this, input, inputIndex);
+    return this;
+  }
+  extractTransaction(disableFeeCheck) {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
+    if (!disableFeeCheck) {
+      const feeRate = this.__FEE_RATE || this.getFeeRate();
+      const vsize = this.__EXTRACTED_TX.virtualSize();
+      const satoshis = feeRate * vsize;
+      if (feeRate >= this.opts.maximumFeeRate) {
+        throw new Error(
+          `Warning: You are paying around ${satoshis / 1e8} in fees, which ` +
+            `is ${feeRate} satoshi per byte for a transaction with a VSize of ` +
+            `${vsize} bytes (segwit counted as 0.25 byte per byte)\n` +
+            `Use setMaximumFeeRate method to raise your threshold, or pass ` +
+            `true to the first arg of extractTransaction.`,
+        );
+      }
+    }
+    if (this.__EXTRACTED_TX) return this.__EXTRACTED_TX;
     const tx = this.__TX.clone();
     this.inputs.forEach((input, idx) => {
       if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
@@ -170,8 +204,51 @@ class Psbt extends bip174_1.Psbt {
         );
       }
     });
+    this.__EXTRACTED_TX = tx;
     return tx;
   }
+  getFeeRate() {
+    if (!this.inputs.every(isFinalized))
+      throw new Error('PSBT must be finalized to calculate fee rate');
+    if (this.__FEE_RATE) return this.__FEE_RATE;
+    let tx;
+    let inputAmount = 0;
+    let mustFinalize = true;
+    if (this.__EXTRACTED_TX) {
+      tx = this.__EXTRACTED_TX;
+      mustFinalize = false;
+    } else {
+      tx = this.__TX.clone();
+    }
+    this.inputs.forEach((input, idx) => {
+      if (mustFinalize && input.finalScriptSig)
+        tx.ins[idx].script = input.finalScriptSig;
+      if (mustFinalize && input.finalScriptWitness) {
+        tx.ins[idx].witness = scriptWitnessToWitnessStack(
+          input.finalScriptWitness,
+        );
+      }
+      if (input.witnessUtxo) {
+        inputAmount += input.witnessUtxo.value;
+      } else if (input.nonWitnessUtxo) {
+        // @ts-ignore
+        if (!this.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
+          addNonWitnessTxCache(this, input, idx);
+        }
+        const vout = this.__TX.ins[idx].index;
+        const out = this.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout];
+        inputAmount += out.value;
+      } else {
+        throw new Error('Missing input value: index #' + idx);
+      }
+    });
+    this.__EXTRACTED_TX = tx;
+    const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0);
+    const fee = inputAmount - outputAmount;
+    const bytes = tx.virtualSize();
+    this.__FEE_RATE = Math.floor(fee / bytes);
+    return this.__FEE_RATE;
+  }
   finalizeAllInputs() {
     const inputResults = range(this.inputs.length).map(idx =>
       this.finalizeInput(idx),
@@ -188,6 +265,7 @@ class Psbt extends bip174_1.Psbt {
       inputIndex,
       input,
       this.__TX,
+      this,
     );
     if (!script) return false;
     const scriptType = classifyScript(script);
@@ -216,6 +294,7 @@ class Psbt extends bip174_1.Psbt {
       inputIndex,
       keyPair.publicKey,
       this.__TX,
+      this,
     );
     const partialSig = {
       pubkey: keyPair.publicKey,
@@ -232,6 +311,7 @@ class Psbt extends bip174_1.Psbt {
         inputIndex,
         keyPair.publicKey,
         this.__TX,
+        this,
       );
       Promise.resolve(keyPair.sign(hash)).then(signature => {
         const partialSig = {
@@ -247,16 +327,50 @@ class Psbt extends bip174_1.Psbt {
 exports.Psbt = Psbt;
 const DEFAULT_OPTS = {
   network: networks_1.bitcoin,
+  maximumFeeRate: 5000,
 };
+function addNonWitnessTxCache(psbt, input, inputIndex) {
+  // @ts-ignore
+  psbt.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo;
+  const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo);
+  // @ts-ignore
+  psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
+  const self = psbt;
+  const selfIndex = inputIndex;
+  delete input.nonWitnessUtxo;
+  Object.defineProperty(input, 'nonWitnessUtxo', {
+    enumerable: true,
+    get() {
+      // @ts-ignore
+      if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) {
+        // @ts-ignore
+        return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+      } else {
+        // @ts-ignore
+        self.__NON_WITNESS_UTXO_BUF_CACHE[
+          selfIndex
+          // @ts-ignore
+        ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer();
+        // @ts-ignore
+        return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+      }
+    },
+    set(data) {
+      // @ts-ignore
+      self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
+    },
+  });
+}
 function isFinalized(input) {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
-function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx) {
+function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx, psbt) {
   const input = utils_1.checkForInput(inputs, inputIndex);
   const { hash, sighashType, script } = getHashForSig(
     inputIndex,
     input,
     unsignedTx,
+    psbt,
   );
   checkScriptForPubkey(pubkey, script);
   return {
@@ -375,15 +489,18 @@ function checkScriptForPubkey(pubkey, script) {
     );
   }
 }
-const getHashForSig = (inputIndex, input, unsignedTx) => {
+const getHashForSig = (inputIndex, input, unsignedTx, psbt) => {
   const sighashType =
     input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
   let hash;
   let script;
   if (input.nonWitnessUtxo) {
-    const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
-      input.nonWitnessUtxo,
-    );
+    // @ts-ignore
+    if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+      addNonWitnessTxCache(psbt, input, inputIndex);
+    }
+    // @ts-ignore
+    const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
     const prevoutHash = unsignedTx.ins[inputIndex].hash;
     const utxoHash = nonWitnessUtxoTx.getHash();
     // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
@@ -494,7 +611,7 @@ const classifyScript = script => {
   if (isP2PK(script)) return 'pubkey';
   return 'nonstandard';
 };
-function getScriptFromInput(inputIndex, input, unsignedTx) {
+function getScriptFromInput(inputIndex, input, unsignedTx, psbt) {
   const res = {
     script: null,
     isSegwit: false,
@@ -506,9 +623,12 @@ function getScriptFromInput(inputIndex, input, unsignedTx) {
       res.isP2SH = true;
       res.script = input.redeemScript;
     } else {
-      const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
-        input.nonWitnessUtxo,
-      );
+      // @ts-ignore
+      if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+        addNonWitnessTxCache(psbt, input, inputIndex);
+      }
+      // @ts-ignore
+      const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
       const prevoutIndex = unsignedTx.ins[inputIndex].index;
       res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
     }
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index fea9a67..bd6aeb4 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,5 +1,6 @@
 import { Psbt as PsbtBase } from 'bip174';
 import {
+  NonWitnessUtxo,
   PartialSig,
   PsbtInput,
   TransactionInput,
@@ -13,7 +14,7 @@ import { Signer, SignerAsync } from './ecpair';
 import { bitcoin as btcNetwork, Network } from './networks';
 import * as payments from './payments';
 import * as bscript from './script';
-import { Transaction } from './transaction';
+import { Output, Transaction } from './transaction';
 const varuint = require('varuint-bitcoin');
 
 export class Psbt extends PsbtBase {
@@ -65,6 +66,10 @@ export class Psbt extends PsbtBase {
   }
   private __TX: Transaction;
   private __TX_BUF_CACHE?: Buffer;
+  private __FEE_RATE?: number;
+  private __EXTRACTED_TX?: Transaction;
+  private __NON_WITNESS_UTXO_TX_CACHE: Transaction[] = [];
+  private __NON_WITNESS_UTXO_BUF_CACHE: Buffer[] = [];
   private opts: PsbtOpts;
   constructor(opts: PsbtOptsOptional = {}) {
     super();
@@ -103,15 +108,25 @@ export class Psbt extends PsbtBase {
         writable,
       });
     dpew(this, '__TX', false, true);
+    dpew(this, '__EXTRACTED_TX', false, true);
+    dpew(this, '__FEE_RATE', false, true);
     dpew(this, '__TX_BUF_CACHE', false, true);
+    dpew(this, '__NON_WITNESS_UTXO_TX_CACHE', false, true);
+    dpew(this, '__NON_WITNESS_UTXO_BUF_CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
 
+  setMaximumFeeRate(satoshiPerByte: number): void {
+    check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
+    this.opts.maximumFeeRate = satoshiPerByte;
+  }
+
   setVersion(version: number): this {
     check32Bit(version);
     checkInputsForPartialSig(this.inputs, 'setVersion');
     this.__TX.version = version;
     this.__TX_BUF_CACHE = undefined;
+    this.__EXTRACTED_TX = undefined;
     return this;
   }
 
@@ -120,6 +135,7 @@ export class Psbt extends PsbtBase {
     checkInputsForPartialSig(this.inputs, 'setLocktime');
     this.__TX.locktime = locktime;
     this.__TX_BUF_CACHE = undefined;
+    this.__EXTRACTED_TX = undefined;
     return this;
   }
 
@@ -131,6 +147,7 @@ export class Psbt extends PsbtBase {
     }
     this.__TX.ins[inputIndex].sequence = sequence;
     this.__TX_BUF_CACHE = undefined;
+    this.__EXTRACTED_TX = undefined;
     return this;
   }
 
@@ -197,8 +214,33 @@ export class Psbt extends PsbtBase {
     return super.addOutput(outputData, true, outputAdder);
   }
 
-  extractTransaction(): Transaction {
+  addNonWitnessUtxoToInput(
+    inputIndex: number,
+    nonWitnessUtxo: NonWitnessUtxo,
+  ): this {
+    super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
+    const input = this.inputs[inputIndex];
+    addNonWitnessTxCache(this, input, inputIndex);
+    return this;
+  }
+
+  extractTransaction(disableFeeCheck?: boolean): Transaction {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
+    if (!disableFeeCheck) {
+      const feeRate = this.__FEE_RATE || this.getFeeRate();
+      const vsize = this.__EXTRACTED_TX!.virtualSize();
+      const satoshis = feeRate * vsize;
+      if (feeRate >= this.opts.maximumFeeRate) {
+        throw new Error(
+          `Warning: You are paying around ${satoshis / 1e8} in fees, which ` +
+            `is ${feeRate} satoshi per byte for a transaction with a VSize of ` +
+            `${vsize} bytes (segwit counted as 0.25 byte per byte)\n` +
+            `Use setMaximumFeeRate method to raise your threshold, or pass ` +
+            `true to the first arg of extractTransaction.`,
+        );
+      }
+    }
+    if (this.__EXTRACTED_TX) return this.__EXTRACTED_TX;
     const tx = this.__TX.clone();
     this.inputs.forEach((input, idx) => {
       if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
@@ -208,9 +250,56 @@ export class Psbt extends PsbtBase {
         );
       }
     });
+    this.__EXTRACTED_TX = tx;
     return tx;
   }
 
+  getFeeRate(): number {
+    if (!this.inputs.every(isFinalized))
+      throw new Error('PSBT must be finalized to calculate fee rate');
+    if (this.__FEE_RATE) return this.__FEE_RATE;
+    let tx: Transaction;
+    let inputAmount = 0;
+    let mustFinalize = true;
+    if (this.__EXTRACTED_TX) {
+      tx = this.__EXTRACTED_TX;
+      mustFinalize = false;
+    } else {
+      tx = this.__TX.clone();
+    }
+    this.inputs.forEach((input, idx) => {
+      if (mustFinalize && input.finalScriptSig)
+        tx.ins[idx].script = input.finalScriptSig;
+      if (mustFinalize && input.finalScriptWitness) {
+        tx.ins[idx].witness = scriptWitnessToWitnessStack(
+          input.finalScriptWitness,
+        );
+      }
+      if (input.witnessUtxo) {
+        inputAmount += input.witnessUtxo.value;
+      } else if (input.nonWitnessUtxo) {
+        // @ts-ignore
+        if (!this.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
+          addNonWitnessTxCache(this, input, idx);
+        }
+        const vout = this.__TX.ins[idx].index;
+        const out = this.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output;
+        inputAmount += out.value;
+      } else {
+        throw new Error('Missing input value: index #' + idx);
+      }
+    });
+    this.__EXTRACTED_TX = tx;
+    const outputAmount = (tx.outs as Output[]).reduce(
+      (total, o) => total + o.value,
+      0,
+    );
+    const fee = inputAmount - outputAmount;
+    const bytes = tx.virtualSize();
+    this.__FEE_RATE = Math.floor(fee / bytes);
+    return this.__FEE_RATE;
+  }
+
   finalizeAllInputs(): {
     result: boolean;
     inputResults: boolean[];
@@ -231,6 +320,7 @@ export class Psbt extends PsbtBase {
       inputIndex,
       input,
       this.__TX,
+      this,
     );
     if (!script) return false;
 
@@ -264,6 +354,7 @@ export class Psbt extends PsbtBase {
       inputIndex,
       keyPair.publicKey,
       this.__TX,
+      this,
     );
 
     const partialSig = {
@@ -284,6 +375,7 @@ export class Psbt extends PsbtBase {
           inputIndex,
           keyPair.publicKey,
           this.__TX,
+          this,
         );
 
         Promise.resolve(keyPair.sign(hash)).then(signature => {
@@ -312,16 +404,58 @@ export class Psbt extends PsbtBase {
 
 interface PsbtOptsOptional {
   network?: Network;
+  maximumFeeRate?: number;
 }
 
 interface PsbtOpts {
   network: Network;
+  maximumFeeRate: number;
 }
 
 const DEFAULT_OPTS = {
   network: btcNetwork,
+  maximumFeeRate: 5000, // satoshi per byte
 };
 
+function addNonWitnessTxCache(
+  psbt: Psbt,
+  input: PsbtInput,
+  inputIndex: number,
+): void {
+  // @ts-ignore
+  psbt.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!;
+
+  const tx = Transaction.fromBuffer(input.nonWitnessUtxo!);
+  // @ts-ignore
+  psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
+
+  const self = psbt;
+  const selfIndex = inputIndex;
+  delete input.nonWitnessUtxo;
+  Object.defineProperty(input, 'nonWitnessUtxo', {
+    enumerable: true,
+    get(): Buffer {
+      // @ts-ignore
+      if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) {
+        // @ts-ignore
+        return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+      } else {
+        // @ts-ignore
+        self.__NON_WITNESS_UTXO_BUF_CACHE[
+          selfIndex
+          // @ts-ignore
+        ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer();
+        // @ts-ignore
+        return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+      }
+    },
+    set(data: Buffer): void {
+      // @ts-ignore
+      self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
+    },
+  });
+}
+
 function isFinalized(input: PsbtInput): boolean {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
@@ -331,6 +465,7 @@ function getHashAndSighashType(
   inputIndex: number,
   pubkey: Buffer,
   unsignedTx: Transaction,
+  psbt: Psbt,
 ): {
   hash: Buffer;
   sighashType: number;
@@ -340,6 +475,7 @@ function getHashAndSighashType(
     inputIndex,
     input,
     unsignedTx,
+    psbt,
   );
   checkScriptForPubkey(pubkey, script);
   return {
@@ -490,13 +626,19 @@ const getHashForSig = (
   inputIndex: number,
   input: PsbtInput,
   unsignedTx: Transaction,
+  psbt: Psbt,
 ): HashForSigData => {
   const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
   let hash: Buffer;
   let script: Buffer;
 
   if (input.nonWitnessUtxo) {
-    const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
+    // @ts-ignore
+    if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+      addNonWitnessTxCache(psbt, input, inputIndex);
+    }
+    // @ts-ignore
+    const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
 
     const prevoutHash = unsignedTx.ins[inputIndex].hash;
     const utxoHash = nonWitnessUtxoTx.getHash();
@@ -636,6 +778,7 @@ function getScriptFromInput(
   inputIndex: number,
   input: PsbtInput,
   unsignedTx: Transaction,
+  psbt: Psbt,
 ): GetScriptReturn {
   const res: GetScriptReturn = {
     script: null,
@@ -648,7 +791,12 @@ function getScriptFromInput(
       res.isP2SH = true;
       res.script = input.redeemScript;
     } else {
-      const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
+      // @ts-ignore
+      if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+        addNonWitnessTxCache(psbt, input, inputIndex);
+      }
+      // @ts-ignore
+      const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
       const prevoutIndex = unsignedTx.ins[inputIndex].index;
       res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
     }
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 1aa6f28..af234d8 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -1,6 +1,6 @@
 /// <reference types="node" />
 import { Psbt as PsbtBase } from 'bip174';
-import { TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces';
+import { NonWitnessUtxo, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces';
 import { Signer, SignerAsync } from './ecpair';
 import { Network } from './networks';
 import { Transaction } from './transaction';
@@ -9,14 +9,21 @@ export declare class Psbt extends PsbtBase {
     static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>;
     private __TX;
     private __TX_BUF_CACHE?;
+    private __FEE_RATE?;
+    private __EXTRACTED_TX?;
+    private __NON_WITNESS_UTXO_TX_CACHE;
+    private __NON_WITNESS_UTXO_BUF_CACHE;
     private opts;
     constructor(opts?: PsbtOptsOptional);
+    setMaximumFeeRate(satoshiPerByte: number): void;
     setVersion(version: number): this;
     setLocktime(locktime: number): this;
     setSequence(inputIndex: number, sequence: number): this;
     addInput(inputData: TransactionInput): this;
     addOutput(outputData: TransactionOutput): this;
-    extractTransaction(): Transaction;
+    addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this;
+    extractTransaction(disableFeeCheck?: boolean): Transaction;
+    getFeeRate(): number;
     finalizeAllInputs(): {
         result: boolean;
         inputResults: boolean[];
@@ -27,5 +34,6 @@ export declare class Psbt extends PsbtBase {
 }
 interface PsbtOptsOptional {
     network?: Network;
+    maximumFeeRate?: number;
 }
 export {};

From 51133c8051458b9d9089aa0374be6fb20999b40d Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 5 Jul 2019 12:51:13 +0900
Subject: [PATCH 061/111] Add type instance check tests

---
 src/psbt.js    | 10 ++++++++--
 test/psbt.js   | 27 ++++++++++++++++++++++++++-
 ts_src/psbt.ts | 10 ++++++++--
 3 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 272c716..ba42917 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -143,7 +143,10 @@ class Psbt extends bip174_1.Psbt {
       });
       return self.__TX.toBuffer();
     };
-    return super.addInput(inputData, inputAdder);
+    super.addInput(inputData, inputAdder);
+    this.__FEE_RATE = undefined;
+    this.__EXTRACTED_TX = undefined;
+    return this;
   }
   addOutput(outputData) {
     checkInputsForPartialSig(this.inputs, 'addOutput');
@@ -170,7 +173,10 @@ class Psbt extends bip174_1.Psbt {
       });
       return self.__TX.toBuffer();
     };
-    return super.addOutput(outputData, true, outputAdder);
+    super.addOutput(outputData, true, outputAdder);
+    this.__FEE_RATE = undefined;
+    this.__EXTRACTED_TX = undefined;
+    return this;
   }
   addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
     super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
diff --git a/test/psbt.js b/test/psbt.js
index 67f3611..d65872c 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -158,7 +158,7 @@ describe(`Psbt`, () => {
             ECPair.fromWIF(f.shouldSign.WIF),
           )
         })
-    
+
         const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
         assert.throws(() => {
           psbtThatShouldThrow.signInput(
@@ -224,4 +224,29 @@ describe(`Psbt`, () => {
       }, {message: 'Input index too high'})
     })
   })
+
+  describe('Method return types', () => {
+    it('fromTransaction returns Psbt type (not base class)', () => {
+      const psbt = Psbt.fromTransaction(Buffer.from([2,0,0,0,0,0,0,0,0,0]));
+      assert.strictEqual(psbt instanceof Psbt, true);
+      assert.ok(psbt.__TX);
+    })
+    it('fromBuffer returns Psbt type (not base class)', () => {
+      const psbt = Psbt.fromBuffer(Buffer.from(
+        '70736274ff01000a01000000000000000000000000', 'hex' //cHNidP8BAAoBAAAAAAAAAAAAAAAA
+      ));
+      assert.strictEqual(psbt instanceof Psbt, true);
+      assert.ok(psbt.__TX);
+    })
+    it('fromBase64 returns Psbt type (not base class)', () => {
+      const psbt = Psbt.fromBase64('cHNidP8BAAoBAAAAAAAAAAAAAAAA');
+      assert.strictEqual(psbt instanceof Psbt, true);
+      assert.ok(psbt.__TX);
+    })
+    it('fromHex returns Psbt type (not base class)', () => {
+      const psbt = Psbt.fromHex('70736274ff01000a01000000000000000000000000');
+      assert.strictEqual(psbt instanceof Psbt, true);
+      assert.ok(psbt.__TX);
+    })
+  })
 })
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index bd6aeb4..73ffe33 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -180,7 +180,10 @@ export class Psbt extends PsbtBase {
       });
       return self.__TX.toBuffer();
     };
-    return super.addInput(inputData, inputAdder);
+    super.addInput(inputData, inputAdder);
+    this.__FEE_RATE = undefined;
+    this.__EXTRACTED_TX = undefined;
+    return this;
   }
 
   addOutput(outputData: TransactionOutput): this {
@@ -211,7 +214,10 @@ export class Psbt extends PsbtBase {
       });
       return self.__TX.toBuffer();
     };
-    return super.addOutput(outputData, true, outputAdder);
+    super.addOutput(outputData, true, outputAdder);
+    this.__FEE_RATE = undefined;
+    this.__EXTRACTED_TX = undefined;
+    return this;
   }
 
   addNonWitnessUtxoToInput(

From 93e1661c6c7c1557203e6ff3e379b8f128fd0ea6 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 5 Jul 2019 14:30:08 +0900
Subject: [PATCH 062/111] Remove need for ts-ignore

---
 src/psbt.js     | 65 +++++++++++++++++++--------------------------
 ts_src/psbt.ts  | 70 ++++++++++++++++++++++---------------------------
 types/psbt.d.ts |  3 +--
 3 files changed, 60 insertions(+), 78 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index ba42917..aa2adf2 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -13,8 +13,10 @@ const varuint = require('varuint-bitcoin');
 class Psbt extends bip174_1.Psbt {
   constructor(opts = {}) {
     super();
-    this.__NON_WITNESS_UTXO_TX_CACHE = [];
-    this.__NON_WITNESS_UTXO_BUF_CACHE = [];
+    this.__NON_WITNESS_UTXO_CACHE = {
+      __NON_WITNESS_UTXO_TX_CACHE: [],
+      __NON_WITNESS_UTXO_BUF_CACHE: [],
+    };
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
@@ -46,8 +48,7 @@ class Psbt extends bip174_1.Psbt {
     dpew(this, '__EXTRACTED_TX', false, true);
     dpew(this, '__FEE_RATE', false, true);
     dpew(this, '__TX_BUF_CACHE', false, true);
-    dpew(this, '__NON_WITNESS_UTXO_TX_CACHE', false, true);
-    dpew(this, '__NON_WITNESS_UTXO_BUF_CACHE', false, true);
+    dpew(this, '__NON_WITNESS_UTXO_CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
   static fromTransaction(txBuf) {
@@ -181,7 +182,7 @@ class Psbt extends bip174_1.Psbt {
   addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
     super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
     const input = this.inputs[inputIndex];
-    addNonWitnessTxCache(this, input, inputIndex);
+    addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, inputIndex);
     return this;
   }
   extractTransaction(disableFeeCheck) {
@@ -237,12 +238,12 @@ class Psbt extends bip174_1.Psbt {
       if (input.witnessUtxo) {
         inputAmount += input.witnessUtxo.value;
       } else if (input.nonWitnessUtxo) {
-        // @ts-ignore
-        if (!this.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
-          addNonWitnessTxCache(this, input, idx);
+        const cache = this.__NON_WITNESS_UTXO_CACHE;
+        if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
+          addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, idx);
         }
         const vout = this.__TX.ins[idx].index;
-        const out = this.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout];
+        const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout];
         inputAmount += out.value;
       } else {
         throw new Error('Missing input value: index #' + idx);
@@ -271,7 +272,7 @@ class Psbt extends bip174_1.Psbt {
       inputIndex,
       input,
       this.__TX,
-      this,
+      this.__NON_WITNESS_UTXO_CACHE,
     );
     if (!script) return false;
     const scriptType = classifyScript(script);
@@ -300,7 +301,7 @@ class Psbt extends bip174_1.Psbt {
       inputIndex,
       keyPair.publicKey,
       this.__TX,
-      this,
+      this.__NON_WITNESS_UTXO_CACHE,
     );
     const partialSig = {
       pubkey: keyPair.publicKey,
@@ -317,7 +318,7 @@ class Psbt extends bip174_1.Psbt {
         inputIndex,
         keyPair.publicKey,
         this.__TX,
-        this,
+        this.__NON_WITNESS_UTXO_CACHE,
       );
       Promise.resolve(keyPair.sign(hash)).then(signature => {
         const partialSig = {
@@ -335,34 +336,26 @@ const DEFAULT_OPTS = {
   network: networks_1.bitcoin,
   maximumFeeRate: 5000,
 };
-function addNonWitnessTxCache(psbt, input, inputIndex) {
-  // @ts-ignore
-  psbt.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo;
+function addNonWitnessTxCache(cache, input, inputIndex) {
+  cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo;
   const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo);
-  // @ts-ignore
-  psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
-  const self = psbt;
+  cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
+  const self = cache;
   const selfIndex = inputIndex;
   delete input.nonWitnessUtxo;
   Object.defineProperty(input, 'nonWitnessUtxo', {
     enumerable: true,
     get() {
-      // @ts-ignore
       if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) {
-        // @ts-ignore
         return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
       } else {
-        // @ts-ignore
         self.__NON_WITNESS_UTXO_BUF_CACHE[
           selfIndex
-          // @ts-ignore
         ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer();
-        // @ts-ignore
         return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
       }
     },
     set(data) {
-      // @ts-ignore
       self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
     },
   });
@@ -370,13 +363,13 @@ function addNonWitnessTxCache(psbt, input, inputIndex) {
 function isFinalized(input) {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
-function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx, psbt) {
+function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx, cache) {
   const input = utils_1.checkForInput(inputs, inputIndex);
   const { hash, sighashType, script } = getHashForSig(
     inputIndex,
     input,
     unsignedTx,
-    psbt,
+    cache,
   );
   checkScriptForPubkey(pubkey, script);
   return {
@@ -495,18 +488,16 @@ function checkScriptForPubkey(pubkey, script) {
     );
   }
 }
-const getHashForSig = (inputIndex, input, unsignedTx, psbt) => {
+const getHashForSig = (inputIndex, input, unsignedTx, cache) => {
   const sighashType =
     input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
   let hash;
   let script;
   if (input.nonWitnessUtxo) {
-    // @ts-ignore
-    if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
-      addNonWitnessTxCache(psbt, input, inputIndex);
+    if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+      addNonWitnessTxCache(cache, input, inputIndex);
     }
-    // @ts-ignore
-    const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+    const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
     const prevoutHash = unsignedTx.ins[inputIndex].hash;
     const utxoHash = nonWitnessUtxoTx.getHash();
     // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
@@ -617,7 +608,7 @@ const classifyScript = script => {
   if (isP2PK(script)) return 'pubkey';
   return 'nonstandard';
 };
-function getScriptFromInput(inputIndex, input, unsignedTx, psbt) {
+function getScriptFromInput(inputIndex, input, unsignedTx, cache) {
   const res = {
     script: null,
     isSegwit: false,
@@ -629,12 +620,10 @@ function getScriptFromInput(inputIndex, input, unsignedTx, psbt) {
       res.isP2SH = true;
       res.script = input.redeemScript;
     } else {
-      // @ts-ignore
-      if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
-        addNonWitnessTxCache(psbt, input, inputIndex);
+      if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+        addNonWitnessTxCache(cache, input, inputIndex);
       }
-      // @ts-ignore
-      const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+      const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
       const prevoutIndex = unsignedTx.ins[inputIndex].index;
       res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
     }
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 73ffe33..e6c96f2 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -64,12 +64,14 @@ export class Psbt extends PsbtBase {
     psbt.__TX = tx!;
     return psbt as InstanceType<T>;
   }
+  private __NON_WITNESS_UTXO_CACHE = {
+    __NON_WITNESS_UTXO_TX_CACHE: [] as Transaction[],
+    __NON_WITNESS_UTXO_BUF_CACHE: [] as Buffer[],
+  };
   private __TX: Transaction;
   private __TX_BUF_CACHE?: Buffer;
   private __FEE_RATE?: number;
   private __EXTRACTED_TX?: Transaction;
-  private __NON_WITNESS_UTXO_TX_CACHE: Transaction[] = [];
-  private __NON_WITNESS_UTXO_BUF_CACHE: Buffer[] = [];
   private opts: PsbtOpts;
   constructor(opts: PsbtOptsOptional = {}) {
     super();
@@ -111,8 +113,7 @@ export class Psbt extends PsbtBase {
     dpew(this, '__EXTRACTED_TX', false, true);
     dpew(this, '__FEE_RATE', false, true);
     dpew(this, '__TX_BUF_CACHE', false, true);
-    dpew(this, '__NON_WITNESS_UTXO_TX_CACHE', false, true);
-    dpew(this, '__NON_WITNESS_UTXO_BUF_CACHE', false, true);
+    dpew(this, '__NON_WITNESS_UTXO_CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
 
@@ -226,7 +227,7 @@ export class Psbt extends PsbtBase {
   ): this {
     super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
     const input = this.inputs[inputIndex];
-    addNonWitnessTxCache(this, input, inputIndex);
+    addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, inputIndex);
     return this;
   }
 
@@ -284,12 +285,12 @@ export class Psbt extends PsbtBase {
       if (input.witnessUtxo) {
         inputAmount += input.witnessUtxo.value;
       } else if (input.nonWitnessUtxo) {
-        // @ts-ignore
-        if (!this.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
-          addNonWitnessTxCache(this, input, idx);
+        const cache = this.__NON_WITNESS_UTXO_CACHE;
+        if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
+          addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, idx);
         }
         const vout = this.__TX.ins[idx].index;
-        const out = this.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output;
+        const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output;
         inputAmount += out.value;
       } else {
         throw new Error('Missing input value: index #' + idx);
@@ -326,7 +327,7 @@ export class Psbt extends PsbtBase {
       inputIndex,
       input,
       this.__TX,
-      this,
+      this.__NON_WITNESS_UTXO_CACHE,
     );
     if (!script) return false;
 
@@ -360,7 +361,7 @@ export class Psbt extends PsbtBase {
       inputIndex,
       keyPair.publicKey,
       this.__TX,
-      this,
+      this.__NON_WITNESS_UTXO_CACHE,
     );
 
     const partialSig = {
@@ -381,7 +382,7 @@ export class Psbt extends PsbtBase {
           inputIndex,
           keyPair.publicKey,
           this.__TX,
-          this,
+          this.__NON_WITNESS_UTXO_CACHE,
         );
 
         Promise.resolve(keyPair.sign(hash)).then(signature => {
@@ -408,6 +409,11 @@ export class Psbt extends PsbtBase {
 //
 //
 
+interface NonWitnessUtxoCache {
+  __NON_WITNESS_UTXO_TX_CACHE: Transaction[];
+  __NON_WITNESS_UTXO_BUF_CACHE: Buffer[];
+}
+
 interface PsbtOptsOptional {
   network?: Network;
   maximumFeeRate?: number;
@@ -424,39 +430,31 @@ const DEFAULT_OPTS = {
 };
 
 function addNonWitnessTxCache(
-  psbt: Psbt,
+  cache: NonWitnessUtxoCache,
   input: PsbtInput,
   inputIndex: number,
 ): void {
-  // @ts-ignore
-  psbt.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!;
+  cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!;
 
   const tx = Transaction.fromBuffer(input.nonWitnessUtxo!);
-  // @ts-ignore
-  psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
+  cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
 
-  const self = psbt;
+  const self = cache;
   const selfIndex = inputIndex;
   delete input.nonWitnessUtxo;
   Object.defineProperty(input, 'nonWitnessUtxo', {
     enumerable: true,
     get(): Buffer {
-      // @ts-ignore
       if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) {
-        // @ts-ignore
         return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
       } else {
-        // @ts-ignore
         self.__NON_WITNESS_UTXO_BUF_CACHE[
           selfIndex
-          // @ts-ignore
         ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer();
-        // @ts-ignore
         return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
       }
     },
     set(data: Buffer): void {
-      // @ts-ignore
       self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
     },
   });
@@ -471,7 +469,7 @@ function getHashAndSighashType(
   inputIndex: number,
   pubkey: Buffer,
   unsignedTx: Transaction,
-  psbt: Psbt,
+  cache: NonWitnessUtxoCache,
 ): {
   hash: Buffer;
   sighashType: number;
@@ -481,7 +479,7 @@ function getHashAndSighashType(
     inputIndex,
     input,
     unsignedTx,
-    psbt,
+    cache,
   );
   checkScriptForPubkey(pubkey, script);
   return {
@@ -632,19 +630,17 @@ const getHashForSig = (
   inputIndex: number,
   input: PsbtInput,
   unsignedTx: Transaction,
-  psbt: Psbt,
+  cache: NonWitnessUtxoCache,
 ): HashForSigData => {
   const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
   let hash: Buffer;
   let script: Buffer;
 
   if (input.nonWitnessUtxo) {
-    // @ts-ignore
-    if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
-      addNonWitnessTxCache(psbt, input, inputIndex);
+    if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+      addNonWitnessTxCache(cache, input, inputIndex);
     }
-    // @ts-ignore
-    const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+    const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
 
     const prevoutHash = unsignedTx.ins[inputIndex].hash;
     const utxoHash = nonWitnessUtxoTx.getHash();
@@ -784,7 +780,7 @@ function getScriptFromInput(
   inputIndex: number,
   input: PsbtInput,
   unsignedTx: Transaction,
-  psbt: Psbt,
+  cache: NonWitnessUtxoCache,
 ): GetScriptReturn {
   const res: GetScriptReturn = {
     script: null,
@@ -797,12 +793,10 @@ function getScriptFromInput(
       res.isP2SH = true;
       res.script = input.redeemScript;
     } else {
-      // @ts-ignore
-      if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
-        addNonWitnessTxCache(psbt, input, inputIndex);
+      if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+        addNonWitnessTxCache(cache, input, inputIndex);
       }
-      // @ts-ignore
-      const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+      const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
       const prevoutIndex = unsignedTx.ins[inputIndex].index;
       res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
     }
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index af234d8..fae4fff 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -7,12 +7,11 @@ import { Transaction } from './transaction';
 export declare class Psbt extends PsbtBase {
     static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>;
     static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>;
+    private __NON_WITNESS_UTXO_CACHE;
     private __TX;
     private __TX_BUF_CACHE?;
     private __FEE_RATE?;
     private __EXTRACTED_TX?;
-    private __NON_WITNESS_UTXO_TX_CACHE;
-    private __NON_WITNESS_UTXO_BUF_CACHE;
     private opts;
     constructor(opts?: PsbtOptsOptional);
     setMaximumFeeRate(satoshiPerByte: number): void;

From 8d52ce1668bc1ee73d56cdfc4bbf2b5e23fa653c Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 5 Jul 2019 16:42:13 +0900
Subject: [PATCH 063/111] Add some tests and an input duplicate checker

---
 src/psbt.js             |  54 ++++++++++++------
 test/fixtures/psbt.json |  31 +++++++++++
 test/psbt.js            | 118 ++++++++++++++++++++++++++++++++++++++--
 ts_src/psbt.ts          |  58 +++++++++++++++-----
 types/psbt.d.ts         |   3 +-
 5 files changed, 228 insertions(+), 36 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index aa2adf2..e797949 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -13,9 +13,10 @@ const varuint = require('varuint-bitcoin');
 class Psbt extends bip174_1.Psbt {
   constructor(opts = {}) {
     super();
-    this.__NON_WITNESS_UTXO_CACHE = {
+    this.__CACHE = {
       __NON_WITNESS_UTXO_TX_CACHE: [],
       __NON_WITNESS_UTXO_BUF_CACHE: [],
+      __TX_IN_CACHE: {},
     };
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
@@ -48,7 +49,7 @@ class Psbt extends bip174_1.Psbt {
     dpew(this, '__EXTRACTED_TX', false, true);
     dpew(this, '__FEE_RATE', false, true);
     dpew(this, '__TX_BUF_CACHE', false, true);
-    dpew(this, '__NON_WITNESS_UTXO_CACHE', false, true);
+    dpew(this, '__CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
   static fromTransaction(txBuf) {
@@ -56,6 +57,7 @@ class Psbt extends bip174_1.Psbt {
     checkTxEmpty(tx);
     const psbt = new this();
     psbt.__TX = tx;
+    checkTxForDupeIns(tx, psbt.__CACHE);
     let inputCount = tx.ins.length;
     let outputCount = tx.outs.length;
     while (inputCount > 0) {
@@ -84,8 +86,12 @@ class Psbt extends bip174_1.Psbt {
     };
     const psbt = super.fromBuffer(buffer, txCountGetter);
     psbt.__TX = tx;
+    checkTxForDupeIns(tx, psbt.__CACHE);
     return psbt;
   }
+  get inputCount() {
+    return this.inputs.length;
+  }
   setMaximumFeeRate(satoshiPerByte) {
     check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
     this.opts.maximumFeeRate = satoshiPerByte;
@@ -134,14 +140,17 @@ class Psbt extends bip174_1.Psbt {
       const prevHash = Buffer.isBuffer(_inputData.hash)
         ? _inputData.hash
         : bufferutils_1.reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
-      self.__TX.ins.push({
-        hash: prevHash,
-        index: _inputData.index,
-        script: Buffer.alloc(0),
-        sequence:
-          _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE,
-        witness: [],
-      });
+      // Check if input already exists in cache.
+      const input = { hash: prevHash, index: _inputData.index };
+      checkTxInputCache(self.__CACHE, input);
+      self.__TX.ins.push(
+        Object.assign({}, input, {
+          script: Buffer.alloc(0),
+          sequence:
+            _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE,
+          witness: [],
+        }),
+      );
       return self.__TX.toBuffer();
     };
     super.addInput(inputData, inputAdder);
@@ -182,7 +191,7 @@ class Psbt extends bip174_1.Psbt {
   addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
     super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
     const input = this.inputs[inputIndex];
-    addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, inputIndex);
+    addNonWitnessTxCache(this.__CACHE, input, inputIndex);
     return this;
   }
   extractTransaction(disableFeeCheck) {
@@ -238,9 +247,9 @@ class Psbt extends bip174_1.Psbt {
       if (input.witnessUtxo) {
         inputAmount += input.witnessUtxo.value;
       } else if (input.nonWitnessUtxo) {
-        const cache = this.__NON_WITNESS_UTXO_CACHE;
+        const cache = this.__CACHE;
         if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
-          addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, idx);
+          addNonWitnessTxCache(this.__CACHE, input, idx);
         }
         const vout = this.__TX.ins[idx].index;
         const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout];
@@ -272,7 +281,7 @@ class Psbt extends bip174_1.Psbt {
       inputIndex,
       input,
       this.__TX,
-      this.__NON_WITNESS_UTXO_CACHE,
+      this.__CACHE,
     );
     if (!script) return false;
     const scriptType = classifyScript(script);
@@ -301,7 +310,7 @@ class Psbt extends bip174_1.Psbt {
       inputIndex,
       keyPair.publicKey,
       this.__TX,
-      this.__NON_WITNESS_UTXO_CACHE,
+      this.__CACHE,
     );
     const partialSig = {
       pubkey: keyPair.publicKey,
@@ -318,7 +327,7 @@ class Psbt extends bip174_1.Psbt {
         inputIndex,
         keyPair.publicKey,
         this.__TX,
-        this.__NON_WITNESS_UTXO_CACHE,
+        this.__CACHE,
       );
       Promise.resolve(keyPair.sign(hash)).then(signature => {
         const partialSig = {
@@ -360,6 +369,19 @@ function addNonWitnessTxCache(cache, input, inputIndex) {
     },
   });
 }
+function checkTxForDupeIns(tx, cache) {
+  tx.ins.forEach(input => {
+    checkTxInputCache(cache, input);
+  });
+}
+function checkTxInputCache(cache, input) {
+  const key =
+    bufferutils_1.reverseBuffer(Buffer.from(input.hash)).toString('hex') +
+    ':' +
+    input.index;
+  if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
+  cache.__TX_IN_CACHE[key] = 1;
+}
 function isFinalized(input) {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 00877f9..59a74cd 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -304,6 +304,37 @@
       }
     ]
   },
+  "addInput": {
+    "checks": [
+      {
+        "description": "checks for hash and index",
+        "inputData": {
+          "hash": 42
+        },
+        "exception": "Error adding input."
+      },
+      {
+        "description": "checks for hash and index",
+        "inputData": {
+          "hash": "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f",
+          "index": 2
+        },
+        "equals": "cHNidP8BADMCAAAAAQABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4PAgAAAAD/////AAAAAAAAAAA="
+      }
+    ]
+  },
+  "addOutput": {
+    "checks": [
+      {
+        "description": "checks for hash and index",
+        "outputData": {
+          "address": "1P2NFEBp32V2arRwZNww6tgXEV58FG94mr",
+          "value": "xyz"
+        },
+        "exception": "Error adding output."
+      }
+    ]
+  },
   "signInput": {
     "checks": [
       {
diff --git a/test/psbt.js b/test/psbt.js
index d65872c..9a3ac92 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -21,8 +21,16 @@ const initBuffers = (attr, data) => {
   } else if (attr === 'bip32Derivation') {
     data.masterFingerprint = b(data.masterFingerprint)
     data.pubkey = b(data.pubkey)
-  }  else if (attr === 'witnessUtxo') {
+  } else if (attr === 'witnessUtxo') {
     data.script = b(data.script)
+  } else if (attr === 'hash') {
+    if (
+      typeof data === 'string' &&
+      data.match(/^[0-9a-f]*$/i) &&
+      data.length % 2 === 0
+    ) {
+      data = b(data)
+    }
   }
 
   return data
@@ -140,11 +148,55 @@ describe(`Psbt`, () => {
 
   fixtures.bip174.extractor.forEach(f => {
     it('Extracts the expected transaction from a PSBT', () => {
-      const psbt =  Psbt.fromBase64(f.psbt)
+      const psbt1 =  Psbt.fromBase64(f.psbt)
+      const transaction1 = psbt1.extractTransaction(true).toHex()
 
-      const transaction = psbt.extractTransaction().toHex()
+      const psbt2 =  Psbt.fromBase64(f.psbt)
+      const transaction2 = psbt2.extractTransaction().toHex()
 
-      assert.strictEqual(transaction, f.transaction)
+      assert.strictEqual(transaction1, transaction2)
+      assert.strictEqual(transaction1, f.transaction)
+
+      const psbt3 =  Psbt.fromBase64(f.psbt)
+      delete psbt3.inputs[0].finalScriptSig
+      delete psbt3.inputs[0].finalScriptWitness
+      assert.throws(() => {
+        psbt3.extractTransaction()
+      }, new RegExp('Not finalized'))
+
+      const psbt4 =  Psbt.fromBase64(f.psbt)
+      psbt4.setMaximumFeeRate(1)
+      assert.throws(() => {
+        psbt4.extractTransaction()
+      }, new RegExp('Warning: You are paying around [\\d.]+ in fees'))
+
+      const psbt5 =  Psbt.fromBase64(f.psbt)
+      psbt5.extractTransaction(true)
+      const fr1 = psbt5.getFeeRate()
+      const fr2 = psbt5.getFeeRate()
+      assert.strictEqual(fr1, fr2)
+    })
+  })
+
+  describe('signInputAsync', () => {
+    fixtures.signInput.checks.forEach(f => {
+      it(f.description, async () => {
+        const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+        assert.doesNotReject(async () => {
+          await psbtThatShouldsign.signInputAsync(
+            f.shouldSign.inputToCheck,
+            ECPair.fromWIF(f.shouldSign.WIF),
+          )
+        })
+
+        const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+        assert.rejects(async () => {
+          await psbtThatShouldThrow.signInputAsync(
+            f.shouldThrow.inputToCheck,
+            ECPair.fromWIF(f.shouldThrow.WIF),
+          )
+        }, {message: f.shouldThrow.errorMessage})
+      })
     })
   })
 
@@ -179,6 +231,54 @@ describe(`Psbt`, () => {
     })
   })
 
+  describe('addInput', () => {
+    fixtures.addInput.checks.forEach(f => {
+      for (const attr of Object.keys(f.inputData)) {
+        f.inputData[attr] = initBuffers(attr, f.inputData[attr])
+      }
+      it(f.description, () => {
+        const psbt = new Psbt()
+
+        if (f.exception) {
+          assert.throws(() => {
+            psbt.addInput(f.inputData)
+          }, new RegExp(f.exception))
+        } else {
+          assert.doesNotThrow(() => {
+            psbt.addInput(f.inputData)
+            if (f.equals) {
+              assert.strictEqual(psbt.toBase64(), f.equals)
+            } else {
+              console.log(psbt.toBase64())
+            }
+          })
+        }
+      })
+    })
+  })
+
+  describe('addOutput', () => {
+    fixtures.addOutput.checks.forEach(f => {
+      for (const attr of Object.keys(f.outputData)) {
+        f.outputData[attr] = initBuffers(attr, f.outputData[attr])
+      }
+      it(f.description, () => {
+        const psbt = new Psbt()
+
+        if (f.exception) {
+          assert.throws(() => {
+            psbt.addOutput(f.outputData)
+          }, new RegExp(f.exception))
+        } else {
+          assert.doesNotThrow(() => {
+            psbt.addOutput(f.outputData)
+            console.log(psbt.toBase64())
+          })
+        }
+      })
+    })
+  })
+
   describe('setVersion', () => {
     it('Sets the version value of the unsigned transaction', () => {
       const psbt = new Psbt()
@@ -225,6 +325,16 @@ describe(`Psbt`, () => {
     })
   })
 
+  describe('setMaximumFeeRate', () => {
+    it('Sets the maximumFeeRate value', () => {
+      const psbt = new Psbt()
+
+      assert.strictEqual(psbt.opts.maximumFeeRate, 5000)
+      psbt.setMaximumFeeRate(6000)
+      assert.strictEqual(psbt.opts.maximumFeeRate, 6000)
+    })
+  })
+
   describe('Method return types', () => {
     it('fromTransaction returns Psbt type (not base class)', () => {
       const psbt = Psbt.fromTransaction(Buffer.from([2,0,0,0,0,0,0,0,0,0]));
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index e6c96f2..24a088e 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -26,6 +26,7 @@ export class Psbt extends PsbtBase {
     checkTxEmpty(tx);
     const psbt = new this() as Psbt;
     psbt.__TX = tx;
+    checkTxForDupeIns(tx, psbt.__CACHE);
     let inputCount = tx.ins.length;
     let outputCount = tx.outs.length;
     while (inputCount > 0) {
@@ -62,11 +63,13 @@ export class Psbt extends PsbtBase {
     };
     const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt;
     psbt.__TX = tx!;
+    checkTxForDupeIns(tx!, psbt.__CACHE);
     return psbt as InstanceType<T>;
   }
-  private __NON_WITNESS_UTXO_CACHE = {
+  private __CACHE = {
     __NON_WITNESS_UTXO_TX_CACHE: [] as Transaction[],
     __NON_WITNESS_UTXO_BUF_CACHE: [] as Buffer[],
+    __TX_IN_CACHE: {} as { [index: string]: number },
   };
   private __TX: Transaction;
   private __TX_BUF_CACHE?: Buffer;
@@ -113,10 +116,14 @@ export class Psbt extends PsbtBase {
     dpew(this, '__EXTRACTED_TX', false, true);
     dpew(this, '__FEE_RATE', false, true);
     dpew(this, '__TX_BUF_CACHE', false, true);
-    dpew(this, '__NON_WITNESS_UTXO_CACHE', false, true);
+    dpew(this, '__CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
 
+  get inputCount(): number {
+    return this.inputs.length;
+  }
+
   setMaximumFeeRate(satoshiPerByte: number): void {
     check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
     this.opts.maximumFeeRate = satoshiPerByte;
@@ -172,9 +179,13 @@ export class Psbt extends PsbtBase {
       const prevHash = Buffer.isBuffer(_inputData.hash)
         ? _inputData.hash
         : reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
+
+      // Check if input already exists in cache.
+      const input = { hash: prevHash, index: _inputData.index };
+      checkTxInputCache(self.__CACHE, input);
+
       self.__TX.ins.push({
-        hash: prevHash,
-        index: _inputData.index,
+        ...input,
         script: Buffer.alloc(0),
         sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE,
         witness: [],
@@ -227,7 +238,7 @@ export class Psbt extends PsbtBase {
   ): this {
     super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
     const input = this.inputs[inputIndex];
-    addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, inputIndex);
+    addNonWitnessTxCache(this.__CACHE, input, inputIndex);
     return this;
   }
 
@@ -285,9 +296,9 @@ export class Psbt extends PsbtBase {
       if (input.witnessUtxo) {
         inputAmount += input.witnessUtxo.value;
       } else if (input.nonWitnessUtxo) {
-        const cache = this.__NON_WITNESS_UTXO_CACHE;
+        const cache = this.__CACHE;
         if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
-          addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, idx);
+          addNonWitnessTxCache(this.__CACHE, input, idx);
         }
         const vout = this.__TX.ins[idx].index;
         const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output;
@@ -327,7 +338,7 @@ export class Psbt extends PsbtBase {
       inputIndex,
       input,
       this.__TX,
-      this.__NON_WITNESS_UTXO_CACHE,
+      this.__CACHE,
     );
     if (!script) return false;
 
@@ -361,7 +372,7 @@ export class Psbt extends PsbtBase {
       inputIndex,
       keyPair.publicKey,
       this.__TX,
-      this.__NON_WITNESS_UTXO_CACHE,
+      this.__CACHE,
     );
 
     const partialSig = {
@@ -382,7 +393,7 @@ export class Psbt extends PsbtBase {
           inputIndex,
           keyPair.publicKey,
           this.__TX,
-          this.__NON_WITNESS_UTXO_CACHE,
+          this.__CACHE,
         );
 
         Promise.resolve(keyPair.sign(hash)).then(signature => {
@@ -409,9 +420,10 @@ export class Psbt extends PsbtBase {
 //
 //
 
-interface NonWitnessUtxoCache {
+interface PsbtCache {
   __NON_WITNESS_UTXO_TX_CACHE: Transaction[];
   __NON_WITNESS_UTXO_BUF_CACHE: Buffer[];
+  __TX_IN_CACHE: { [index: string]: number };
 }
 
 interface PsbtOptsOptional {
@@ -430,7 +442,7 @@ const DEFAULT_OPTS = {
 };
 
 function addNonWitnessTxCache(
-  cache: NonWitnessUtxoCache,
+  cache: PsbtCache,
   input: PsbtInput,
   inputIndex: number,
 ): void {
@@ -460,6 +472,22 @@ function addNonWitnessTxCache(
   });
 }
 
+function checkTxForDupeIns(tx: Transaction, cache: PsbtCache): void {
+  tx.ins.forEach(input => {
+    checkTxInputCache(cache, input);
+  });
+}
+
+function checkTxInputCache(
+  cache: PsbtCache,
+  input: { hash: Buffer; index: number },
+): void {
+  const key =
+    reverseBuffer(Buffer.from(input.hash)).toString('hex') + ':' + input.index;
+  if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
+  cache.__TX_IN_CACHE[key] = 1;
+}
+
 function isFinalized(input: PsbtInput): boolean {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
@@ -469,7 +497,7 @@ function getHashAndSighashType(
   inputIndex: number,
   pubkey: Buffer,
   unsignedTx: Transaction,
-  cache: NonWitnessUtxoCache,
+  cache: PsbtCache,
 ): {
   hash: Buffer;
   sighashType: number;
@@ -630,7 +658,7 @@ const getHashForSig = (
   inputIndex: number,
   input: PsbtInput,
   unsignedTx: Transaction,
-  cache: NonWitnessUtxoCache,
+  cache: PsbtCache,
 ): HashForSigData => {
   const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
   let hash: Buffer;
@@ -780,7 +808,7 @@ function getScriptFromInput(
   inputIndex: number,
   input: PsbtInput,
   unsignedTx: Transaction,
-  cache: NonWitnessUtxoCache,
+  cache: PsbtCache,
 ): GetScriptReturn {
   const res: GetScriptReturn = {
     script: null,
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index fae4fff..a708c21 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -7,13 +7,14 @@ import { Transaction } from './transaction';
 export declare class Psbt extends PsbtBase {
     static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>;
     static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>;
-    private __NON_WITNESS_UTXO_CACHE;
+    private __CACHE;
     private __TX;
     private __TX_BUF_CACHE?;
     private __FEE_RATE?;
     private __EXTRACTED_TX?;
     private opts;
     constructor(opts?: PsbtOptsOptional);
+    readonly inputCount: number;
     setMaximumFeeRate(satoshiPerByte: number): void;
     setVersion(version: number): this;
     setLocktime(locktime: number): this;

From 5f26654802a5b487fc99a7e82f5fbc89c977845c Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 5 Jul 2019 18:26:52 +0900
Subject: [PATCH 064/111] Add tests

---
 src/psbt.js    | 20 +++++++++-----------
 test/psbt.js   | 28 ++++++++++++++++++++++++++++
 ts_src/psbt.ts | 22 ++++++++++------------
 3 files changed, 47 insertions(+), 23 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index e797949..529f023 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -10,6 +10,10 @@ const payments = require('./payments');
 const bscript = require('./script');
 const transaction_1 = require('./transaction');
 const varuint = require('varuint-bitcoin');
+const DEFAULT_OPTS = {
+  network: networks_1.bitcoin,
+  maximumFeeRate: 5000,
+};
 class Psbt extends bip174_1.Psbt {
   constructor(opts = {}) {
     super();
@@ -202,11 +206,11 @@ class Psbt extends bip174_1.Psbt {
       const satoshis = feeRate * vsize;
       if (feeRate >= this.opts.maximumFeeRate) {
         throw new Error(
-          `Warning: You are paying around ${satoshis / 1e8} in fees, which ` +
-            `is ${feeRate} satoshi per byte for a transaction with a VSize of ` +
-            `${vsize} bytes (segwit counted as 0.25 byte per byte)\n` +
-            `Use setMaximumFeeRate method to raise your threshold, or pass ` +
-            `true to the first arg of extractTransaction.`,
+          `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
+            `fees, which is ${feeRate} satoshi per byte for a transaction ` +
+            `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
+            `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
+            `pass true to the first arg of extractTransaction.`,
         );
       }
     }
@@ -254,8 +258,6 @@ class Psbt extends bip174_1.Psbt {
         const vout = this.__TX.ins[idx].index;
         const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout];
         inputAmount += out.value;
-      } else {
-        throw new Error('Missing input value: index #' + idx);
       }
     });
     this.__EXTRACTED_TX = tx;
@@ -341,10 +343,6 @@ class Psbt extends bip174_1.Psbt {
   }
 }
 exports.Psbt = Psbt;
-const DEFAULT_OPTS = {
-  network: networks_1.bitcoin,
-  maximumFeeRate: 5000,
-};
 function addNonWitnessTxCache(cache, input, inputIndex) {
   cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo;
   const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo);
diff --git a/test/psbt.js b/test/psbt.js
index 9a3ac92..ecb78ab 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -97,6 +97,16 @@ describe(`Psbt`, () => {
                     arg.forEach(a => adder(i, initBuffers(attr, a)))
                   } else {
                     adder(i, initBuffers(attr, arg))
+                    if (attr === 'nonWitnessUtxo') {
+                      const first = psbt.inputs[i].nonWitnessUtxo
+                      psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[i] = undefined
+                      const second = psbt.inputs[i].nonWitnessUtxo
+                      psbt.inputs[i].nonWitnessUtxo = Buffer.from([1,2,3])
+                      psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[i] = undefined
+                      const third = psbt.inputs[i].nonWitnessUtxo
+                      assert.ok(first.equals(second))
+                      assert.ok(first.equals(third))
+                    }
                   }
                 }
               }
@@ -140,6 +150,10 @@ describe(`Psbt`, () => {
     it('Finalizes inputs and gives the expected PSBT', () => {
       const psbt =  Psbt.fromBase64(f.psbt)
 
+      assert.throws(() => {
+        psbt.getFeeRate()
+      }, new RegExp('PSBT must be finalized to calculate fee rate'))
+
       psbt.finalizeAllInputs()
 
       assert.strictEqual(psbt.toBase64(), f.result)
@@ -196,6 +210,11 @@ describe(`Psbt`, () => {
             ECPair.fromWIF(f.shouldThrow.WIF),
           )
         }, {message: f.shouldThrow.errorMessage})
+        assert.rejects(async () => {
+          await psbtThatShouldThrow.signInputAsync(
+            f.shouldThrow.inputToCheck,
+          )
+        }, new RegExp('Need Signer to sign input'))
       })
     })
   })
@@ -218,6 +237,11 @@ describe(`Psbt`, () => {
             ECPair.fromWIF(f.shouldThrow.WIF),
           )
         }, {message: f.shouldThrow.errorMessage})
+        assert.throws(() => {
+          psbtThatShouldThrow.signInput(
+            f.shouldThrow.inputToCheck,
+          )
+        }, new RegExp('Need Signer to sign input'))
       })
     })
   })
@@ -252,6 +276,9 @@ describe(`Psbt`, () => {
               console.log(psbt.toBase64())
             }
           })
+          assert.throws(() => {
+            psbt.addInput(f.inputData)
+          }, new RegExp('Duplicate input detected.'))
         }
       })
     })
@@ -307,6 +334,7 @@ describe(`Psbt`, () => {
         index: 0
       });
 
+      assert.strictEqual(psbt.inputCount, 1)
       assert.strictEqual(psbt.__TX.ins[0].sequence, 0xffffffff)
       psbt.setSequence(0, 0)
       assert.strictEqual(psbt.__TX.ins[0].sequence, 0)
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 24a088e..9072033 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -17,6 +17,11 @@ import * as bscript from './script';
 import { Output, Transaction } from './transaction';
 const varuint = require('varuint-bitcoin');
 
+const DEFAULT_OPTS: PsbtOpts = {
+  network: btcNetwork,
+  maximumFeeRate: 5000, // satoshi per byte
+};
+
 export class Psbt extends PsbtBase {
   static fromTransaction<T extends typeof PsbtBase>(
     this: T,
@@ -250,11 +255,11 @@ export class Psbt extends PsbtBase {
       const satoshis = feeRate * vsize;
       if (feeRate >= this.opts.maximumFeeRate) {
         throw new Error(
-          `Warning: You are paying around ${satoshis / 1e8} in fees, which ` +
-            `is ${feeRate} satoshi per byte for a transaction with a VSize of ` +
-            `${vsize} bytes (segwit counted as 0.25 byte per byte)\n` +
-            `Use setMaximumFeeRate method to raise your threshold, or pass ` +
-            `true to the first arg of extractTransaction.`,
+          `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
+            `fees, which is ${feeRate} satoshi per byte for a transaction ` +
+            `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
+            `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
+            `pass true to the first arg of extractTransaction.`,
         );
       }
     }
@@ -303,8 +308,6 @@ export class Psbt extends PsbtBase {
         const vout = this.__TX.ins[idx].index;
         const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output;
         inputAmount += out.value;
-      } else {
-        throw new Error('Missing input value: index #' + idx);
       }
     });
     this.__EXTRACTED_TX = tx;
@@ -436,11 +439,6 @@ interface PsbtOpts {
   maximumFeeRate: number;
 }
 
-const DEFAULT_OPTS = {
-  network: btcNetwork,
-  maximumFeeRate: 5000, // satoshi per byte
-};
-
 function addNonWitnessTxCache(
   cache: PsbtCache,
   input: PsbtInput,

From 02ba6c78d1e4827dd362cd400c343708bf36c0a1 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 5 Jul 2019 19:40:31 +0900
Subject: [PATCH 065/111] Add integration tests with examples

---
 test/integration/transactions-psbt.js | 366 ++++++++++++++++++++++++++
 1 file changed, 366 insertions(+)
 create mode 100644 test/integration/transactions-psbt.js

diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js
new file mode 100644
index 0000000..e48e335
--- /dev/null
+++ b/test/integration/transactions-psbt.js
@@ -0,0 +1,366 @@
+const { describe, it } = require('mocha')
+const assert = require('assert')
+const bitcoin = require('../../')
+const regtestUtils = require('./_regtest')
+const regtest = regtestUtils.network
+
+// See bottom of file for some helper functions used to make the payment objects needed.
+
+describe('bitcoinjs-lib (transactions with psbt)', () => {
+  it('can create (and broadcast via 3PBP) a typical Transaction', async () => {
+    // these are { payment: Payment; keys: ECPair[] }
+    const alice1 = createPayment('p2pkh')
+    const alice2 = createPayment('p2pkh')
+
+    // give Alice 2 unspent outputs
+    const inputData1 = await getInputData(5e4, alice1.payment, false, 'noredeem')
+    const inputData2 = await getInputData(7e4, alice2.payment, false, 'noredeem')
+    {
+      const {
+        hash, // string of txid or Buffer of tx hash. (txid and hash are reverse order)
+        index, // the output index of the txo you are spending
+        nonWitnessUtxo, // the full previous transaction as a Buffer
+      } = inputData1
+      assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData1)
+    }
+
+    // network is only needed if you pass an address to addOutput
+    // using script (Buffer of scriptPubkey) instead will avoid needed network.
+    const psbt = new bitcoin.Psbt({ network: regtest })
+      .addInput(inputData1) // alice1 unspent
+      .addInput(inputData2) // alice2 unspent
+      .addOutput({
+        address: 'mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf',
+        value: 8e4
+      }) // the actual "spend"
+      .addOutput({
+        address: alice2.payment.address, // OR script, which is a Buffer.
+        value: 1e4
+      }) // Alice's change
+    // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee
+
+    // Let's show a new feature with PSBT.
+    // We can have multiple signers sign in parrallel and combine them.
+    // (this is not necessary, but a nice feature)
+
+    // encode to send out to the signers
+    const psbtBaseText = psbt.toBase64()
+
+    // each signer imports
+    const signer1 = bitcoin.Psbt.fromBase64(psbtBaseText)
+    const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText)
+
+    // Alice signs each input with the respective private keys
+    signer1.signInput(0, alice1.keys[0])
+    signer2.signInput(1, alice2.keys[0])
+
+    // encode to send back to combiner (signer 1 and 2 are not near each other)
+    const s1text = signer1.toBase64()
+    const s2text = signer2.toBase64()
+
+    const final1 = bitcoin.Psbt.fromBase64(s1text)
+    const final2 = bitcoin.Psbt.fromBase64(s2text)
+
+    // final1.combine(final2) would give the exact same result
+    psbt.combine(final1, final2)
+
+    // This step it new. Since we separate the signing operation and
+    // the creation of the scriptSig and witness stack, we are able to
+    psbt.finalizeAllInputs()
+    // it returns an array of the success of each input, also a result attribute
+    // which is true if all array items are true.
+
+    // build and broadcast our RegTest network
+    await regtestUtils.broadcast(psbt.extractTransaction().toHex())
+    // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839
+  })
+
+  it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => {
+    const alice1 = createPayment('p2pkh')
+    const inputData1 = await getInputData(2e5, alice1.payment, false, 'noredeem')
+
+    const data = Buffer.from('bitcoinjs-lib', 'utf8')
+    const embed = bitcoin.payments.embed({ data: [data] })
+
+    const psbt = new bitcoin.Psbt({ network: regtest })
+      .addInput(inputData1)
+      .addOutput({
+        script: embed.output,
+        value: 1000
+      })
+      .addOutput({
+        address: regtestUtils.RANDOM_ADDRESS,
+        value: 1e5
+      })
+      .signInput(0, alice1.keys[0])
+
+    psbt.finalizeAllInputs()
+
+    // build and broadcast to the RegTest network
+    await regtestUtils.broadcast(psbt.extractTransaction().toHex())
+  })
+
+  it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => {
+    const multisig = createPayment('p2sh-p2ms(2 of 4)')
+    const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh')
+    {
+      const {
+        hash,
+        index,
+        nonWitnessUtxo,
+        redeemScript, // NEW: P2SH needs to give redeemScript when adding an input.
+      } = inputData1
+      assert.deepStrictEqual({ hash, index, nonWitnessUtxo, redeemScript }, inputData1)
+    }
+
+    const psbt = new bitcoin.Psbt({ network: regtest })
+      .addInput(inputData1)
+      .addOutput({
+        address: regtestUtils.RANDOM_ADDRESS,
+        value: 1e4
+      })
+      .signInput(0, multisig.keys[0])
+      .signInput(0, multisig.keys[2])
+
+    psbt.finalizeAllInputs()
+
+    const tx = psbt.extractTransaction()
+
+    // build and broadcast to the Bitcoin RegTest network
+    await regtestUtils.broadcast(tx.toHex())
+
+    await regtestUtils.verify({
+      txId: tx.getId(),
+      address: regtestUtils.RANDOM_ADDRESS,
+      vout: 0,
+      value: 1e4
+    })
+  })
+
+  it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => {
+    const p2sh = createPayment('p2sh-p2wpkh')
+    const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh')
+    {
+      const {
+        hash,
+        index,
+        witnessUtxo, // NEW: this is an object of the output being spent { script: Buffer; value: Satoshis; }
+        redeemScript,
+      } = inputData
+      assert.deepStrictEqual({ hash, index, witnessUtxo, redeemScript }, inputData)
+    }
+
+    const psbt = new bitcoin.Psbt({ network: regtest })
+      .addInput(inputData)
+      .addOutput({
+        address: regtestUtils.RANDOM_ADDRESS,
+        value: 2e4
+      })
+      .signInput(0, p2sh.keys[0])
+
+    psbt.finalizeAllInputs()
+
+    const tx = psbt.extractTransaction()
+
+    // build and broadcast to the Bitcoin RegTest network
+    await regtestUtils.broadcast(tx.toHex())
+
+    await regtestUtils.verify({
+      txId: tx.getId(),
+      address: regtestUtils.RANDOM_ADDRESS,
+      vout: 0,
+      value: 2e4
+    })
+  })
+
+  it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => {
+
+    // the only thing that changes is you don't give a redeemscript for input data
+
+    const p2wpkh = createPayment('p2wpkh')
+    const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem')
+    {
+      const {
+        hash,
+        index,
+        witnessUtxo,
+      } = inputData
+      assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData)
+    }
+
+    const psbt = new bitcoin.Psbt({ network: regtest })
+      .addInput(inputData)
+      .addOutput({
+        address: regtestUtils.RANDOM_ADDRESS,
+        value: 2e4
+      })
+      .signInput(0, p2wpkh.keys[0])
+
+    psbt.finalizeAllInputs()
+
+    const tx = psbt.extractTransaction()
+
+    // build and broadcast to the Bitcoin RegTest network
+    await regtestUtils.broadcast(tx.toHex())
+
+    await regtestUtils.verify({
+      txId: tx.getId(),
+      address: regtestUtils.RANDOM_ADDRESS,
+      vout: 0,
+      value: 2e4
+    })
+  })
+
+  it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => {
+    const p2wsh = createPayment('p2wsh-p2pk')
+    const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh')
+    {
+      const {
+        hash,
+        index,
+        witnessUtxo,
+        witnessScript, // NEW: A Buffer of the witnessScript
+      } = inputData
+      assert.deepStrictEqual({ hash, index, witnessUtxo, witnessScript }, inputData)
+    }
+
+    const psbt = new bitcoin.Psbt({ network: regtest })
+      .addInput(inputData)
+      .addOutput({
+        address: regtestUtils.RANDOM_ADDRESS,
+        value: 2e4
+      })
+      .signInput(0, p2wsh.keys[0])
+
+    psbt.finalizeAllInputs()
+
+    const tx = psbt.extractTransaction()
+
+    // build and broadcast to the Bitcoin RegTest network
+    await regtestUtils.broadcast(tx.toHex())
+
+    await regtestUtils.verify({
+      txId: tx.getId(),
+      address: regtestUtils.RANDOM_ADDRESS,
+      vout: 0,
+      value: 2e4
+    })
+  })
+
+  it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => {
+    const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)')
+    const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh')
+    {
+      const {
+        hash,
+        index,
+        witnessUtxo,
+        redeemScript,
+        witnessScript,
+      } = inputData
+      assert.deepStrictEqual({ hash, index, witnessUtxo, redeemScript, witnessScript }, inputData)
+    }
+
+    const psbt = new bitcoin.Psbt({ network: regtest })
+      .addInput(inputData)
+      .addOutput({
+        address: regtestUtils.RANDOM_ADDRESS,
+        value: 2e4
+      })
+      .signInput(0, p2sh.keys[0])
+      .signInput(0, p2sh.keys[2])
+      .signInput(0, p2sh.keys[3])
+
+    psbt.finalizeAllInputs()
+
+    const tx = psbt.extractTransaction()
+
+    // build and broadcast to the Bitcoin RegTest network
+    await regtestUtils.broadcast(tx.toHex())
+
+    await regtestUtils.verify({
+      txId: tx.getId(),
+      address: regtestUtils.RANDOM_ADDRESS,
+      vout: 0,
+      value: 2e4
+    })
+  })
+})
+
+function createPayment(_type) {
+  const splitType = _type.split('-').reverse();
+  const isMultisig = splitType[0].slice(0, 4) === 'p2ms';
+  const keys = [];
+  let m;
+  if (isMultisig) {
+    const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/)
+    m = parseInt(match[1])
+    let n = parseInt(match[2])
+    while (n > 1) {
+      keys.push(bitcoin.ECPair.makeRandom({ network: regtest }));
+      n--
+    }
+  }
+  keys.push(bitcoin.ECPair.makeRandom({ network: regtest }));
+
+  let payment;
+  splitType.forEach(type => {
+    if (type.slice(0, 4) === 'p2ms') {
+      payment = bitcoin.payments.p2ms({
+        m,
+        pubkeys: keys.map(key => key.publicKey).sort(),
+        network: regtest,
+      });
+    } else if (['p2sh', 'p2wsh'].indexOf(type) > -1) {
+      payment = bitcoin.payments[type]({
+        redeem: payment,
+        network: regtest,
+      });
+    } else {
+      payment = bitcoin.payments[type]({
+        pubkey: keys[0].publicKey,
+        network: regtest,
+      });
+    }
+  });
+
+  return {
+    payment,
+    keys,
+  };
+}
+
+function getWitnessUtxo(out) {
+  delete out.address;
+  out.script = Buffer.from(out.script, 'hex');
+  return out;
+}
+
+async function getInputData(amount, payment, isSegwit, redeemType) {
+  const unspent = await regtestUtils.faucetComplex(payment.output, amount);
+  const utx = await regtestUtils.fetch(unspent.txId);
+  // for non segwit inputs, you must pass the full transaction buffer
+  const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex');
+  // for segwit inputs, you only need the output script and value as an object.
+  const witnessUtxo = getWitnessUtxo(utx.outs[unspent.vout]);
+  const mixin = isSegwit ? { witnessUtxo } : { nonWitnessUtxo };
+  const mixin2 = {};
+  switch (redeemType) {
+    case 'p2sh':
+      mixin2.redeemScript = payment.redeem.output;
+      break;
+    case 'p2wsh':
+      mixin2.witnessScript = payment.redeem.output;
+      break;
+    case 'p2sh-p2wsh':
+      mixin2.witnessScript = payment.redeem.redeem.output;
+      mixin2.redeemScript = payment.redeem.output;
+      break;
+  }
+  return {
+    hash: unspent.txId,
+    index: unspent.vout,
+    ...mixin,
+    ...mixin2,
+  };
+}

From d0d94c7f06715b74b6447339889b5dbe3bc4d235 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Mon, 8 Jul 2019 15:46:06 +0900
Subject: [PATCH 066/111] Add signature verify method

---
 src/psbt.js                           | 40 +++++++++++-
 test/integration/transactions-psbt.js | 92 +++++++++++++++++++++++++--
 ts_src/psbt.ts                        | 50 +++++++++++++--
 types/psbt.d.ts                       |  1 +
 4 files changed, 170 insertions(+), 13 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 529f023..3bb1b3f 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -5,6 +5,7 @@ const utils_1 = require('bip174/src/lib/utils');
 const address_1 = require('./address');
 const bufferutils_1 = require('./bufferutils');
 const crypto_1 = require('./crypto');
+const ecpair_1 = require('./ecpair');
 const networks_1 = require('./networks');
 const payments = require('./payments');
 const bscript = require('./script');
@@ -304,6 +305,39 @@ class Psbt extends bip174_1.Psbt {
     this.clearFinalizedInput(inputIndex);
     return true;
   }
+  validateSignatures(inputIndex, pubkey) {
+    const input = this.inputs[inputIndex];
+    const partialSig = (input || {}).partialSig;
+    if (!input || !partialSig || partialSig.length < 1)
+      throw new Error('No signatures to validate');
+    const mySigs = pubkey
+      ? partialSig.filter(sig => sig.pubkey.equals(pubkey))
+      : partialSig;
+    if (mySigs.length < 1) throw new Error('No signatures for this pubkey');
+    const results = [];
+    let hashCache;
+    let scriptCache;
+    let sighashCache;
+    for (const pSig of mySigs) {
+      const sig = bscript.signature.decode(pSig.signature);
+      const { hash, script } =
+        sighashCache !== sig.hashType
+          ? getHashForSig(
+              inputIndex,
+              Object.assign({}, input, { sighashType: sig.hashType }),
+              this.__TX,
+              this.__CACHE,
+            )
+          : { hash: hashCache, script: scriptCache };
+      sighashCache = sig.hashType;
+      hashCache = hash;
+      scriptCache = script;
+      checkScriptForPubkey(pSig.pubkey, script, 'verify');
+      const keypair = ecpair_1.fromPublicKey(pSig.pubkey);
+      results.push(keypair.verify(hash, sig.signature));
+    }
+    return results.every(res => res === true);
+  }
   signInput(inputIndex, keyPair) {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
@@ -391,7 +425,7 @@ function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx, cache) {
     unsignedTx,
     cache,
   );
-  checkScriptForPubkey(pubkey, script);
+  checkScriptForPubkey(pubkey, script, 'sign');
   return {
     hash,
     sighashType,
@@ -494,7 +528,7 @@ function canFinalize(input, script, scriptType) {
       return false;
   }
 }
-function checkScriptForPubkey(pubkey, script) {
+function checkScriptForPubkey(pubkey, script, action) {
   const pubkeyHash = crypto_1.hash160(pubkey);
   const decompiled = bscript.decompile(script);
   if (decompiled === null) throw new Error('Unknown script error');
@@ -504,7 +538,7 @@ function checkScriptForPubkey(pubkey, script) {
   });
   if (!hasKey) {
     throw new Error(
-      `Can not sign for this input with the key ${pubkey.toString('hex')}`,
+      `Can not ${action} for this input with the key ${pubkey.toString('hex')}`,
     );
   }
 }
diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js
index e48e335..9fb76f7 100644
--- a/test/integration/transactions-psbt.js
+++ b/test/integration/transactions-psbt.js
@@ -7,6 +7,65 @@ const regtest = regtestUtils.network
 // See bottom of file for some helper functions used to make the payment objects needed.
 
 describe('bitcoinjs-lib (transactions with psbt)', () => {
+  it('can create a 1-to-1 Transaction', () => {
+    const alice = bitcoin.ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr')
+    const psbt = new bitcoin.Psbt()
+    psbt.setVersion(2) // These are defaults. This line is not needed.
+    psbt.setLocktime(0) // These are defaults. This line is not needed.
+    psbt.addInput({
+      // if hash is string, txid, if hash is Buffer, is reversed compared to txid
+      hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e',
+      index: 0,
+      sequence: 0xffffffff, // These are defaults. This line is not needed.
+
+      // non-segwit inputs now require passing the whole previous tx as Buffer
+      nonWitnessUtxo: Buffer.from(
+        '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' +
+        '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' +
+        'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' +
+        '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' +
+        '631e5e1e66009ce3710ceea5b1ad13ffffffff01' +
+        // value in satoshis (Int64LE) = 0x015f90 = 90000
+        '905f010000000000' +
+        // scriptPubkey length
+        '19' +
+        // scriptPubkey
+        '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' +
+        // locktime
+        '00000000',
+        'hex',
+      ),
+
+      // // If this input was segwit, instead of nonWitnessUtxo, you would add
+      // // a witnessUtxo as follows. The scriptPubkey and the value only are needed.
+      // witnessUtxo: {
+      //   script: Buffer.from(
+      //     '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac',
+      //     'hex',
+      //   ),
+      //   value: 90000,
+      // },
+
+      // Not featured here: redeemScript. A Buffer of the redeemScript
+    })
+    psbt.addOutput({
+      address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp',
+      value: 80000
+    })
+    psbt.signInput(0, alice)
+    psbt.validateSignatures(0)
+    psbt.finalizeAllInputs()
+    assert.strictEqual(
+      psbt.extractTransaction().toHex(),
+      '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' +
+      'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' +
+      'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' +
+      '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' +
+      'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' +
+      '08a22724efa6f6a07b0ec4c79aa88ac00000000',
+    )
+  })
+
   it('can create (and broadcast via 3PBP) a typical Transaction', async () => {
     // these are { payment: Payment; keys: ECPair[] }
     const alice1 = createPayment('p2pkh')
@@ -64,6 +123,12 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
     // final1.combine(final2) would give the exact same result
     psbt.combine(final1, final2)
 
+    // Finalizer wants to check all signatures are valid before finalizing.
+    // If the finalizer wants to check for specific pubkeys, the second arg
+    // can be passed. See the first multisig example below.
+    assert.strictEqual(psbt.validateSignatures(0), true)
+    assert.strictEqual(psbt.validateSignatures(1), true)
+
     // This step it new. Since we separate the signing operation and
     // the creation of the scriptSig and witness stack, we are able to
     psbt.finalizeAllInputs()
@@ -94,6 +159,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       })
       .signInput(0, alice1.keys[0])
 
+    assert.strictEqual(psbt.validateSignatures(0), true)
     psbt.finalizeAllInputs()
 
     // build and broadcast to the RegTest network
@@ -122,6 +188,11 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       .signInput(0, multisig.keys[0])
       .signInput(0, multisig.keys[2])
 
+    assert.strictEqual(psbt.validateSignatures(0), true)
+    assert.strictEqual(psbt.validateSignatures(0, multisig.keys[0].publicKey), true)
+    assert.throws(() => {
+      psbt.validateSignatures(0, multisig.keys[3].publicKey)
+    }, new RegExp('No signatures for this pubkey'))
     psbt.finalizeAllInputs()
 
     const tx = psbt.extractTransaction()
@@ -158,6 +229,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       })
       .signInput(0, p2sh.keys[0])
 
+    assert.strictEqual(psbt.validateSignatures(0), true)
     psbt.finalizeAllInputs()
 
     const tx = psbt.extractTransaction()
@@ -196,6 +268,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       })
       .signInput(0, p2wpkh.keys[0])
 
+    assert.strictEqual(psbt.validateSignatures(0), true)
     psbt.finalizeAllInputs()
 
     const tx = psbt.extractTransaction()
@@ -232,6 +305,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       })
       .signInput(0, p2wsh.keys[0])
 
+    assert.strictEqual(psbt.validateSignatures(0), true)
     psbt.finalizeAllInputs()
 
     const tx = psbt.extractTransaction()
@@ -271,6 +345,11 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       .signInput(0, p2sh.keys[2])
       .signInput(0, p2sh.keys[3])
 
+    assert.strictEqual(psbt.validateSignatures(0), true)
+    assert.strictEqual(psbt.validateSignatures(0, p2sh.keys[3].publicKey), true)
+    assert.throws(() => {
+      psbt.validateSignatures(0, p2sh.keys[1].publicKey)
+    }, new RegExp('No signatures for this pubkey'))
     psbt.finalizeAllInputs()
 
     const tx = psbt.extractTransaction()
@@ -287,7 +366,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
   })
 })
 
-function createPayment(_type) {
+function createPayment(_type, network) {
+  network = network || regtest
   const splitType = _type.split('-').reverse();
   const isMultisig = splitType[0].slice(0, 4) === 'p2ms';
   const keys = [];
@@ -297,11 +377,11 @@ function createPayment(_type) {
     m = parseInt(match[1])
     let n = parseInt(match[2])
     while (n > 1) {
-      keys.push(bitcoin.ECPair.makeRandom({ network: regtest }));
+      keys.push(bitcoin.ECPair.makeRandom({ network }));
       n--
     }
   }
-  keys.push(bitcoin.ECPair.makeRandom({ network: regtest }));
+  keys.push(bitcoin.ECPair.makeRandom({ network }));
 
   let payment;
   splitType.forEach(type => {
@@ -309,17 +389,17 @@ function createPayment(_type) {
       payment = bitcoin.payments.p2ms({
         m,
         pubkeys: keys.map(key => key.publicKey).sort(),
-        network: regtest,
+        network,
       });
     } else if (['p2sh', 'p2wsh'].indexOf(type) > -1) {
       payment = bitcoin.payments[type]({
         redeem: payment,
-        network: regtest,
+        network,
       });
     } else {
       payment = bitcoin.payments[type]({
         pubkey: keys[0].publicKey,
-        network: regtest,
+        network,
       });
     }
   });
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 9072033..6ccef08 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -10,7 +10,11 @@ import { checkForInput } from 'bip174/src/lib/utils';
 import { toOutputScript } from './address';
 import { reverseBuffer } from './bufferutils';
 import { hash160 } from './crypto';
-import { Signer, SignerAsync } from './ecpair';
+import {
+  fromPublicKey as ecPairFromPublicKey,
+  Signer,
+  SignerAsync,
+} from './ecpair';
 import { bitcoin as btcNetwork, Network } from './networks';
 import * as payments from './payments';
 import * as bscript from './script';
@@ -367,6 +371,40 @@ export class Psbt extends PsbtBase {
     return true;
   }
 
+  validateSignatures(inputIndex: number, pubkey?: Buffer): boolean {
+    const input = this.inputs[inputIndex];
+    const partialSig = (input || {}).partialSig;
+    if (!input || !partialSig || partialSig.length < 1)
+      throw new Error('No signatures to validate');
+    const mySigs = pubkey
+      ? partialSig.filter(sig => sig.pubkey.equals(pubkey))
+      : partialSig;
+    if (mySigs.length < 1) throw new Error('No signatures for this pubkey');
+    const results: boolean[] = [];
+    let hashCache: Buffer;
+    let scriptCache: Buffer;
+    let sighashCache: number;
+    for (const pSig of mySigs) {
+      const sig = bscript.signature.decode(pSig.signature);
+      const { hash, script } =
+        sighashCache! !== sig.hashType
+          ? getHashForSig(
+              inputIndex,
+              Object.assign({}, input, { sighashType: sig.hashType }),
+              this.__TX,
+              this.__CACHE,
+            )
+          : { hash: hashCache!, script: scriptCache! };
+      sighashCache = sig.hashType;
+      hashCache = hash;
+      scriptCache = script;
+      checkScriptForPubkey(pSig.pubkey, script, 'verify');
+      const keypair = ecPairFromPublicKey(pSig.pubkey);
+      results.push(keypair.verify(hash, sig.signature));
+    }
+    return results.every(res => res === true);
+  }
+
   signInput(inputIndex: number, keyPair: Signer): this {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
@@ -507,7 +545,7 @@ function getHashAndSighashType(
     unsignedTx,
     cache,
   );
-  checkScriptForPubkey(pubkey, script);
+  checkScriptForPubkey(pubkey, script, 'sign');
   return {
     hash,
     sighashType,
@@ -628,7 +666,11 @@ function canFinalize(
   }
 }
 
-function checkScriptForPubkey(pubkey: Buffer, script: Buffer): void {
+function checkScriptForPubkey(
+  pubkey: Buffer,
+  script: Buffer,
+  action: string,
+): void {
   const pubkeyHash = hash160(pubkey);
 
   const decompiled = bscript.decompile(script);
@@ -641,7 +683,7 @@ function checkScriptForPubkey(pubkey: Buffer, script: Buffer): void {
 
   if (!hasKey) {
     throw new Error(
-      `Can not sign for this input with the key ${pubkey.toString('hex')}`,
+      `Can not ${action} for this input with the key ${pubkey.toString('hex')}`,
     );
   }
 }
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index a708c21..1f9ee7b 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -29,6 +29,7 @@ export declare class Psbt extends PsbtBase {
         inputResults: boolean[];
     };
     finalizeInput(inputIndex: number): boolean;
+    validateSignatures(inputIndex: number, pubkey?: Buffer): boolean;
     signInput(inputIndex: number, keyPair: Signer): this;
     signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>;
 }

From f66b568e4d86c86368b89cb470b96e650c56f499 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Mon, 8 Jul 2019 16:30:59 +0900
Subject: [PATCH 067/111] Add sign all inputs method

---
 src/psbt.js                           | 49 ++++++++++++++++++++++++
 test/integration/transactions-psbt.js |  9 ++++-
 ts_src/psbt.ts                        | 55 +++++++++++++++++++++++++++
 types/psbt.d.ts                       |  2 +
 4 files changed, 113 insertions(+), 2 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 3bb1b3f..d89752b 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -338,6 +338,55 @@ class Psbt extends bip174_1.Psbt {
     }
     return results.every(res => res === true);
   }
+  sign(keyPair) {
+    if (!keyPair || !keyPair.publicKey)
+      throw new Error('Need Signer to sign input');
+    // TODO: Add a pubkey/pubkeyhash cache to each input
+    // as input information is added, then eventually
+    // optimize this method.
+    const results = [];
+    for (const [i] of this.inputs.entries()) {
+      try {
+        this.signInput(i, keyPair);
+        results.push(true);
+      } catch (err) {
+        results.push(false);
+      }
+    }
+    if (results.every(v => v === false)) {
+      throw new Error('No inputs were signed');
+    }
+    return this;
+  }
+  signAsync(keyPair) {
+    return new Promise((resolve, reject) => {
+      if (!keyPair || !keyPair.publicKey)
+        return reject(new Error('Need Signer to sign input'));
+      // TODO: Add a pubkey/pubkeyhash cache to each input
+      // as input information is added, then eventually
+      // optimize this method.
+      const results = [];
+      const promises = [];
+      for (const [i] of this.inputs.entries()) {
+        promises.push(
+          this.signInputAsync(i, keyPair).then(
+            () => {
+              results.push(true);
+            },
+            () => {
+              results.push(false);
+            },
+          ),
+        );
+      }
+      return Promise.all(promises).then(() => {
+        if (results.every(v => v === false)) {
+          return reject(new Error('No inputs were signed'));
+        }
+        resolve();
+      });
+    });
+  }
   signInput(inputIndex, keyPair) {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js
index 9fb76f7..37348c1 100644
--- a/test/integration/transactions-psbt.js
+++ b/test/integration/transactions-psbt.js
@@ -110,8 +110,13 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
     const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText)
 
     // Alice signs each input with the respective private keys
-    signer1.signInput(0, alice1.keys[0])
-    signer2.signInput(1, alice2.keys[0])
+    // signInput and signInputAsync are better
+    // (They take the input index explicitly as the first arg)
+    signer1.sign(alice1.keys[0])
+    signer2.sign(alice2.keys[0])
+
+    // If your signer object's sign method returns a promise, use the following
+    // await signer2.signAsync(alice2.keys[0])
 
     // encode to send back to combiner (signer 1 and 2 are not near each other)
     const s1text = signer1.toBase64()
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 6ccef08..3461b56 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -405,6 +405,61 @@ export class Psbt extends PsbtBase {
     return results.every(res => res === true);
   }
 
+  sign(keyPair: Signer): this {
+    if (!keyPair || !keyPair.publicKey)
+      throw new Error('Need Signer to sign input');
+
+    // TODO: Add a pubkey/pubkeyhash cache to each input
+    // as input information is added, then eventually
+    // optimize this method.
+    const results: boolean[] = [];
+    for (const [i] of this.inputs.entries()) {
+      try {
+        this.signInput(i, keyPair);
+        results.push(true);
+      } catch (err) {
+        results.push(false);
+      }
+    }
+    if (results.every(v => v === false)) {
+      throw new Error('No inputs were signed');
+    }
+    return this;
+  }
+
+  signAsync(keyPair: SignerAsync): Promise<void> {
+    return new Promise(
+      (resolve, reject): any => {
+        if (!keyPair || !keyPair.publicKey)
+          return reject(new Error('Need Signer to sign input'));
+
+        // TODO: Add a pubkey/pubkeyhash cache to each input
+        // as input information is added, then eventually
+        // optimize this method.
+        const results: boolean[] = [];
+        const promises: Array<Promise<void>> = [];
+        for (const [i] of this.inputs.entries()) {
+          promises.push(
+            this.signInputAsync(i, keyPair).then(
+              () => {
+                results.push(true);
+              },
+              () => {
+                results.push(false);
+              },
+            ),
+          );
+        }
+        return Promise.all(promises).then(() => {
+          if (results.every(v => v === false)) {
+            return reject(new Error('No inputs were signed'));
+          }
+          resolve();
+        });
+      },
+    );
+  }
+
   signInput(inputIndex: number, keyPair: Signer): this {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 1f9ee7b..b2893b8 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -30,6 +30,8 @@ export declare class Psbt extends PsbtBase {
     };
     finalizeInput(inputIndex: number): boolean;
     validateSignatures(inputIndex: number, pubkey?: Buffer): boolean;
+    sign(keyPair: Signer): this;
+    signAsync(keyPair: SignerAsync): Promise<void>;
     signInput(inputIndex: number, keyPair: Signer): this;
     signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>;
 }

From e15b51536792659f1afb113379244514af0c0699 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Mon, 8 Jul 2019 17:40:21 +0900
Subject: [PATCH 068/111] Add tests

---
 test/psbt.js | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)

diff --git a/test/psbt.js b/test/psbt.js
index ecb78ab..cd45c1e 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -154,6 +154,20 @@ describe(`Psbt`, () => {
         psbt.getFeeRate()
       }, new RegExp('PSBT must be finalized to calculate fee rate'))
 
+      const pubkey = Buffer.from(
+        '029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f',
+        'hex',
+      )
+      assert.strictEqual(psbt.validateSignatures(0), true)
+      assert.strictEqual(psbt.validateSignatures(0, pubkey), true)
+      assert.throws(() => {
+        pubkey[32] = 42
+        psbt.validateSignatures(0, pubkey)
+      }, new RegExp('No signatures for this pubkey'))
+      assert.throws(() => {
+        psbt.validateSignatures(42)
+      }, new RegExp('No signatures to validate'))
+
       psbt.finalizeAllInputs()
 
       assert.strictEqual(psbt.toBase64(), f.result)
@@ -246,6 +260,54 @@ describe(`Psbt`, () => {
     })
   })
 
+  describe('signAsync', () => {
+    fixtures.signInput.checks.forEach(f => {
+      if (f.description === 'checks the input exists') return
+      it(f.description, async () => {
+        const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+        assert.doesNotReject(async () => {
+          await psbtThatShouldsign.signAsync(
+            ECPair.fromWIF(f.shouldSign.WIF),
+          )
+        })
+
+        const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+        assert.rejects(async () => {
+          await psbtThatShouldThrow.signAsync(
+            ECPair.fromWIF(f.shouldThrow.WIF),
+          )
+        }, new RegExp('No inputs were signed'))
+        assert.rejects(async () => {
+          await psbtThatShouldThrow.signAsync()
+        }, new RegExp('Need Signer to sign input'))
+      })
+    })
+  })
+
+  describe('sign', () => {
+    fixtures.signInput.checks.forEach(f => {
+      if (f.description === 'checks the input exists') return
+      it(f.description, () => {
+        const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+        assert.doesNotThrow(() => {
+          psbtThatShouldsign.sign(
+            ECPair.fromWIF(f.shouldSign.WIF),
+          )
+        })
+
+        const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+        assert.throws(() => {
+          psbtThatShouldThrow.sign(
+            ECPair.fromWIF(f.shouldThrow.WIF),
+          )
+        }, new RegExp('No inputs were signed'))
+        assert.throws(() => {
+          psbtThatShouldThrow.sign()
+        }, new RegExp('Need Signer to sign input'))
+      })
+    })
+  })
+
   describe('fromTransaction', () => {
     fixtures.fromTransaction.forEach(f => {
       it('Creates the expected PSBT from a transaction buffer', () => {
@@ -363,6 +425,44 @@ describe(`Psbt`, () => {
     })
   })
 
+  describe('create 1-to-1 transaction', () => {
+    const alice = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr')
+    const psbt = new Psbt()
+    psbt.addInput({
+      hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e',
+      index: 0,
+      nonWitnessUtxo: Buffer.from(
+        '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' +
+        '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' +
+        'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' +
+        '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' +
+        '631e5e1e66009ce3710ceea5b1ad13ffffffff01905f0100000000001976a9148bb' +
+        'c95d2709c71607c60ee3f097c1217482f518d88ac00000000',
+        'hex',
+      ),
+      sighashType: 1,
+    })
+    psbt.addOutput({
+      address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp',
+      value: 80000
+    })
+    psbt.signInput(0, alice)
+    assert.throws(() => {
+      psbt.setVersion(3)
+    }, new RegExp('Can not modify transaction, signatures exist.'))
+    psbt.validateSignatures(0)
+    psbt.finalizeAllInputs()
+    assert.strictEqual(
+      psbt.extractTransaction().toHex(),
+      '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' +
+      'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' +
+      'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' +
+      '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' +
+      'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' +
+      '08a22724efa6f6a07b0ec4c79aa88ac00000000',
+    )
+  })
+
   describe('Method return types', () => {
     it('fromTransaction returns Psbt type (not base class)', () => {
       const psbt = Psbt.fromTransaction(Buffer.from([2,0,0,0,0,0,0,0,0,0]));

From 09fcb1c6ee85d13a09c5c877020c67fc77b8b280 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 10:57:41 +0900
Subject: [PATCH 069/111] Use function keyword

---
 src/psbt.js    | 60 ++++++++++++++++++++++---------------------
 ts_src/psbt.ts | 70 ++++++++++++++++++++++++++------------------------
 2 files changed, 68 insertions(+), 62 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index d89752b..8ddf6ab 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -591,7 +591,7 @@ function checkScriptForPubkey(pubkey, script, action) {
     );
   }
 }
-const getHashForSig = (inputIndex, input, unsignedTx, cache) => {
+function getHashForSig(inputIndex, input, unsignedTx, cache) {
   const sighashType =
     input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
   let hash;
@@ -672,45 +672,45 @@ const getHashForSig = (inputIndex, input, unsignedTx, cache) => {
     sighashType,
     hash,
   };
-};
-const scriptCheckerFactory = (payment, paymentScriptName) => (
-  inputIndex,
-  scriptPubKey,
-  redeemScript,
-) => {
-  const redeemScriptOutput = payment({
-    redeem: { output: redeemScript },
-  }).output;
-  if (!scriptPubKey.equals(redeemScriptOutput)) {
-    throw new Error(
-      `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
-    );
-  }
-};
+}
+function scriptCheckerFactory(payment, paymentScriptName) {
+  return (inputIndex, scriptPubKey, redeemScript) => {
+    const redeemScriptOutput = payment({
+      redeem: { output: redeemScript },
+    }).output;
+    if (!scriptPubKey.equals(redeemScriptOutput)) {
+      throw new Error(
+        `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+      );
+    }
+  };
+}
 const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
 const checkWitnessScript = scriptCheckerFactory(
   payments.p2wsh,
   'Witness script',
 );
-const isPaymentFactory = payment => script => {
-  try {
-    payment({ output: script });
-    return true;
-  } catch (err) {
-    return false;
-  }
-};
+function isPaymentFactory(payment) {
+  return script => {
+    try {
+      payment({ output: script });
+      return true;
+    } catch (err) {
+      return false;
+    }
+  };
+}
 const isP2WPKH = isPaymentFactory(payments.p2wpkh);
 const isP2PKH = isPaymentFactory(payments.p2pkh);
 const isP2MS = isPaymentFactory(payments.p2ms);
 const isP2PK = isPaymentFactory(payments.p2pk);
-const classifyScript = script => {
+function classifyScript(script) {
   if (isP2WPKH(script)) return 'witnesspubkeyhash';
   if (isP2PKH(script)) return 'pubkeyhash';
   if (isP2MS(script)) return 'multisig';
   if (isP2PK(script)) return 'pubkey';
   return 'nonstandard';
-};
+}
 function getScriptFromInput(inputIndex, input, unsignedTx, cache) {
   const res = {
     script: null,
@@ -748,11 +748,11 @@ function getScriptFromInput(inputIndex, input, unsignedTx, cache) {
   }
   return res;
 }
-const hasSigs = (neededSigs, partialSig) => {
+function hasSigs(neededSigs, partialSig) {
   if (!partialSig) return false;
   if (partialSig.length > neededSigs) throw new Error('Too many signatures');
   return partialSig.length === neededSigs;
-};
+}
 function witnessStackToScriptWitness(witness) {
   let buffer = Buffer.allocUnsafe(0);
   function writeSlice(slice) {
@@ -797,7 +797,9 @@ function scriptWitnessToWitnessStack(buffer) {
   }
   return readVector();
 }
-const range = n => [...Array(n).keys()];
+function range(n) {
+  return [...Array(n).keys()];
+}
 function checkTxEmpty(tx) {
   const isEmpty = tx.ins.every(
     input =>
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 3461b56..122bba8 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -749,12 +749,12 @@ interface HashForSigData {
   sighashType: number;
 }
 
-const getHashForSig = (
+function getHashForSig(
   inputIndex: number,
   input: PsbtInput,
   unsignedTx: Transaction,
   cache: PsbtCache,
-): HashForSigData => {
+): HashForSigData {
   const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
   let hash: Buffer;
   let script: Buffer;
@@ -839,28 +839,30 @@ const getHashForSig = (
     sighashType,
     hash,
   };
-};
+}
 
 type ScriptCheckerFunction = (idx: number, spk: Buffer, rs: Buffer) => void;
 
-const scriptCheckerFactory = (
+function scriptCheckerFactory(
   payment: any,
   paymentScriptName: string,
-): ScriptCheckerFunction => (
-  inputIndex: number,
-  scriptPubKey: Buffer,
-  redeemScript: Buffer,
-): void => {
-  const redeemScriptOutput = payment({
-    redeem: { output: redeemScript },
-  }).output as Buffer;
+): ScriptCheckerFunction {
+  return (
+    inputIndex: number,
+    scriptPubKey: Buffer,
+    redeemScript: Buffer,
+  ): void => {
+    const redeemScriptOutput = payment({
+      redeem: { output: redeemScript },
+    }).output as Buffer;
 
-  if (!scriptPubKey.equals(redeemScriptOutput)) {
-    throw new Error(
-      `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
-    );
-  }
-};
+    if (!scriptPubKey.equals(redeemScriptOutput)) {
+      throw new Error(
+        `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+      );
+    }
+  };
+}
 
 const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
 const checkWitnessScript = scriptCheckerFactory(
@@ -870,28 +872,28 @@ const checkWitnessScript = scriptCheckerFactory(
 
 type isPaymentFunction = (script: Buffer) => boolean;
 
-const isPaymentFactory = (payment: any): isPaymentFunction => (
-  script: Buffer,
-): boolean => {
-  try {
-    payment({ output: script });
-    return true;
-  } catch (err) {
-    return false;
-  }
-};
+function isPaymentFactory(payment: any): isPaymentFunction {
+  return (script: Buffer): boolean => {
+    try {
+      payment({ output: script });
+      return true;
+    } catch (err) {
+      return false;
+    }
+  };
+}
 const isP2WPKH = isPaymentFactory(payments.p2wpkh);
 const isP2PKH = isPaymentFactory(payments.p2pkh);
 const isP2MS = isPaymentFactory(payments.p2ms);
 const isP2PK = isPaymentFactory(payments.p2pk);
 
-const classifyScript = (script: Buffer): string => {
+function classifyScript(script: Buffer): string {
   if (isP2WPKH(script)) return 'witnesspubkeyhash';
   if (isP2PKH(script)) return 'pubkeyhash';
   if (isP2MS(script)) return 'multisig';
   if (isP2PK(script)) return 'pubkey';
   return 'nonstandard';
-};
+}
 
 interface GetScriptReturn {
   script: Buffer | null;
@@ -942,11 +944,11 @@ function getScriptFromInput(
   return res;
 }
 
-const hasSigs = (neededSigs: number, partialSig?: any[]): boolean => {
+function hasSigs(neededSigs: number, partialSig?: any[]): boolean {
   if (!partialSig) return false;
   if (partialSig.length > neededSigs) throw new Error('Too many signatures');
   return partialSig.length === neededSigs;
-};
+}
 
 function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
   let buffer = Buffer.allocUnsafe(0);
@@ -1006,7 +1008,9 @@ function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
   return readVector();
 }
 
-const range = (n: number): number[] => [...Array(n).keys()];
+function range(n: number): number[] {
+  return [...Array(n).keys()];
+}
 
 function checkTxEmpty(tx: Transaction): void {
   const isEmpty = tx.ins.every(

From 36a966cfcdafd9245d60e31d26c619981d0240fa Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 11:06:39 +0900
Subject: [PATCH 070/111] Check actual sighash flags instead of psbtInput one

---
 src/psbt.js    | 43 +++++++++++++++++++------------------------
 ts_src/psbt.ts | 42 ++++++++++++++++++------------------------
 2 files changed, 37 insertions(+), 48 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 8ddf6ab..c3a47ab 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -815,32 +815,27 @@ function checkTxEmpty(tx) {
 function checkInputsForPartialSig(inputs, action) {
   inputs.forEach(input => {
     let throws = false;
-    if ((input.partialSig || []).length > 0) {
-      if (input.sighashType !== undefined) {
-        const whitelist = [];
-        const isAnyoneCanPay =
-          input.sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
-        if (isAnyoneCanPay) whitelist.push('addInput');
-        if (!isAnyoneCanPay && action === 'addInput') {
-          throws = true;
-        }
-        const hashType = input.sighashType & 0x1f;
-        switch (hashType) {
-          case transaction_1.Transaction.SIGHASH_ALL:
-            break;
-          case transaction_1.Transaction.SIGHASH_SINGLE:
-          case transaction_1.Transaction.SIGHASH_NONE:
-            whitelist.push('addOutput');
-            whitelist.push('setSequence');
-            break;
-        }
-        if (whitelist.indexOf(action) === -1) {
-          throws = true;
-        }
-      } else {
+    if ((input.partialSig || []).length === 0) return;
+    input.partialSig.forEach(pSig => {
+      const { hashType } = bscript.signature.decode(pSig.signature);
+      const whitelist = [];
+      const isAnyoneCanPay =
+        hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
+      if (isAnyoneCanPay) whitelist.push('addInput');
+      const hashMod = hashType & 0x1f;
+      switch (hashMod) {
+        case transaction_1.Transaction.SIGHASH_ALL:
+          break;
+        case transaction_1.Transaction.SIGHASH_SINGLE:
+        case transaction_1.Transaction.SIGHASH_NONE:
+          whitelist.push('addOutput');
+          whitelist.push('setSequence');
+          break;
+      }
+      if (whitelist.indexOf(action) === -1) {
         throws = true;
       }
-    }
+    });
     if (throws) {
       throw new Error('Can not modify transaction, signatures exist.');
     }
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 122bba8..9e45c83 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1028,32 +1028,26 @@ function checkTxEmpty(tx: Transaction): void {
 function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
   inputs.forEach(input => {
     let throws = false;
-    if ((input.partialSig || []).length > 0) {
-      if (input.sighashType !== undefined) {
-        const whitelist: string[] = [];
-        const isAnyoneCanPay =
-          input.sighashType & Transaction.SIGHASH_ANYONECANPAY;
-        if (isAnyoneCanPay) whitelist.push('addInput');
-        if (!isAnyoneCanPay && action === 'addInput') {
-          throws = true;
-        }
-        const hashType = input.sighashType & 0x1f;
-        switch (hashType) {
-          case Transaction.SIGHASH_ALL:
-            break;
-          case Transaction.SIGHASH_SINGLE:
-          case Transaction.SIGHASH_NONE:
-            whitelist.push('addOutput');
-            whitelist.push('setSequence');
-            break;
-        }
-        if (whitelist.indexOf(action) === -1) {
-          throws = true;
-        }
-      } else {
+    if ((input.partialSig || []).length === 0) return;
+    input.partialSig!.forEach(pSig => {
+      const { hashType } = bscript.signature.decode(pSig.signature);
+      const whitelist: string[] = [];
+      const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY;
+      if (isAnyoneCanPay) whitelist.push('addInput');
+      const hashMod = hashType & 0x1f;
+      switch (hashMod) {
+        case Transaction.SIGHASH_ALL:
+          break;
+        case Transaction.SIGHASH_SINGLE:
+        case Transaction.SIGHASH_NONE:
+          whitelist.push('addOutput');
+          whitelist.push('setSequence');
+          break;
+      }
+      if (whitelist.indexOf(action) === -1) {
         throws = true;
       }
-    }
+    });
     if (throws) {
       throw new Error('Can not modify transaction, signatures exist.');
     }

From 88de1e7b0e140612a733c90cc2303c0e127d39be Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 11:29:20 +0900
Subject: [PATCH 071/111] Refactor: nonWitnessUtxo cache

---
 src/psbt.js    | 44 +++++++++++++++++++++++++-------------------
 ts_src/psbt.ts | 49 ++++++++++++++++++++++++++++++-------------------
 2 files changed, 55 insertions(+), 38 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index c3a47ab..fdb457f 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -252,12 +252,9 @@ class Psbt extends bip174_1.Psbt {
       if (input.witnessUtxo) {
         inputAmount += input.witnessUtxo.value;
       } else if (input.nonWitnessUtxo) {
-        const cache = this.__CACHE;
-        if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
-          addNonWitnessTxCache(this.__CACHE, input, idx);
-        }
+        const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx);
         const vout = this.__TX.ins[idx].index;
-        const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout];
+        const out = nwTx.outs[vout];
         inputAmount += out.value;
       }
     });
@@ -436,13 +433,14 @@ function addNonWitnessTxCache(cache, input, inputIndex) {
   Object.defineProperty(input, 'nonWitnessUtxo', {
     enumerable: true,
     get() {
-      if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) {
-        return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+      const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+      const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex];
+      if (buf !== undefined) {
+        return buf;
       } else {
-        self.__NON_WITNESS_UTXO_BUF_CACHE[
-          selfIndex
-        ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer();
-        return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+        const newBuf = txCache.toBuffer();
+        self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf;
+        return newBuf;
       }
     },
     set(data) {
@@ -597,10 +595,11 @@ function getHashForSig(inputIndex, input, unsignedTx, cache) {
   let hash;
   let script;
   if (input.nonWitnessUtxo) {
-    if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
-      addNonWitnessTxCache(cache, input, inputIndex);
-    }
-    const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+    const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
+      cache,
+      input,
+      inputIndex,
+    );
     const prevoutHash = unsignedTx.ins[inputIndex].hash;
     const utxoHash = nonWitnessUtxoTx.getHash();
     // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
@@ -723,10 +722,11 @@ function getScriptFromInput(inputIndex, input, unsignedTx, cache) {
       res.isP2SH = true;
       res.script = input.redeemScript;
     } else {
-      if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
-        addNonWitnessTxCache(cache, input, inputIndex);
-      }
-      const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+      const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
+        cache,
+        input,
+        inputIndex,
+      );
       const prevoutIndex = unsignedTx.ins[inputIndex].index;
       res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
     }
@@ -841,6 +841,12 @@ function checkInputsForPartialSig(inputs, action) {
     }
   });
 }
+function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
+  if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+    addNonWitnessTxCache(cache, input, inputIndex);
+  }
+  return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+}
 function check32Bit(num) {
   if (
     typeof num !== 'number' ||
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 9e45c83..272ac8e 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -305,12 +305,9 @@ export class Psbt extends PsbtBase {
       if (input.witnessUtxo) {
         inputAmount += input.witnessUtxo.value;
       } else if (input.nonWitnessUtxo) {
-        const cache = this.__CACHE;
-        if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
-          addNonWitnessTxCache(this.__CACHE, input, idx);
-        }
+        const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx);
         const vout = this.__TX.ins[idx].index;
-        const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output;
+        const out = nwTx.outs[vout] as Output;
         inputAmount += out.value;
       }
     });
@@ -548,13 +545,14 @@ function addNonWitnessTxCache(
   Object.defineProperty(input, 'nonWitnessUtxo', {
     enumerable: true,
     get(): Buffer {
-      if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) {
-        return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+      const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+      const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex];
+      if (buf !== undefined) {
+        return buf;
       } else {
-        self.__NON_WITNESS_UTXO_BUF_CACHE[
-          selfIndex
-        ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer();
-        return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+        const newBuf = txCache.toBuffer();
+        self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf;
+        return newBuf;
       }
     },
     set(data: Buffer): void {
@@ -760,10 +758,11 @@ function getHashForSig(
   let script: Buffer;
 
   if (input.nonWitnessUtxo) {
-    if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
-      addNonWitnessTxCache(cache, input, inputIndex);
-    }
-    const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+    const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
+      cache,
+      input,
+      inputIndex,
+    );
 
     const prevoutHash = unsignedTx.ins[inputIndex].hash;
     const utxoHash = nonWitnessUtxoTx.getHash();
@@ -918,10 +917,11 @@ function getScriptFromInput(
       res.isP2SH = true;
       res.script = input.redeemScript;
     } else {
-      if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
-        addNonWitnessTxCache(cache, input, inputIndex);
-      }
-      const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+      const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
+        cache,
+        input,
+        inputIndex,
+      );
       const prevoutIndex = unsignedTx.ins[inputIndex].index;
       res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
     }
@@ -1054,6 +1054,17 @@ function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
   });
 }
 
+function nonWitnessUtxoTxFromCache(
+  cache: PsbtCache,
+  input: PsbtInput,
+  inputIndex: number,
+): Transaction {
+  if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+    addNonWitnessTxCache(cache, input, inputIndex);
+  }
+  return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+}
+
 function check32Bit(num: number): void {
   if (
     typeof num !== 'number' ||

From e4e51113768a48061fd1ae2982f40ee3fffb05ef Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 11:51:28 +0900
Subject: [PATCH 072/111] Refactor: cache

---
 src/psbt.js     | 149 ++++++++++++++++++++++--------------------
 test/psbt.js    |  12 ++--
 ts_src/psbt.ts  | 169 +++++++++++++++++++++++++-----------------------
 types/psbt.d.ts |   4 --
 4 files changed, 171 insertions(+), 163 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index fdb457f..c549d30 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -22,10 +22,13 @@ class Psbt extends bip174_1.Psbt {
       __NON_WITNESS_UTXO_TX_CACHE: [],
       __NON_WITNESS_UTXO_BUF_CACHE: [],
       __TX_IN_CACHE: {},
+      __TX: new transaction_1.Transaction(),
     };
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
-    this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
+    this.__CACHE.__TX = transaction_1.Transaction.fromBuffer(
+      this.globalMap.unsignedTx,
+    );
     this.setVersion(2);
     // set cache
     const self = this;
@@ -33,15 +36,15 @@ class Psbt extends bip174_1.Psbt {
     Object.defineProperty(this.globalMap, 'unsignedTx', {
       enumerable: true,
       get() {
-        if (self.__TX_BUF_CACHE !== undefined) {
-          return self.__TX_BUF_CACHE;
+        if (self.__CACHE.__TX_BUF_CACHE !== undefined) {
+          return self.__CACHE.__TX_BUF_CACHE;
         } else {
-          self.__TX_BUF_CACHE = self.__TX.toBuffer();
-          return self.__TX_BUF_CACHE;
+          self.__CACHE.__TX_BUF_CACHE = self.__CACHE.__TX.toBuffer();
+          return self.__CACHE.__TX_BUF_CACHE;
         }
       },
       set(data) {
-        self.__TX_BUF_CACHE = data;
+        self.__CACHE.__TX_BUF_CACHE = data;
       },
     });
     // Make data hidden when enumerating
@@ -52,8 +55,6 @@ class Psbt extends bip174_1.Psbt {
       });
     dpew(this, '__TX', false, true);
     dpew(this, '__EXTRACTED_TX', false, true);
-    dpew(this, '__FEE_RATE', false, true);
-    dpew(this, '__TX_BUF_CACHE', false, true);
     dpew(this, '__CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
@@ -61,7 +62,7 @@ class Psbt extends bip174_1.Psbt {
     const tx = transaction_1.Transaction.fromBuffer(txBuf);
     checkTxEmpty(tx);
     const psbt = new this();
-    psbt.__TX = tx;
+    psbt.__CACHE.__TX = tx;
     checkTxForDupeIns(tx, psbt.__CACHE);
     let inputCount = tx.ins.length;
     let outputCount = tx.outs.length;
@@ -90,7 +91,7 @@ class Psbt extends bip174_1.Psbt {
       };
     };
     const psbt = super.fromBuffer(buffer, txCountGetter);
-    psbt.__TX = tx;
+    psbt.__CACHE.__TX = tx;
     checkTxForDupeIns(tx, psbt.__CACHE);
     return psbt;
   }
@@ -104,63 +105,39 @@ class Psbt extends bip174_1.Psbt {
   setVersion(version) {
     check32Bit(version);
     checkInputsForPartialSig(this.inputs, 'setVersion');
-    this.__TX.version = version;
-    this.__TX_BUF_CACHE = undefined;
-    this.__EXTRACTED_TX = undefined;
+    const c = this.__CACHE;
+    c.__TX.version = version;
+    c.__TX_BUF_CACHE = undefined;
+    c.__EXTRACTED_TX = undefined;
     return this;
   }
   setLocktime(locktime) {
     check32Bit(locktime);
     checkInputsForPartialSig(this.inputs, 'setLocktime');
-    this.__TX.locktime = locktime;
-    this.__TX_BUF_CACHE = undefined;
-    this.__EXTRACTED_TX = undefined;
+    const c = this.__CACHE;
+    c.__TX.locktime = locktime;
+    c.__TX_BUF_CACHE = undefined;
+    c.__EXTRACTED_TX = undefined;
     return this;
   }
   setSequence(inputIndex, sequence) {
     check32Bit(sequence);
     checkInputsForPartialSig(this.inputs, 'setSequence');
-    if (this.__TX.ins.length <= inputIndex) {
+    const c = this.__CACHE;
+    if (c.__TX.ins.length <= inputIndex) {
       throw new Error('Input index too high');
     }
-    this.__TX.ins[inputIndex].sequence = sequence;
-    this.__TX_BUF_CACHE = undefined;
-    this.__EXTRACTED_TX = undefined;
+    c.__TX.ins[inputIndex].sequence = sequence;
+    c.__TX_BUF_CACHE = undefined;
+    c.__EXTRACTED_TX = undefined;
     return this;
   }
   addInput(inputData) {
     checkInputsForPartialSig(this.inputs, 'addInput');
-    const self = this;
-    const inputAdder = (_inputData, txBuf) => {
-      if (
-        !txBuf ||
-        _inputData.hash === undefined ||
-        _inputData.index === undefined ||
-        (!Buffer.isBuffer(_inputData.hash) &&
-          typeof _inputData.hash !== 'string') ||
-        typeof _inputData.index !== 'number'
-      ) {
-        throw new Error('Error adding input.');
-      }
-      const prevHash = Buffer.isBuffer(_inputData.hash)
-        ? _inputData.hash
-        : bufferutils_1.reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
-      // Check if input already exists in cache.
-      const input = { hash: prevHash, index: _inputData.index };
-      checkTxInputCache(self.__CACHE, input);
-      self.__TX.ins.push(
-        Object.assign({}, input, {
-          script: Buffer.alloc(0),
-          sequence:
-            _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE,
-          witness: [],
-        }),
-      );
-      return self.__TX.toBuffer();
-    };
+    const inputAdder = getInputAdder(this.__CACHE);
     super.addInput(inputData, inputAdder);
-    this.__FEE_RATE = undefined;
-    this.__EXTRACTED_TX = undefined;
+    this.__CACHE.__FEE_RATE = undefined;
+    this.__CACHE.__EXTRACTED_TX = undefined;
     return this;
   }
   addOutput(outputData) {
@@ -182,15 +159,15 @@ class Psbt extends bip174_1.Psbt {
       ) {
         throw new Error('Error adding output.');
       }
-      self.__TX.outs.push({
+      self.__CACHE.__TX.outs.push({
         script: _outputData.script,
         value: _outputData.value,
       });
-      return self.__TX.toBuffer();
+      return self.__CACHE.__TX.toBuffer();
     };
     super.addOutput(outputData, true, outputAdder);
-    this.__FEE_RATE = undefined;
-    this.__EXTRACTED_TX = undefined;
+    this.__CACHE.__FEE_RATE = undefined;
+    this.__CACHE.__EXTRACTED_TX = undefined;
     return this;
   }
   addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
@@ -202,8 +179,8 @@ class Psbt extends bip174_1.Psbt {
   extractTransaction(disableFeeCheck) {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
     if (!disableFeeCheck) {
-      const feeRate = this.__FEE_RATE || this.getFeeRate();
-      const vsize = this.__EXTRACTED_TX.virtualSize();
+      const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate();
+      const vsize = this.__CACHE.__EXTRACTED_TX.virtualSize();
       const satoshis = feeRate * vsize;
       if (feeRate >= this.opts.maximumFeeRate) {
         throw new Error(
@@ -215,8 +192,8 @@ class Psbt extends bip174_1.Psbt {
         );
       }
     }
-    if (this.__EXTRACTED_TX) return this.__EXTRACTED_TX;
-    const tx = this.__TX.clone();
+    if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX;
+    const tx = this.__CACHE.__TX.clone();
     this.inputs.forEach((input, idx) => {
       if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
       if (input.finalScriptWitness) {
@@ -225,21 +202,21 @@ class Psbt extends bip174_1.Psbt {
         );
       }
     });
-    this.__EXTRACTED_TX = tx;
+    this.__CACHE.__EXTRACTED_TX = tx;
     return tx;
   }
   getFeeRate() {
     if (!this.inputs.every(isFinalized))
       throw new Error('PSBT must be finalized to calculate fee rate');
-    if (this.__FEE_RATE) return this.__FEE_RATE;
+    if (this.__CACHE.__FEE_RATE) return this.__CACHE.__FEE_RATE;
     let tx;
     let inputAmount = 0;
     let mustFinalize = true;
-    if (this.__EXTRACTED_TX) {
-      tx = this.__EXTRACTED_TX;
+    if (this.__CACHE.__EXTRACTED_TX) {
+      tx = this.__CACHE.__EXTRACTED_TX;
       mustFinalize = false;
     } else {
-      tx = this.__TX.clone();
+      tx = this.__CACHE.__TX.clone();
     }
     this.inputs.forEach((input, idx) => {
       if (mustFinalize && input.finalScriptSig)
@@ -253,17 +230,17 @@ class Psbt extends bip174_1.Psbt {
         inputAmount += input.witnessUtxo.value;
       } else if (input.nonWitnessUtxo) {
         const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx);
-        const vout = this.__TX.ins[idx].index;
+        const vout = this.__CACHE.__TX.ins[idx].index;
         const out = nwTx.outs[vout];
         inputAmount += out.value;
       }
     });
-    this.__EXTRACTED_TX = tx;
+    this.__CACHE.__EXTRACTED_TX = tx;
     const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0);
     const fee = inputAmount - outputAmount;
     const bytes = tx.virtualSize();
-    this.__FEE_RATE = Math.floor(fee / bytes);
-    return this.__FEE_RATE;
+    this.__CACHE.__FEE_RATE = Math.floor(fee / bytes);
+    return this.__CACHE.__FEE_RATE;
   }
   finalizeAllInputs() {
     const inputResults = range(this.inputs.length).map(idx =>
@@ -280,7 +257,7 @@ class Psbt extends bip174_1.Psbt {
     const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
-      this.__TX,
+      this.__CACHE.__TX,
       this.__CACHE,
     );
     if (!script) return false;
@@ -322,7 +299,7 @@ class Psbt extends bip174_1.Psbt {
           ? getHashForSig(
               inputIndex,
               Object.assign({}, input, { sighashType: sig.hashType }),
-              this.__TX,
+              this.__CACHE.__TX,
               this.__CACHE,
             )
           : { hash: hashCache, script: scriptCache };
@@ -391,7 +368,7 @@ class Psbt extends bip174_1.Psbt {
       this.inputs,
       inputIndex,
       keyPair.publicKey,
-      this.__TX,
+      this.__CACHE.__TX,
       this.__CACHE,
     );
     const partialSig = {
@@ -408,7 +385,7 @@ class Psbt extends bip174_1.Psbt {
         this.inputs,
         inputIndex,
         keyPair.publicKey,
-        this.__TX,
+        this.__CACHE.__TX,
         this.__CACHE,
       );
       Promise.resolve(keyPair.sign(hash)).then(signature => {
@@ -847,6 +824,36 @@ function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
   }
   return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
 }
+function getInputAdder(cache) {
+  const selfCache = cache;
+  return (_inputData, txBuf) => {
+    if (
+      !txBuf ||
+      _inputData.hash === undefined ||
+      _inputData.index === undefined ||
+      (!Buffer.isBuffer(_inputData.hash) &&
+        typeof _inputData.hash !== 'string') ||
+      typeof _inputData.index !== 'number'
+    ) {
+      throw new Error('Error adding input.');
+    }
+    const prevHash = Buffer.isBuffer(_inputData.hash)
+      ? _inputData.hash
+      : bufferutils_1.reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
+    // Check if input already exists in cache.
+    const input = { hash: prevHash, index: _inputData.index };
+    checkTxInputCache(selfCache, input);
+    selfCache.__TX.ins.push(
+      Object.assign({}, input, {
+        script: Buffer.alloc(0),
+        sequence:
+          _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE,
+        witness: [],
+      }),
+    );
+    return selfCache.__TX.toBuffer();
+  };
+}
 function check32Bit(num) {
   if (
     typeof num !== 'number' ||
diff --git a/test/psbt.js b/test/psbt.js
index cd45c1e..657a67a 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -397,9 +397,9 @@ describe(`Psbt`, () => {
       });
 
       assert.strictEqual(psbt.inputCount, 1)
-      assert.strictEqual(psbt.__TX.ins[0].sequence, 0xffffffff)
+      assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0xffffffff)
       psbt.setSequence(0, 0)
-      assert.strictEqual(psbt.__TX.ins[0].sequence, 0)
+      assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0)
     })
 
     it('throws if input index is too high', () => {
@@ -467,24 +467,24 @@ describe(`Psbt`, () => {
     it('fromTransaction returns Psbt type (not base class)', () => {
       const psbt = Psbt.fromTransaction(Buffer.from([2,0,0,0,0,0,0,0,0,0]));
       assert.strictEqual(psbt instanceof Psbt, true);
-      assert.ok(psbt.__TX);
+      assert.ok(psbt.__CACHE.__TX);
     })
     it('fromBuffer returns Psbt type (not base class)', () => {
       const psbt = Psbt.fromBuffer(Buffer.from(
         '70736274ff01000a01000000000000000000000000', 'hex' //cHNidP8BAAoBAAAAAAAAAAAAAAAA
       ));
       assert.strictEqual(psbt instanceof Psbt, true);
-      assert.ok(psbt.__TX);
+      assert.ok(psbt.__CACHE.__TX);
     })
     it('fromBase64 returns Psbt type (not base class)', () => {
       const psbt = Psbt.fromBase64('cHNidP8BAAoBAAAAAAAAAAAAAAAA');
       assert.strictEqual(psbt instanceof Psbt, true);
-      assert.ok(psbt.__TX);
+      assert.ok(psbt.__CACHE.__TX);
     })
     it('fromHex returns Psbt type (not base class)', () => {
       const psbt = Psbt.fromHex('70736274ff01000a01000000000000000000000000');
       assert.strictEqual(psbt instanceof Psbt, true);
-      assert.ok(psbt.__TX);
+      assert.ok(psbt.__CACHE.__TX);
     })
   })
 })
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 272ac8e..0174b3b 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -34,7 +34,7 @@ export class Psbt extends PsbtBase {
     const tx = Transaction.fromBuffer(txBuf);
     checkTxEmpty(tx);
     const psbt = new this() as Psbt;
-    psbt.__TX = tx;
+    psbt.__CACHE.__TX = tx;
     checkTxForDupeIns(tx, psbt.__CACHE);
     let inputCount = tx.ins.length;
     let outputCount = tx.outs.length;
@@ -71,25 +71,22 @@ export class Psbt extends PsbtBase {
       };
     };
     const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt;
-    psbt.__TX = tx!;
+    psbt.__CACHE.__TX = tx!;
     checkTxForDupeIns(tx!, psbt.__CACHE);
     return psbt as InstanceType<T>;
   }
-  private __CACHE = {
-    __NON_WITNESS_UTXO_TX_CACHE: [] as Transaction[],
-    __NON_WITNESS_UTXO_BUF_CACHE: [] as Buffer[],
-    __TX_IN_CACHE: {} as { [index: string]: number },
+  private __CACHE: PsbtCache = {
+    __NON_WITNESS_UTXO_TX_CACHE: [],
+    __NON_WITNESS_UTXO_BUF_CACHE: [],
+    __TX_IN_CACHE: {},
+    __TX: new Transaction(),
   };
-  private __TX: Transaction;
-  private __TX_BUF_CACHE?: Buffer;
-  private __FEE_RATE?: number;
-  private __EXTRACTED_TX?: Transaction;
   private opts: PsbtOpts;
   constructor(opts: PsbtOptsOptional = {}) {
     super();
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
-    this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
+    this.__CACHE.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
     this.setVersion(2);
 
     // set cache
@@ -98,15 +95,15 @@ export class Psbt extends PsbtBase {
     Object.defineProperty(this.globalMap, 'unsignedTx', {
       enumerable: true,
       get(): Buffer {
-        if (self.__TX_BUF_CACHE !== undefined) {
-          return self.__TX_BUF_CACHE;
+        if (self.__CACHE.__TX_BUF_CACHE !== undefined) {
+          return self.__CACHE.__TX_BUF_CACHE;
         } else {
-          self.__TX_BUF_CACHE = self.__TX.toBuffer();
-          return self.__TX_BUF_CACHE;
+          self.__CACHE.__TX_BUF_CACHE = self.__CACHE.__TX.toBuffer();
+          return self.__CACHE.__TX_BUF_CACHE;
         }
       },
       set(data: Buffer): void {
-        self.__TX_BUF_CACHE = data;
+        self.__CACHE.__TX_BUF_CACHE = data;
       },
     });
 
@@ -123,8 +120,6 @@ export class Psbt extends PsbtBase {
       });
     dpew(this, '__TX', false, true);
     dpew(this, '__EXTRACTED_TX', false, true);
-    dpew(this, '__FEE_RATE', false, true);
-    dpew(this, '__TX_BUF_CACHE', false, true);
     dpew(this, '__CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
@@ -141,69 +136,42 @@ export class Psbt extends PsbtBase {
   setVersion(version: number): this {
     check32Bit(version);
     checkInputsForPartialSig(this.inputs, 'setVersion');
-    this.__TX.version = version;
-    this.__TX_BUF_CACHE = undefined;
-    this.__EXTRACTED_TX = undefined;
+    const c = this.__CACHE;
+    c.__TX.version = version;
+    c.__TX_BUF_CACHE = undefined;
+    c.__EXTRACTED_TX = undefined;
     return this;
   }
 
   setLocktime(locktime: number): this {
     check32Bit(locktime);
     checkInputsForPartialSig(this.inputs, 'setLocktime');
-    this.__TX.locktime = locktime;
-    this.__TX_BUF_CACHE = undefined;
-    this.__EXTRACTED_TX = undefined;
+    const c = this.__CACHE;
+    c.__TX.locktime = locktime;
+    c.__TX_BUF_CACHE = undefined;
+    c.__EXTRACTED_TX = undefined;
     return this;
   }
 
   setSequence(inputIndex: number, sequence: number): this {
     check32Bit(sequence);
     checkInputsForPartialSig(this.inputs, 'setSequence');
-    if (this.__TX.ins.length <= inputIndex) {
+    const c = this.__CACHE;
+    if (c.__TX.ins.length <= inputIndex) {
       throw new Error('Input index too high');
     }
-    this.__TX.ins[inputIndex].sequence = sequence;
-    this.__TX_BUF_CACHE = undefined;
-    this.__EXTRACTED_TX = undefined;
+    c.__TX.ins[inputIndex].sequence = sequence;
+    c.__TX_BUF_CACHE = undefined;
+    c.__EXTRACTED_TX = undefined;
     return this;
   }
 
   addInput(inputData: TransactionInput): this {
     checkInputsForPartialSig(this.inputs, 'addInput');
-    const self = this;
-    const inputAdder = (
-      _inputData: TransactionInput,
-      txBuf: Buffer,
-    ): Buffer => {
-      if (
-        !txBuf ||
-        (_inputData as any).hash === undefined ||
-        (_inputData as any).index === undefined ||
-        (!Buffer.isBuffer((_inputData as any).hash) &&
-          typeof (_inputData as any).hash !== 'string') ||
-        typeof (_inputData as any).index !== 'number'
-      ) {
-        throw new Error('Error adding input.');
-      }
-      const prevHash = Buffer.isBuffer(_inputData.hash)
-        ? _inputData.hash
-        : reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
-
-      // Check if input already exists in cache.
-      const input = { hash: prevHash, index: _inputData.index };
-      checkTxInputCache(self.__CACHE, input);
-
-      self.__TX.ins.push({
-        ...input,
-        script: Buffer.alloc(0),
-        sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE,
-        witness: [],
-      });
-      return self.__TX.toBuffer();
-    };
+    const inputAdder = getInputAdder(this.__CACHE);
     super.addInput(inputData, inputAdder);
-    this.__FEE_RATE = undefined;
-    this.__EXTRACTED_TX = undefined;
+    this.__CACHE.__FEE_RATE = undefined;
+    this.__CACHE.__EXTRACTED_TX = undefined;
     return this;
   }
 
@@ -229,15 +197,15 @@ export class Psbt extends PsbtBase {
       ) {
         throw new Error('Error adding output.');
       }
-      self.__TX.outs.push({
+      self.__CACHE.__TX.outs.push({
         script: (_outputData as any).script!,
         value: _outputData.value,
       });
-      return self.__TX.toBuffer();
+      return self.__CACHE.__TX.toBuffer();
     };
     super.addOutput(outputData, true, outputAdder);
-    this.__FEE_RATE = undefined;
-    this.__EXTRACTED_TX = undefined;
+    this.__CACHE.__FEE_RATE = undefined;
+    this.__CACHE.__EXTRACTED_TX = undefined;
     return this;
   }
 
@@ -254,8 +222,8 @@ export class Psbt extends PsbtBase {
   extractTransaction(disableFeeCheck?: boolean): Transaction {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
     if (!disableFeeCheck) {
-      const feeRate = this.__FEE_RATE || this.getFeeRate();
-      const vsize = this.__EXTRACTED_TX!.virtualSize();
+      const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate();
+      const vsize = this.__CACHE.__EXTRACTED_TX!.virtualSize();
       const satoshis = feeRate * vsize;
       if (feeRate >= this.opts.maximumFeeRate) {
         throw new Error(
@@ -267,8 +235,8 @@ export class Psbt extends PsbtBase {
         );
       }
     }
-    if (this.__EXTRACTED_TX) return this.__EXTRACTED_TX;
-    const tx = this.__TX.clone();
+    if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX;
+    const tx = this.__CACHE.__TX.clone();
     this.inputs.forEach((input, idx) => {
       if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
       if (input.finalScriptWitness) {
@@ -277,22 +245,22 @@ export class Psbt extends PsbtBase {
         );
       }
     });
-    this.__EXTRACTED_TX = tx;
+    this.__CACHE.__EXTRACTED_TX = tx;
     return tx;
   }
 
   getFeeRate(): number {
     if (!this.inputs.every(isFinalized))
       throw new Error('PSBT must be finalized to calculate fee rate');
-    if (this.__FEE_RATE) return this.__FEE_RATE;
+    if (this.__CACHE.__FEE_RATE) return this.__CACHE.__FEE_RATE;
     let tx: Transaction;
     let inputAmount = 0;
     let mustFinalize = true;
-    if (this.__EXTRACTED_TX) {
-      tx = this.__EXTRACTED_TX;
+    if (this.__CACHE.__EXTRACTED_TX) {
+      tx = this.__CACHE.__EXTRACTED_TX;
       mustFinalize = false;
     } else {
-      tx = this.__TX.clone();
+      tx = this.__CACHE.__TX.clone();
     }
     this.inputs.forEach((input, idx) => {
       if (mustFinalize && input.finalScriptSig)
@@ -306,20 +274,20 @@ export class Psbt extends PsbtBase {
         inputAmount += input.witnessUtxo.value;
       } else if (input.nonWitnessUtxo) {
         const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx);
-        const vout = this.__TX.ins[idx].index;
+        const vout = this.__CACHE.__TX.ins[idx].index;
         const out = nwTx.outs[vout] as Output;
         inputAmount += out.value;
       }
     });
-    this.__EXTRACTED_TX = tx;
+    this.__CACHE.__EXTRACTED_TX = tx;
     const outputAmount = (tx.outs as Output[]).reduce(
       (total, o) => total + o.value,
       0,
     );
     const fee = inputAmount - outputAmount;
     const bytes = tx.virtualSize();
-    this.__FEE_RATE = Math.floor(fee / bytes);
-    return this.__FEE_RATE;
+    this.__CACHE.__FEE_RATE = Math.floor(fee / bytes);
+    return this.__CACHE.__FEE_RATE;
   }
 
   finalizeAllInputs(): {
@@ -341,7 +309,7 @@ export class Psbt extends PsbtBase {
     const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
-      this.__TX,
+      this.__CACHE.__TX,
       this.__CACHE,
     );
     if (!script) return false;
@@ -388,7 +356,7 @@ export class Psbt extends PsbtBase {
           ? getHashForSig(
               inputIndex,
               Object.assign({}, input, { sighashType: sig.hashType }),
-              this.__TX,
+              this.__CACHE.__TX,
               this.__CACHE,
             )
           : { hash: hashCache!, script: scriptCache! };
@@ -464,7 +432,7 @@ export class Psbt extends PsbtBase {
       this.inputs,
       inputIndex,
       keyPair.publicKey,
-      this.__TX,
+      this.__CACHE.__TX,
       this.__CACHE,
     );
 
@@ -485,7 +453,7 @@ export class Psbt extends PsbtBase {
           this.inputs,
           inputIndex,
           keyPair.publicKey,
-          this.__TX,
+          this.__CACHE.__TX,
           this.__CACHE,
         );
 
@@ -517,6 +485,10 @@ interface PsbtCache {
   __NON_WITNESS_UTXO_TX_CACHE: Transaction[];
   __NON_WITNESS_UTXO_BUF_CACHE: Buffer[];
   __TX_IN_CACHE: { [index: string]: number };
+  __TX: Transaction;
+  __TX_BUF_CACHE?: Buffer;
+  __FEE_RATE?: number;
+  __EXTRACTED_TX?: Transaction;
 }
 
 interface PsbtOptsOptional {
@@ -1065,6 +1037,39 @@ function nonWitnessUtxoTxFromCache(
   return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
 }
 
+function getInputAdder(
+  cache: PsbtCache,
+): (_inputData: TransactionInput, txBuf: Buffer) => Buffer {
+  const selfCache = cache;
+  return (_inputData: TransactionInput, txBuf: Buffer): Buffer => {
+    if (
+      !txBuf ||
+      (_inputData as any).hash === undefined ||
+      (_inputData as any).index === undefined ||
+      (!Buffer.isBuffer((_inputData as any).hash) &&
+        typeof (_inputData as any).hash !== 'string') ||
+      typeof (_inputData as any).index !== 'number'
+    ) {
+      throw new Error('Error adding input.');
+    }
+    const prevHash = Buffer.isBuffer(_inputData.hash)
+      ? _inputData.hash
+      : reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
+
+    // Check if input already exists in cache.
+    const input = { hash: prevHash, index: _inputData.index };
+    checkTxInputCache(selfCache, input);
+
+    selfCache.__TX.ins.push({
+      ...input,
+      script: Buffer.alloc(0),
+      sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE,
+      witness: [],
+    });
+    return selfCache.__TX.toBuffer();
+  };
+}
+
 function check32Bit(num: number): void {
   if (
     typeof num !== 'number' ||
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index b2893b8..775f3b0 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -8,10 +8,6 @@ export declare class Psbt extends PsbtBase {
     static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>;
     static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>;
     private __CACHE;
-    private __TX;
-    private __TX_BUF_CACHE?;
-    private __FEE_RATE?;
-    private __EXTRACTED_TX?;
     private opts;
     constructor(opts?: PsbtOptsOptional);
     readonly inputCount: number;

From 497d048ebf3b269ee3782423da10478fe822f194 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 11:57:50 +0900
Subject: [PATCH 073/111] Refactor: externalize outputAdder

---
 src/psbt.js    | 49 ++++++++++++++++++++++++--------------------
 ts_src/psbt.ts | 55 +++++++++++++++++++++++++++-----------------------
 2 files changed, 57 insertions(+), 47 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index c549d30..eff98ae 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -134,10 +134,11 @@ class Psbt extends bip174_1.Psbt {
   }
   addInput(inputData) {
     checkInputsForPartialSig(this.inputs, 'addInput');
-    const inputAdder = getInputAdder(this.__CACHE);
+    const c = this.__CACHE;
+    const inputAdder = getInputAdder(c);
     super.addInput(inputData, inputAdder);
-    this.__CACHE.__FEE_RATE = undefined;
-    this.__CACHE.__EXTRACTED_TX = undefined;
+    c.__FEE_RATE = undefined;
+    c.__EXTRACTED_TX = undefined;
     return this;
   }
   addOutput(outputData) {
@@ -148,26 +149,11 @@ class Psbt extends bip174_1.Psbt {
       const script = address_1.toOutputScript(address, network);
       outputData = Object.assign(outputData, { script });
     }
-    const self = this;
-    const outputAdder = (_outputData, txBuf) => {
-      if (
-        !txBuf ||
-        _outputData.script === undefined ||
-        _outputData.value === undefined ||
-        !Buffer.isBuffer(_outputData.script) ||
-        typeof _outputData.value !== 'number'
-      ) {
-        throw new Error('Error adding output.');
-      }
-      self.__CACHE.__TX.outs.push({
-        script: _outputData.script,
-        value: _outputData.value,
-      });
-      return self.__CACHE.__TX.toBuffer();
-    };
+    const c = this.__CACHE;
+    const outputAdder = getOutputAdder(c);
     super.addOutput(outputData, true, outputAdder);
-    this.__CACHE.__FEE_RATE = undefined;
-    this.__CACHE.__EXTRACTED_TX = undefined;
+    c.__FEE_RATE = undefined;
+    c.__EXTRACTED_TX = undefined;
     return this;
   }
   addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
@@ -854,6 +840,25 @@ function getInputAdder(cache) {
     return selfCache.__TX.toBuffer();
   };
 }
+function getOutputAdder(cache) {
+  const selfCache = cache;
+  return (_outputData, txBuf) => {
+    if (
+      !txBuf ||
+      _outputData.script === undefined ||
+      _outputData.value === undefined ||
+      !Buffer.isBuffer(_outputData.script) ||
+      typeof _outputData.value !== 'number'
+    ) {
+      throw new Error('Error adding output.');
+    }
+    selfCache.__TX.outs.push({
+      script: _outputData.script,
+      value: _outputData.value,
+    });
+    return selfCache.__TX.toBuffer();
+  };
+}
 function check32Bit(num) {
   if (
     typeof num !== 'number' ||
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 0174b3b..370b481 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -168,10 +168,11 @@ export class Psbt extends PsbtBase {
 
   addInput(inputData: TransactionInput): this {
     checkInputsForPartialSig(this.inputs, 'addInput');
-    const inputAdder = getInputAdder(this.__CACHE);
+    const c = this.__CACHE;
+    const inputAdder = getInputAdder(c);
     super.addInput(inputData, inputAdder);
-    this.__CACHE.__FEE_RATE = undefined;
-    this.__CACHE.__EXTRACTED_TX = undefined;
+    c.__FEE_RATE = undefined;
+    c.__EXTRACTED_TX = undefined;
     return this;
   }
 
@@ -183,29 +184,11 @@ export class Psbt extends PsbtBase {
       const script = toOutputScript(address, network);
       outputData = Object.assign(outputData, { script });
     }
-    const self = this;
-    const outputAdder = (
-      _outputData: TransactionOutput,
-      txBuf: Buffer,
-    ): Buffer => {
-      if (
-        !txBuf ||
-        (_outputData as any).script === undefined ||
-        (_outputData as any).value === undefined ||
-        !Buffer.isBuffer((_outputData as any).script) ||
-        typeof (_outputData as any).value !== 'number'
-      ) {
-        throw new Error('Error adding output.');
-      }
-      self.__CACHE.__TX.outs.push({
-        script: (_outputData as any).script!,
-        value: _outputData.value,
-      });
-      return self.__CACHE.__TX.toBuffer();
-    };
+    const c = this.__CACHE;
+    const outputAdder = getOutputAdder(c);
     super.addOutput(outputData, true, outputAdder);
-    this.__CACHE.__FEE_RATE = undefined;
-    this.__CACHE.__EXTRACTED_TX = undefined;
+    c.__FEE_RATE = undefined;
+    c.__EXTRACTED_TX = undefined;
     return this;
   }
 
@@ -1070,6 +1053,28 @@ function getInputAdder(
   };
 }
 
+function getOutputAdder(
+  cache: PsbtCache,
+): (_outputData: TransactionOutput, txBuf: Buffer) => Buffer {
+  const selfCache = cache;
+  return (_outputData: TransactionOutput, txBuf: Buffer): Buffer => {
+    if (
+      !txBuf ||
+      (_outputData as any).script === undefined ||
+      (_outputData as any).value === undefined ||
+      !Buffer.isBuffer((_outputData as any).script) ||
+      typeof (_outputData as any).value !== 'number'
+    ) {
+      throw new Error('Error adding output.');
+    }
+    selfCache.__TX.outs.push({
+      script: (_outputData as any).script!,
+      value: _outputData.value,
+    });
+    return selfCache.__TX.toBuffer();
+  };
+}
+
 function check32Bit(num: number): void {
   if (
     typeof num !== 'number' ||

From 9749a216b8cc68fff6e00771cf93af23ce47c52a Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 12:15:20 +0900
Subject: [PATCH 074/111] Refactor: input finalize and get fee shared logic

---
 src/psbt.js    | 104 ++++++++++++++++++++++++---------------------
 ts_src/psbt.ts | 112 ++++++++++++++++++++++++++++---------------------
 2 files changed, 120 insertions(+), 96 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index eff98ae..c2f9bd3 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -164,69 +164,42 @@ class Psbt extends bip174_1.Psbt {
   }
   extractTransaction(disableFeeCheck) {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
+    const c = this.__CACHE;
     if (!disableFeeCheck) {
-      const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate();
-      const vsize = this.__CACHE.__EXTRACTED_TX.virtualSize();
-      const satoshis = feeRate * vsize;
-      if (feeRate >= this.opts.maximumFeeRate) {
-        throw new Error(
-          `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
-            `fees, which is ${feeRate} satoshi per byte for a transaction ` +
-            `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
-            `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
-            `pass true to the first arg of extractTransaction.`,
-        );
-      }
+      checkFees(this, c, this.opts);
     }
-    if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX;
-    const tx = this.__CACHE.__TX.clone();
-    this.inputs.forEach((input, idx) => {
-      if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
-      if (input.finalScriptWitness) {
-        tx.ins[idx].witness = scriptWitnessToWitnessStack(
-          input.finalScriptWitness,
-        );
-      }
-    });
-    this.__CACHE.__EXTRACTED_TX = tx;
+    if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
+    const tx = c.__TX.clone();
+    inputFinalizeGetAmts(this.inputs, tx, c, true, false);
+    c.__EXTRACTED_TX = tx;
     return tx;
   }
   getFeeRate() {
     if (!this.inputs.every(isFinalized))
       throw new Error('PSBT must be finalized to calculate fee rate');
-    if (this.__CACHE.__FEE_RATE) return this.__CACHE.__FEE_RATE;
+    const c = this.__CACHE;
+    if (c.__FEE_RATE) return c.__FEE_RATE;
     let tx;
-    let inputAmount = 0;
     let mustFinalize = true;
-    if (this.__CACHE.__EXTRACTED_TX) {
-      tx = this.__CACHE.__EXTRACTED_TX;
+    if (c.__EXTRACTED_TX) {
+      tx = c.__EXTRACTED_TX;
       mustFinalize = false;
     } else {
-      tx = this.__CACHE.__TX.clone();
+      tx = c.__TX.clone();
     }
-    this.inputs.forEach((input, idx) => {
-      if (mustFinalize && input.finalScriptSig)
-        tx.ins[idx].script = input.finalScriptSig;
-      if (mustFinalize && input.finalScriptWitness) {
-        tx.ins[idx].witness = scriptWitnessToWitnessStack(
-          input.finalScriptWitness,
-        );
-      }
-      if (input.witnessUtxo) {
-        inputAmount += input.witnessUtxo.value;
-      } else if (input.nonWitnessUtxo) {
-        const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx);
-        const vout = this.__CACHE.__TX.ins[idx].index;
-        const out = nwTx.outs[vout];
-        inputAmount += out.value;
-      }
-    });
-    this.__CACHE.__EXTRACTED_TX = tx;
+    const inputAmount = inputFinalizeGetAmts(
+      this.inputs,
+      tx,
+      c,
+      mustFinalize,
+      true,
+    );
+    c.__EXTRACTED_TX = tx;
     const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0);
     const fee = inputAmount - outputAmount;
     const bytes = tx.virtualSize();
-    this.__CACHE.__FEE_RATE = Math.floor(fee / bytes);
-    return this.__CACHE.__FEE_RATE;
+    c.__FEE_RATE = Math.floor(fee / bytes);
+    return c.__FEE_RATE;
   }
   finalizeAllInputs() {
     const inputResults = range(this.inputs.length).map(idx =>
@@ -859,6 +832,41 @@ function getOutputAdder(cache) {
     return selfCache.__TX.toBuffer();
   };
 }
+function checkFees(psbt, cache, opts) {
+  const feeRate = cache.__FEE_RATE || psbt.getFeeRate();
+  const vsize = cache.__EXTRACTED_TX.virtualSize();
+  const satoshis = feeRate * vsize;
+  if (feeRate >= opts.maximumFeeRate) {
+    throw new Error(
+      `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
+        `fees, which is ${feeRate} satoshi per byte for a transaction ` +
+        `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
+        `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
+        `pass true to the first arg of extractTransaction.`,
+    );
+  }
+}
+function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) {
+  let inputAmount = 0;
+  inputs.forEach((input, idx) => {
+    if (mustFinalize && input.finalScriptSig)
+      tx.ins[idx].script = input.finalScriptSig;
+    if (mustFinalize && input.finalScriptWitness) {
+      tx.ins[idx].witness = scriptWitnessToWitnessStack(
+        input.finalScriptWitness,
+      );
+    }
+    if (getAmounts && input.witnessUtxo) {
+      inputAmount += input.witnessUtxo.value;
+    } else if (getAmounts && input.nonWitnessUtxo) {
+      const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx);
+      const vout = tx.ins[idx].index;
+      const out = nwTx.outs[vout];
+      inputAmount += out.value;
+    }
+  });
+  return inputAmount;
+}
 function check32Bit(num) {
   if (
     typeof num !== 'number' ||
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 370b481..0771436 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -204,73 +204,46 @@ export class Psbt extends PsbtBase {
 
   extractTransaction(disableFeeCheck?: boolean): Transaction {
     if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
+    const c = this.__CACHE;
     if (!disableFeeCheck) {
-      const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate();
-      const vsize = this.__CACHE.__EXTRACTED_TX!.virtualSize();
-      const satoshis = feeRate * vsize;
-      if (feeRate >= this.opts.maximumFeeRate) {
-        throw new Error(
-          `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
-            `fees, which is ${feeRate} satoshi per byte for a transaction ` +
-            `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
-            `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
-            `pass true to the first arg of extractTransaction.`,
-        );
-      }
+      checkFees(this, c, this.opts);
     }
-    if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX;
-    const tx = this.__CACHE.__TX.clone();
-    this.inputs.forEach((input, idx) => {
-      if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
-      if (input.finalScriptWitness) {
-        tx.ins[idx].witness = scriptWitnessToWitnessStack(
-          input.finalScriptWitness,
-        );
-      }
-    });
-    this.__CACHE.__EXTRACTED_TX = tx;
+    if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
+    const tx = c.__TX.clone();
+    inputFinalizeGetAmts(this.inputs, tx, c, true, false);
+    c.__EXTRACTED_TX = tx;
     return tx;
   }
 
   getFeeRate(): number {
     if (!this.inputs.every(isFinalized))
       throw new Error('PSBT must be finalized to calculate fee rate');
-    if (this.__CACHE.__FEE_RATE) return this.__CACHE.__FEE_RATE;
+    const c = this.__CACHE;
+    if (c.__FEE_RATE) return c.__FEE_RATE;
     let tx: Transaction;
-    let inputAmount = 0;
     let mustFinalize = true;
-    if (this.__CACHE.__EXTRACTED_TX) {
-      tx = this.__CACHE.__EXTRACTED_TX;
+    if (c.__EXTRACTED_TX) {
+      tx = c.__EXTRACTED_TX;
       mustFinalize = false;
     } else {
-      tx = this.__CACHE.__TX.clone();
+      tx = c.__TX.clone();
     }
-    this.inputs.forEach((input, idx) => {
-      if (mustFinalize && input.finalScriptSig)
-        tx.ins[idx].script = input.finalScriptSig;
-      if (mustFinalize && input.finalScriptWitness) {
-        tx.ins[idx].witness = scriptWitnessToWitnessStack(
-          input.finalScriptWitness,
-        );
-      }
-      if (input.witnessUtxo) {
-        inputAmount += input.witnessUtxo.value;
-      } else if (input.nonWitnessUtxo) {
-        const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx);
-        const vout = this.__CACHE.__TX.ins[idx].index;
-        const out = nwTx.outs[vout] as Output;
-        inputAmount += out.value;
-      }
-    });
-    this.__CACHE.__EXTRACTED_TX = tx;
+    const inputAmount = inputFinalizeGetAmts(
+      this.inputs,
+      tx,
+      c,
+      mustFinalize,
+      true,
+    );
+    c.__EXTRACTED_TX = tx;
     const outputAmount = (tx.outs as Output[]).reduce(
       (total, o) => total + o.value,
       0,
     );
     const fee = inputAmount - outputAmount;
     const bytes = tx.virtualSize();
-    this.__CACHE.__FEE_RATE = Math.floor(fee / bytes);
-    return this.__CACHE.__FEE_RATE;
+    c.__FEE_RATE = Math.floor(fee / bytes);
+    return c.__FEE_RATE;
   }
 
   finalizeAllInputs(): {
@@ -1075,6 +1048,49 @@ function getOutputAdder(
   };
 }
 
+function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void {
+  const feeRate = cache.__FEE_RATE || psbt.getFeeRate();
+  const vsize = cache.__EXTRACTED_TX!.virtualSize();
+  const satoshis = feeRate * vsize;
+  if (feeRate >= opts.maximumFeeRate) {
+    throw new Error(
+      `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
+        `fees, which is ${feeRate} satoshi per byte for a transaction ` +
+        `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
+        `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
+        `pass true to the first arg of extractTransaction.`,
+    );
+  }
+}
+
+function inputFinalizeGetAmts(
+  inputs: PsbtInput[],
+  tx: Transaction,
+  cache: PsbtCache,
+  mustFinalize: boolean,
+  getAmounts: boolean,
+): number {
+  let inputAmount = 0;
+  inputs.forEach((input, idx) => {
+    if (mustFinalize && input.finalScriptSig)
+      tx.ins[idx].script = input.finalScriptSig;
+    if (mustFinalize && input.finalScriptWitness) {
+      tx.ins[idx].witness = scriptWitnessToWitnessStack(
+        input.finalScriptWitness,
+      );
+    }
+    if (getAmounts && input.witnessUtxo) {
+      inputAmount += input.witnessUtxo.value;
+    } else if (getAmounts && input.nonWitnessUtxo) {
+      const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx);
+      const vout = tx.ins[idx].index;
+      const out = nwTx.outs[vout] as Output;
+      inputAmount += out.value;
+    }
+  });
+  return inputAmount;
+}
+
 function check32Bit(num: number): void {
   if (
     typeof num !== 'number' ||

From 2fd4b9dc54eeffabd35474a732d02a60f4c0a434 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 12:30:51 +0900
Subject: [PATCH 075/111] Refactor: pass only cache to certain functions

---
 src/psbt.js    | 38 ++++++++++++++------------------------
 ts_src/psbt.ts | 36 ++++++++++++++----------------------
 2 files changed, 28 insertions(+), 46 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index c2f9bd3..d7cc03a 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -26,25 +26,24 @@ class Psbt extends bip174_1.Psbt {
     };
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
-    this.__CACHE.__TX = transaction_1.Transaction.fromBuffer(
-      this.globalMap.unsignedTx,
-    );
+    const c = this.__CACHE;
+    c.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
     this.setVersion(2);
     // set cache
-    const self = this;
     delete this.globalMap.unsignedTx;
     Object.defineProperty(this.globalMap, 'unsignedTx', {
       enumerable: true,
       get() {
-        if (self.__CACHE.__TX_BUF_CACHE !== undefined) {
-          return self.__CACHE.__TX_BUF_CACHE;
+        const buf = c.__TX_BUF_CACHE;
+        if (buf !== undefined) {
+          return buf;
         } else {
-          self.__CACHE.__TX_BUF_CACHE = self.__CACHE.__TX.toBuffer();
-          return self.__CACHE.__TX_BUF_CACHE;
+          c.__TX_BUF_CACHE = c.__TX.toBuffer();
+          return c.__TX_BUF_CACHE;
         }
       },
       set(data) {
-        self.__CACHE.__TX_BUF_CACHE = data;
+        c.__TX_BUF_CACHE = data;
       },
     });
     // Make data hidden when enumerating
@@ -53,8 +52,6 @@ class Psbt extends bip174_1.Psbt {
         enumerable,
         writable,
       });
-    dpew(this, '__TX', false, true);
-    dpew(this, '__EXTRACTED_TX', false, true);
     dpew(this, '__CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
@@ -216,7 +213,6 @@ class Psbt extends bip174_1.Psbt {
     const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
-      this.__CACHE.__TX,
       this.__CACHE,
     );
     if (!script) return false;
@@ -258,7 +254,6 @@ class Psbt extends bip174_1.Psbt {
           ? getHashForSig(
               inputIndex,
               Object.assign({}, input, { sighashType: sig.hashType }),
-              this.__CACHE.__TX,
               this.__CACHE,
             )
           : { hash: hashCache, script: scriptCache };
@@ -327,7 +322,6 @@ class Psbt extends bip174_1.Psbt {
       this.inputs,
       inputIndex,
       keyPair.publicKey,
-      this.__CACHE.__TX,
       this.__CACHE,
     );
     const partialSig = {
@@ -344,7 +338,6 @@ class Psbt extends bip174_1.Psbt {
         this.inputs,
         inputIndex,
         keyPair.publicKey,
-        this.__CACHE.__TX,
         this.__CACHE,
       );
       Promise.resolve(keyPair.sign(hash)).then(signature => {
@@ -400,14 +393,9 @@ function checkTxInputCache(cache, input) {
 function isFinalized(input) {
   return !!input.finalScriptSig || !!input.finalScriptWitness;
 }
-function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx, cache) {
+function getHashAndSighashType(inputs, inputIndex, pubkey, cache) {
   const input = utils_1.checkForInput(inputs, inputIndex);
-  const { hash, sighashType, script } = getHashForSig(
-    inputIndex,
-    input,
-    unsignedTx,
-    cache,
-  );
+  const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
   checkScriptForPubkey(pubkey, script, 'sign');
   return {
     hash,
@@ -525,7 +513,8 @@ function checkScriptForPubkey(pubkey, script, action) {
     );
   }
 }
-function getHashForSig(inputIndex, input, unsignedTx, cache) {
+function getHashForSig(inputIndex, input, cache) {
+  const unsignedTx = cache.__TX;
   const sighashType =
     input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
   let hash;
@@ -646,7 +635,8 @@ function classifyScript(script) {
   if (isP2PK(script)) return 'pubkey';
   return 'nonstandard';
 }
-function getScriptFromInput(inputIndex, input, unsignedTx, cache) {
+function getScriptFromInput(inputIndex, input, cache) {
+  const unsignedTx = cache.__TX;
   const res = {
     script: null,
     isSegwit: false,
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 0771436..ffe417f 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -52,6 +52,7 @@ export class Psbt extends PsbtBase {
     }
     return psbt as InstanceType<T>;
   }
+
   static fromBuffer<T extends typeof PsbtBase>(
     this: T,
     buffer: Buffer,
@@ -75,6 +76,7 @@ export class Psbt extends PsbtBase {
     checkTxForDupeIns(tx!, psbt.__CACHE);
     return psbt as InstanceType<T>;
   }
+
   private __CACHE: PsbtCache = {
     __NON_WITNESS_UTXO_TX_CACHE: [],
     __NON_WITNESS_UTXO_BUF_CACHE: [],
@@ -82,28 +84,30 @@ export class Psbt extends PsbtBase {
     __TX: new Transaction(),
   };
   private opts: PsbtOpts;
+
   constructor(opts: PsbtOptsOptional = {}) {
     super();
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
-    this.__CACHE.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
+    const c = this.__CACHE;
+    c.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
     this.setVersion(2);
 
     // set cache
-    const self = this;
     delete this.globalMap.unsignedTx;
     Object.defineProperty(this.globalMap, 'unsignedTx', {
       enumerable: true,
       get(): Buffer {
-        if (self.__CACHE.__TX_BUF_CACHE !== undefined) {
-          return self.__CACHE.__TX_BUF_CACHE;
+        const buf = c.__TX_BUF_CACHE;
+        if (buf !== undefined) {
+          return buf;
         } else {
-          self.__CACHE.__TX_BUF_CACHE = self.__CACHE.__TX.toBuffer();
-          return self.__CACHE.__TX_BUF_CACHE;
+          c.__TX_BUF_CACHE = c.__TX.toBuffer();
+          return c.__TX_BUF_CACHE;
         }
       },
       set(data: Buffer): void {
-        self.__CACHE.__TX_BUF_CACHE = data;
+        c.__TX_BUF_CACHE = data;
       },
     });
 
@@ -118,8 +122,6 @@ export class Psbt extends PsbtBase {
         enumerable,
         writable,
       });
-    dpew(this, '__TX', false, true);
-    dpew(this, '__EXTRACTED_TX', false, true);
     dpew(this, '__CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
@@ -265,7 +267,6 @@ export class Psbt extends PsbtBase {
     const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
-      this.__CACHE.__TX,
       this.__CACHE,
     );
     if (!script) return false;
@@ -312,7 +313,6 @@ export class Psbt extends PsbtBase {
           ? getHashForSig(
               inputIndex,
               Object.assign({}, input, { sighashType: sig.hashType }),
-              this.__CACHE.__TX,
               this.__CACHE,
             )
           : { hash: hashCache!, script: scriptCache! };
@@ -388,7 +388,6 @@ export class Psbt extends PsbtBase {
       this.inputs,
       inputIndex,
       keyPair.publicKey,
-      this.__CACHE.__TX,
       this.__CACHE,
     );
 
@@ -409,7 +408,6 @@ export class Psbt extends PsbtBase {
           this.inputs,
           inputIndex,
           keyPair.publicKey,
-          this.__CACHE.__TX,
           this.__CACHE,
         );
 
@@ -513,19 +511,13 @@ function getHashAndSighashType(
   inputs: PsbtInput[],
   inputIndex: number,
   pubkey: Buffer,
-  unsignedTx: Transaction,
   cache: PsbtCache,
 ): {
   hash: Buffer;
   sighashType: number;
 } {
   const input = checkForInput(inputs, inputIndex);
-  const { hash, sighashType, script } = getHashForSig(
-    inputIndex,
-    input,
-    unsignedTx,
-    cache,
-  );
+  const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
   checkScriptForPubkey(pubkey, script, 'sign');
   return {
     hash,
@@ -678,9 +670,9 @@ interface HashForSigData {
 function getHashForSig(
   inputIndex: number,
   input: PsbtInput,
-  unsignedTx: Transaction,
   cache: PsbtCache,
 ): HashForSigData {
+  const unsignedTx = cache.__TX;
   const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
   let hash: Buffer;
   let script: Buffer;
@@ -831,9 +823,9 @@ interface GetScriptReturn {
 function getScriptFromInput(
   inputIndex: number,
   input: PsbtInput,
-  unsignedTx: Transaction,
   cache: PsbtCache,
 ): GetScriptReturn {
+  const unsignedTx = cache.__TX;
   const res: GetScriptReturn = {
     script: null,
     isSegwit: false,

From 479c56bbb4af63f11ac0df072add047f35fb8b81 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 12:58:03 +0900
Subject: [PATCH 076/111] Refactor: Re-order helper functions based on
 like-kind

---
 src/psbt.js    | 606 +++++++++++++++++++-------------------
 ts_src/psbt.ts | 773 ++++++++++++++++++++++++-------------------------
 2 files changed, 681 insertions(+), 698 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index d7cc03a..8e6f1f2 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -352,31 +352,120 @@ class Psbt extends bip174_1.Psbt {
   }
 }
 exports.Psbt = Psbt;
-function addNonWitnessTxCache(cache, input, inputIndex) {
-  cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo;
-  const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo);
-  cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
-  const self = cache;
-  const selfIndex = inputIndex;
-  delete input.nonWitnessUtxo;
-  Object.defineProperty(input, 'nonWitnessUtxo', {
-    enumerable: true,
-    get() {
-      const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
-      const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex];
-      if (buf !== undefined) {
-        return buf;
-      } else {
-        const newBuf = txCache.toBuffer();
-        self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf;
-        return newBuf;
+function canFinalize(input, script, scriptType) {
+  switch (scriptType) {
+    case 'pubkey':
+    case 'pubkeyhash':
+    case 'witnesspubkeyhash':
+      return hasSigs(1, input.partialSig);
+    case 'multisig':
+      const p2ms = payments.p2ms({ output: script });
+      return hasSigs(p2ms.m, input.partialSig);
+    default:
+      return false;
+  }
+}
+function hasSigs(neededSigs, partialSig) {
+  if (!partialSig) return false;
+  if (partialSig.length > neededSigs) throw new Error('Too many signatures');
+  return partialSig.length === neededSigs;
+}
+function isFinalized(input) {
+  return !!input.finalScriptSig || !!input.finalScriptWitness;
+}
+function isPaymentFactory(payment) {
+  return script => {
+    try {
+      payment({ output: script });
+      return true;
+    } catch (err) {
+      return false;
+    }
+  };
+}
+const isP2MS = isPaymentFactory(payments.p2ms);
+const isP2PK = isPaymentFactory(payments.p2pk);
+const isP2PKH = isPaymentFactory(payments.p2pkh);
+const isP2WPKH = isPaymentFactory(payments.p2wpkh);
+function check32Bit(num) {
+  if (
+    typeof num !== 'number' ||
+    num !== Math.floor(num) ||
+    num > 0xffffffff ||
+    num < 0
+  ) {
+    throw new Error('Invalid 32 bit integer');
+  }
+}
+function checkFees(psbt, cache, opts) {
+  const feeRate = cache.__FEE_RATE || psbt.getFeeRate();
+  const vsize = cache.__EXTRACTED_TX.virtualSize();
+  const satoshis = feeRate * vsize;
+  if (feeRate >= opts.maximumFeeRate) {
+    throw new Error(
+      `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
+        `fees, which is ${feeRate} satoshi per byte for a transaction ` +
+        `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
+        `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
+        `pass true to the first arg of extractTransaction.`,
+    );
+  }
+}
+function checkInputsForPartialSig(inputs, action) {
+  inputs.forEach(input => {
+    let throws = false;
+    if ((input.partialSig || []).length === 0) return;
+    input.partialSig.forEach(pSig => {
+      const { hashType } = bscript.signature.decode(pSig.signature);
+      const whitelist = [];
+      const isAnyoneCanPay =
+        hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
+      if (isAnyoneCanPay) whitelist.push('addInput');
+      const hashMod = hashType & 0x1f;
+      switch (hashMod) {
+        case transaction_1.Transaction.SIGHASH_ALL:
+          break;
+        case transaction_1.Transaction.SIGHASH_SINGLE:
+        case transaction_1.Transaction.SIGHASH_NONE:
+          whitelist.push('addOutput');
+          whitelist.push('setSequence');
+          break;
       }
-    },
-    set(data) {
-      self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
-    },
+      if (whitelist.indexOf(action) === -1) {
+        throws = true;
+      }
+    });
+    if (throws) {
+      throw new Error('Can not modify transaction, signatures exist.');
+    }
   });
 }
+function checkScriptForPubkey(pubkey, script, action) {
+  const pubkeyHash = crypto_1.hash160(pubkey);
+  const decompiled = bscript.decompile(script);
+  if (decompiled === null) throw new Error('Unknown script error');
+  const hasKey = decompiled.some(element => {
+    if (typeof element === 'number') return false;
+    return element.equals(pubkey) || element.equals(pubkeyHash);
+  });
+  if (!hasKey) {
+    throw new Error(
+      `Can not ${action} for this input with the key ${pubkey.toString('hex')}`,
+    );
+  }
+}
+function checkTxEmpty(tx) {
+  const isEmpty = tx.ins.every(
+    input =>
+      input.script &&
+      input.script.length === 0 &&
+      input.witness &&
+      input.witness.length === 0,
+  );
+  if (!isEmpty) {
+    throw new Error('Format Error: Transaction ScriptSigs are not empty');
+  }
+}
 function checkTxForDupeIns(tx, cache) {
   tx.ins.forEach(input => {
     checkTxInputCache(cache, input);
@@ -390,18 +479,23 @@ function checkTxInputCache(cache, input) {
   if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
   cache.__TX_IN_CACHE[key] = 1;
 }
-function isFinalized(input) {
-  return !!input.finalScriptSig || !!input.finalScriptWitness;
-}
-function getHashAndSighashType(inputs, inputIndex, pubkey, cache) {
-  const input = utils_1.checkForInput(inputs, inputIndex);
-  const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
-  checkScriptForPubkey(pubkey, script, 'sign');
-  return {
-    hash,
-    sighashType,
+function scriptCheckerFactory(payment, paymentScriptName) {
+  return (inputIndex, scriptPubKey, redeemScript) => {
+    const redeemScriptOutput = payment({
+      redeem: { output: redeemScript },
+    }).output;
+    if (!scriptPubKey.equals(redeemScriptOutput)) {
+      throw new Error(
+        `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+      );
+    }
   };
 }
+const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
+const checkWitnessScript = scriptCheckerFactory(
+  payments.p2wsh,
+  'Witness script',
+);
 function getFinalScripts(
   script,
   scriptType,
@@ -437,81 +531,14 @@ function getFinalScripts(
     finalScriptWitness,
   };
 }
-function getSortedSigs(script, partialSig) {
-  const p2ms = payments.p2ms({ output: script });
-  // for each pubkey in order of p2ms script
-  return p2ms.pubkeys
-    .map(pk => {
-      // filter partialSig array by pubkey being equal
-      return (
-        partialSig.filter(ps => {
-          return ps.pubkey.equals(pk);
-        })[0] || {}
-      ).signature;
-      // Any pubkey without a match will return undefined
-      // this last filter removes all the undefined items in the array.
-    })
-    .filter(v => !!v);
-}
-function getPayment(script, scriptType, partialSig) {
-  let payment;
-  switch (scriptType) {
-    case 'multisig':
-      const sigs = getSortedSigs(script, partialSig);
-      payment = payments.p2ms({
-        output: script,
-        signatures: sigs,
-      });
-      break;
-    case 'pubkey':
-      payment = payments.p2pk({
-        output: script,
-        signature: partialSig[0].signature,
-      });
-      break;
-    case 'pubkeyhash':
-      payment = payments.p2pkh({
-        output: script,
-        pubkey: partialSig[0].pubkey,
-        signature: partialSig[0].signature,
-      });
-      break;
-    case 'witnesspubkeyhash':
-      payment = payments.p2wpkh({
-        output: script,
-        pubkey: partialSig[0].pubkey,
-        signature: partialSig[0].signature,
-      });
-      break;
-  }
-  return payment;
-}
-function canFinalize(input, script, scriptType) {
-  switch (scriptType) {
-    case 'pubkey':
-    case 'pubkeyhash':
-    case 'witnesspubkeyhash':
-      return hasSigs(1, input.partialSig);
-    case 'multisig':
-      const p2ms = payments.p2ms({ output: script });
-      return hasSigs(p2ms.m, input.partialSig);
-    default:
-      return false;
-  }
-}
-function checkScriptForPubkey(pubkey, script, action) {
-  const pubkeyHash = crypto_1.hash160(pubkey);
-  const decompiled = bscript.decompile(script);
-  if (decompiled === null) throw new Error('Unknown script error');
-  const hasKey = decompiled.some(element => {
-    if (typeof element === 'number') return false;
-    return element.equals(pubkey) || element.equals(pubkeyHash);
-  });
-  if (!hasKey) {
-    throw new Error(
-      `Can not ${action} for this input with the key ${pubkey.toString('hex')}`,
-    );
-  }
+function getHashAndSighashType(inputs, inputIndex, pubkey, cache) {
+  const input = utils_1.checkForInput(inputs, inputIndex);
+  const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
+  checkScriptForPubkey(pubkey, script, 'sign');
+  return {
+    hash,
+    sighashType,
+  };
 }
 function getHashForSig(inputIndex, input, cache) {
   const unsignedTx = cache.__TX;
@@ -597,182 +624,6 @@ function getHashForSig(inputIndex, input, cache) {
     hash,
   };
 }
-function scriptCheckerFactory(payment, paymentScriptName) {
-  return (inputIndex, scriptPubKey, redeemScript) => {
-    const redeemScriptOutput = payment({
-      redeem: { output: redeemScript },
-    }).output;
-    if (!scriptPubKey.equals(redeemScriptOutput)) {
-      throw new Error(
-        `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
-      );
-    }
-  };
-}
-const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
-const checkWitnessScript = scriptCheckerFactory(
-  payments.p2wsh,
-  'Witness script',
-);
-function isPaymentFactory(payment) {
-  return script => {
-    try {
-      payment({ output: script });
-      return true;
-    } catch (err) {
-      return false;
-    }
-  };
-}
-const isP2WPKH = isPaymentFactory(payments.p2wpkh);
-const isP2PKH = isPaymentFactory(payments.p2pkh);
-const isP2MS = isPaymentFactory(payments.p2ms);
-const isP2PK = isPaymentFactory(payments.p2pk);
-function classifyScript(script) {
-  if (isP2WPKH(script)) return 'witnesspubkeyhash';
-  if (isP2PKH(script)) return 'pubkeyhash';
-  if (isP2MS(script)) return 'multisig';
-  if (isP2PK(script)) return 'pubkey';
-  return 'nonstandard';
-}
-function getScriptFromInput(inputIndex, input, cache) {
-  const unsignedTx = cache.__TX;
-  const res = {
-    script: null,
-    isSegwit: false,
-    isP2SH: false,
-    isP2WSH: false,
-  };
-  if (input.nonWitnessUtxo) {
-    if (input.redeemScript) {
-      res.isP2SH = true;
-      res.script = input.redeemScript;
-    } else {
-      const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
-        cache,
-        input,
-        inputIndex,
-      );
-      const prevoutIndex = unsignedTx.ins[inputIndex].index;
-      res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
-    }
-  } else if (input.witnessUtxo) {
-    res.isSegwit = true;
-    res.isP2SH = !!input.redeemScript;
-    res.isP2WSH = !!input.witnessScript;
-    if (input.witnessScript) {
-      res.script = input.witnessScript;
-    } else if (input.redeemScript) {
-      res.script = payments.p2wpkh({
-        hash: input.redeemScript.slice(2),
-      }).output;
-    } else {
-      res.script = payments.p2wpkh({
-        hash: input.witnessUtxo.script.slice(2),
-      }).output;
-    }
-  }
-  return res;
-}
-function hasSigs(neededSigs, partialSig) {
-  if (!partialSig) return false;
-  if (partialSig.length > neededSigs) throw new Error('Too many signatures');
-  return partialSig.length === neededSigs;
-}
-function witnessStackToScriptWitness(witness) {
-  let buffer = Buffer.allocUnsafe(0);
-  function writeSlice(slice) {
-    buffer = Buffer.concat([buffer, Buffer.from(slice)]);
-  }
-  function writeVarInt(i) {
-    const currentLen = buffer.length;
-    const varintLen = varuint.encodingLength(i);
-    buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
-    varuint.encode(i, buffer, currentLen);
-  }
-  function writeVarSlice(slice) {
-    writeVarInt(slice.length);
-    writeSlice(slice);
-  }
-  function writeVector(vector) {
-    writeVarInt(vector.length);
-    vector.forEach(writeVarSlice);
-  }
-  writeVector(witness);
-  return buffer;
-}
-function scriptWitnessToWitnessStack(buffer) {
-  let offset = 0;
-  function readSlice(n) {
-    offset += n;
-    return buffer.slice(offset - n, offset);
-  }
-  function readVarInt() {
-    const vi = varuint.decode(buffer, offset);
-    offset += varuint.decode.bytes;
-    return vi;
-  }
-  function readVarSlice() {
-    return readSlice(readVarInt());
-  }
-  function readVector() {
-    const count = readVarInt();
-    const vector = [];
-    for (let i = 0; i < count; i++) vector.push(readVarSlice());
-    return vector;
-  }
-  return readVector();
-}
-function range(n) {
-  return [...Array(n).keys()];
-}
-function checkTxEmpty(tx) {
-  const isEmpty = tx.ins.every(
-    input =>
-      input.script &&
-      input.script.length === 0 &&
-      input.witness &&
-      input.witness.length === 0,
-  );
-  if (!isEmpty) {
-    throw new Error('Format Error: Transaction ScriptSigs are not empty');
-  }
-}
-function checkInputsForPartialSig(inputs, action) {
-  inputs.forEach(input => {
-    let throws = false;
-    if ((input.partialSig || []).length === 0) return;
-    input.partialSig.forEach(pSig => {
-      const { hashType } = bscript.signature.decode(pSig.signature);
-      const whitelist = [];
-      const isAnyoneCanPay =
-        hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
-      if (isAnyoneCanPay) whitelist.push('addInput');
-      const hashMod = hashType & 0x1f;
-      switch (hashMod) {
-        case transaction_1.Transaction.SIGHASH_ALL:
-          break;
-        case transaction_1.Transaction.SIGHASH_SINGLE:
-        case transaction_1.Transaction.SIGHASH_NONE:
-          whitelist.push('addOutput');
-          whitelist.push('setSequence');
-          break;
-      }
-      if (whitelist.indexOf(action) === -1) {
-        throws = true;
-      }
-    });
-    if (throws) {
-      throw new Error('Can not modify transaction, signatures exist.');
-    }
-  });
-}
-function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
-  if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
-    addNonWitnessTxCache(cache, input, inputIndex);
-  }
-  return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
-}
 function getInputAdder(cache) {
   const selfCache = cache;
   return (_inputData, txBuf) => {
@@ -822,19 +673,162 @@ function getOutputAdder(cache) {
     return selfCache.__TX.toBuffer();
   };
 }
-function checkFees(psbt, cache, opts) {
-  const feeRate = cache.__FEE_RATE || psbt.getFeeRate();
-  const vsize = cache.__EXTRACTED_TX.virtualSize();
-  const satoshis = feeRate * vsize;
-  if (feeRate >= opts.maximumFeeRate) {
-    throw new Error(
-      `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
-        `fees, which is ${feeRate} satoshi per byte for a transaction ` +
-        `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
-        `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
-        `pass true to the first arg of extractTransaction.`,
-    );
+function getPayment(script, scriptType, partialSig) {
+  let payment;
+  switch (scriptType) {
+    case 'multisig':
+      const sigs = getSortedSigs(script, partialSig);
+      payment = payments.p2ms({
+        output: script,
+        signatures: sigs,
+      });
+      break;
+    case 'pubkey':
+      payment = payments.p2pk({
+        output: script,
+        signature: partialSig[0].signature,
+      });
+      break;
+    case 'pubkeyhash':
+      payment = payments.p2pkh({
+        output: script,
+        pubkey: partialSig[0].pubkey,
+        signature: partialSig[0].signature,
+      });
+      break;
+    case 'witnesspubkeyhash':
+      payment = payments.p2wpkh({
+        output: script,
+        pubkey: partialSig[0].pubkey,
+        signature: partialSig[0].signature,
+      });
+      break;
   }
+  return payment;
+}
+function getScriptFromInput(inputIndex, input, cache) {
+  const unsignedTx = cache.__TX;
+  const res = {
+    script: null,
+    isSegwit: false,
+    isP2SH: false,
+    isP2WSH: false,
+  };
+  if (input.nonWitnessUtxo) {
+    if (input.redeemScript) {
+      res.isP2SH = true;
+      res.script = input.redeemScript;
+    } else {
+      const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
+        cache,
+        input,
+        inputIndex,
+      );
+      const prevoutIndex = unsignedTx.ins[inputIndex].index;
+      res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
+    }
+  } else if (input.witnessUtxo) {
+    res.isSegwit = true;
+    res.isP2SH = !!input.redeemScript;
+    res.isP2WSH = !!input.witnessScript;
+    if (input.witnessScript) {
+      res.script = input.witnessScript;
+    } else if (input.redeemScript) {
+      res.script = payments.p2wpkh({
+        hash: input.redeemScript.slice(2),
+      }).output;
+    } else {
+      res.script = payments.p2wpkh({
+        hash: input.witnessUtxo.script.slice(2),
+      }).output;
+    }
+  }
+  return res;
+}
+function getSortedSigs(script, partialSig) {
+  const p2ms = payments.p2ms({ output: script });
+  // for each pubkey in order of p2ms script
+  return p2ms.pubkeys
+    .map(pk => {
+      // filter partialSig array by pubkey being equal
+      return (
+        partialSig.filter(ps => {
+          return ps.pubkey.equals(pk);
+        })[0] || {}
+      ).signature;
+      // Any pubkey without a match will return undefined
+      // this last filter removes all the undefined items in the array.
+    })
+    .filter(v => !!v);
+}
+function scriptWitnessToWitnessStack(buffer) {
+  let offset = 0;
+  function readSlice(n) {
+    offset += n;
+    return buffer.slice(offset - n, offset);
+  }
+  function readVarInt() {
+    const vi = varuint.decode(buffer, offset);
+    offset += varuint.decode.bytes;
+    return vi;
+  }
+  function readVarSlice() {
+    return readSlice(readVarInt());
+  }
+  function readVector() {
+    const count = readVarInt();
+    const vector = [];
+    for (let i = 0; i < count; i++) vector.push(readVarSlice());
+    return vector;
+  }
+  return readVector();
+}
+function witnessStackToScriptWitness(witness) {
+  let buffer = Buffer.allocUnsafe(0);
+  function writeSlice(slice) {
+    buffer = Buffer.concat([buffer, Buffer.from(slice)]);
+  }
+  function writeVarInt(i) {
+    const currentLen = buffer.length;
+    const varintLen = varuint.encodingLength(i);
+    buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
+    varuint.encode(i, buffer, currentLen);
+  }
+  function writeVarSlice(slice) {
+    writeVarInt(slice.length);
+    writeSlice(slice);
+  }
+  function writeVector(vector) {
+    writeVarInt(vector.length);
+    vector.forEach(writeVarSlice);
+  }
+  writeVector(witness);
+  return buffer;
+}
+function addNonWitnessTxCache(cache, input, inputIndex) {
+  cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo;
+  const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo);
+  cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
+  const self = cache;
+  const selfIndex = inputIndex;
+  delete input.nonWitnessUtxo;
+  Object.defineProperty(input, 'nonWitnessUtxo', {
+    enumerable: true,
+    get() {
+      const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+      const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex];
+      if (buf !== undefined) {
+        return buf;
+      } else {
+        const newBuf = txCache.toBuffer();
+        self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf;
+        return newBuf;
+      }
+    },
+    set(data) {
+      self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
+    },
+  });
 }
 function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) {
   let inputAmount = 0;
@@ -857,13 +851,19 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) {
   });
   return inputAmount;
 }
-function check32Bit(num) {
-  if (
-    typeof num !== 'number' ||
-    num !== Math.floor(num) ||
-    num > 0xffffffff ||
-    num < 0
-  ) {
-    throw new Error('Invalid 32 bit integer');
+function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
+  if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+    addNonWitnessTxCache(cache, input, inputIndex);
   }
+  return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+}
+function classifyScript(script) {
+  if (isP2WPKH(script)) return 'witnesspubkeyhash';
+  if (isP2PKH(script)) return 'pubkeyhash';
+  if (isP2MS(script)) return 'multisig';
+  if (isP2PK(script)) return 'pubkey';
+  return 'nonstandard';
+}
+function range(n) {
+  return [...Array(n).keys()];
 }
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index ffe417f..617cde9 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -425,16 +425,6 @@ export class Psbt extends PsbtBase {
   }
 }
 
-//
-//
-//
-//
-// Helper functions
-//
-//
-//
-//
-
 interface PsbtCache {
   __NON_WITNESS_UTXO_TX_CACHE: Transaction[];
   __NON_WITNESS_UTXO_BUF_CACHE: Buffer[];
@@ -455,38 +445,139 @@ interface PsbtOpts {
   maximumFeeRate: number;
 }
 
-function addNonWitnessTxCache(
-  cache: PsbtCache,
+function canFinalize(
   input: PsbtInput,
-  inputIndex: number,
-): void {
-  cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!;
+  script: Buffer,
+  scriptType: string,
+): boolean {
+  switch (scriptType) {
+    case 'pubkey':
+    case 'pubkeyhash':
+    case 'witnesspubkeyhash':
+      return hasSigs(1, input.partialSig);
+    case 'multisig':
+      const p2ms = payments.p2ms({ output: script });
+      return hasSigs(p2ms.m!, input.partialSig);
+    default:
+      return false;
+  }
+}
 
-  const tx = Transaction.fromBuffer(input.nonWitnessUtxo!);
-  cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
+function hasSigs(neededSigs: number, partialSig?: any[]): boolean {
+  if (!partialSig) return false;
+  if (partialSig.length > neededSigs) throw new Error('Too many signatures');
+  return partialSig.length === neededSigs;
+}
 
-  const self = cache;
-  const selfIndex = inputIndex;
-  delete input.nonWitnessUtxo;
-  Object.defineProperty(input, 'nonWitnessUtxo', {
-    enumerable: true,
-    get(): Buffer {
-      const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
-      const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex];
-      if (buf !== undefined) {
-        return buf;
-      } else {
-        const newBuf = txCache.toBuffer();
-        self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf;
-        return newBuf;
+function isFinalized(input: PsbtInput): boolean {
+  return !!input.finalScriptSig || !!input.finalScriptWitness;
+}
+
+function isPaymentFactory(payment: any): (script: Buffer) => boolean {
+  return (script: Buffer): boolean => {
+    try {
+      payment({ output: script });
+      return true;
+    } catch (err) {
+      return false;
+    }
+  };
+}
+const isP2MS = isPaymentFactory(payments.p2ms);
+const isP2PK = isPaymentFactory(payments.p2pk);
+const isP2PKH = isPaymentFactory(payments.p2pkh);
+const isP2WPKH = isPaymentFactory(payments.p2wpkh);
+
+function check32Bit(num: number): void {
+  if (
+    typeof num !== 'number' ||
+    num !== Math.floor(num) ||
+    num > 0xffffffff ||
+    num < 0
+  ) {
+    throw new Error('Invalid 32 bit integer');
+  }
+}
+
+function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void {
+  const feeRate = cache.__FEE_RATE || psbt.getFeeRate();
+  const vsize = cache.__EXTRACTED_TX!.virtualSize();
+  const satoshis = feeRate * vsize;
+  if (feeRate >= opts.maximumFeeRate) {
+    throw new Error(
+      `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
+        `fees, which is ${feeRate} satoshi per byte for a transaction ` +
+        `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
+        `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
+        `pass true to the first arg of extractTransaction.`,
+    );
+  }
+}
+
+function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
+  inputs.forEach(input => {
+    let throws = false;
+    if ((input.partialSig || []).length === 0) return;
+    input.partialSig!.forEach(pSig => {
+      const { hashType } = bscript.signature.decode(pSig.signature);
+      const whitelist: string[] = [];
+      const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY;
+      if (isAnyoneCanPay) whitelist.push('addInput');
+      const hashMod = hashType & 0x1f;
+      switch (hashMod) {
+        case Transaction.SIGHASH_ALL:
+          break;
+        case Transaction.SIGHASH_SINGLE:
+        case Transaction.SIGHASH_NONE:
+          whitelist.push('addOutput');
+          whitelist.push('setSequence');
+          break;
       }
-    },
-    set(data: Buffer): void {
-      self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
-    },
+      if (whitelist.indexOf(action) === -1) {
+        throws = true;
+      }
+    });
+    if (throws) {
+      throw new Error('Can not modify transaction, signatures exist.');
+    }
   });
 }
 
+function checkScriptForPubkey(
+  pubkey: Buffer,
+  script: Buffer,
+  action: string,
+): void {
+  const pubkeyHash = hash160(pubkey);
+
+  const decompiled = bscript.decompile(script);
+  if (decompiled === null) throw new Error('Unknown script error');
+
+  const hasKey = decompiled.some(element => {
+    if (typeof element === 'number') return false;
+    return element.equals(pubkey) || element.equals(pubkeyHash);
+  });
+
+  if (!hasKey) {
+    throw new Error(
+      `Can not ${action} for this input with the key ${pubkey.toString('hex')}`,
+    );
+  }
+}
+
+function checkTxEmpty(tx: Transaction): void {
+  const isEmpty = tx.ins.every(
+    input =>
+      input.script &&
+      input.script.length === 0 &&
+      input.witness &&
+      input.witness.length === 0,
+  );
+  if (!isEmpty) {
+    throw new Error('Format Error: Transaction ScriptSigs are not empty');
+  }
+}
+
 function checkTxForDupeIns(tx: Transaction, cache: PsbtCache): void {
   tx.ins.forEach(input => {
     checkTxInputCache(cache, input);
@@ -503,27 +594,31 @@ function checkTxInputCache(
   cache.__TX_IN_CACHE[key] = 1;
 }
 
-function isFinalized(input: PsbtInput): boolean {
-  return !!input.finalScriptSig || !!input.finalScriptWitness;
-}
+function scriptCheckerFactory(
+  payment: any,
+  paymentScriptName: string,
+): (idx: number, spk: Buffer, rs: Buffer) => void {
+  return (
+    inputIndex: number,
+    scriptPubKey: Buffer,
+    redeemScript: Buffer,
+  ): void => {
+    const redeemScriptOutput = payment({
+      redeem: { output: redeemScript },
+    }).output as Buffer;
 
-function getHashAndSighashType(
-  inputs: PsbtInput[],
-  inputIndex: number,
-  pubkey: Buffer,
-  cache: PsbtCache,
-): {
-  hash: Buffer;
-  sighashType: number;
-} {
-  const input = checkForInput(inputs, inputIndex);
-  const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
-  checkScriptForPubkey(pubkey, script, 'sign');
-  return {
-    hash,
-    sighashType,
+    if (!scriptPubKey.equals(redeemScriptOutput)) {
+      throw new Error(
+        `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+      );
+    }
   };
 }
+const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
+const checkWitnessScript = scriptCheckerFactory(
+  payments.p2wsh,
+  'Witness script',
+);
 
 function getFinalScripts(
   script: Buffer,
@@ -566,112 +661,33 @@ function getFinalScripts(
   };
 }
 
-function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] {
-  const p2ms = payments.p2ms({ output: script });
-  // for each pubkey in order of p2ms script
-  return p2ms
-    .pubkeys!.map(pk => {
-      // filter partialSig array by pubkey being equal
-      return (
-        partialSig.filter(ps => {
-          return ps.pubkey.equals(pk);
-        })[0] || {}
-      ).signature;
-      // Any pubkey without a match will return undefined
-      // this last filter removes all the undefined items in the array.
-    })
-    .filter(v => !!v);
-}
-
-function getPayment(
-  script: Buffer,
-  scriptType: string,
-  partialSig: PartialSig[],
-): payments.Payment {
-  let payment: payments.Payment;
-  switch (scriptType) {
-    case 'multisig':
-      const sigs = getSortedSigs(script, partialSig);
-      payment = payments.p2ms({
-        output: script,
-        signatures: sigs,
-      });
-      break;
-    case 'pubkey':
-      payment = payments.p2pk({
-        output: script,
-        signature: partialSig[0].signature,
-      });
-      break;
-    case 'pubkeyhash':
-      payment = payments.p2pkh({
-        output: script,
-        pubkey: partialSig[0].pubkey,
-        signature: partialSig[0].signature,
-      });
-      break;
-    case 'witnesspubkeyhash':
-      payment = payments.p2wpkh({
-        output: script,
-        pubkey: partialSig[0].pubkey,
-        signature: partialSig[0].signature,
-      });
-      break;
-  }
-  return payment!;
-}
-
-function canFinalize(
-  input: PsbtInput,
-  script: Buffer,
-  scriptType: string,
-): boolean {
-  switch (scriptType) {
-    case 'pubkey':
-    case 'pubkeyhash':
-    case 'witnesspubkeyhash':
-      return hasSigs(1, input.partialSig);
-    case 'multisig':
-      const p2ms = payments.p2ms({ output: script });
-      return hasSigs(p2ms.m!, input.partialSig);
-    default:
-      return false;
-  }
-}
-
-function checkScriptForPubkey(
+function getHashAndSighashType(
+  inputs: PsbtInput[],
+  inputIndex: number,
   pubkey: Buffer,
-  script: Buffer,
-  action: string,
-): void {
-  const pubkeyHash = hash160(pubkey);
-
-  const decompiled = bscript.decompile(script);
-  if (decompiled === null) throw new Error('Unknown script error');
-
-  const hasKey = decompiled.some(element => {
-    if (typeof element === 'number') return false;
-    return element.equals(pubkey) || element.equals(pubkeyHash);
-  });
-
-  if (!hasKey) {
-    throw new Error(
-      `Can not ${action} for this input with the key ${pubkey.toString('hex')}`,
-    );
-  }
-}
-
-interface HashForSigData {
-  script: Buffer;
+  cache: PsbtCache,
+): {
   hash: Buffer;
   sighashType: number;
+} {
+  const input = checkForInput(inputs, inputIndex);
+  const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
+  checkScriptForPubkey(pubkey, script, 'sign');
+  return {
+    hash,
+    sighashType,
+  };
 }
 
 function getHashForSig(
   inputIndex: number,
   input: PsbtInput,
   cache: PsbtCache,
-): HashForSigData {
+): {
+  script: Buffer;
+  hash: Buffer;
+  sighashType: number;
+} {
   const unsignedTx = cache.__TX;
   const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
   let hash: Buffer;
@@ -760,231 +776,6 @@ function getHashForSig(
   };
 }
 
-type ScriptCheckerFunction = (idx: number, spk: Buffer, rs: Buffer) => void;
-
-function scriptCheckerFactory(
-  payment: any,
-  paymentScriptName: string,
-): ScriptCheckerFunction {
-  return (
-    inputIndex: number,
-    scriptPubKey: Buffer,
-    redeemScript: Buffer,
-  ): void => {
-    const redeemScriptOutput = payment({
-      redeem: { output: redeemScript },
-    }).output as Buffer;
-
-    if (!scriptPubKey.equals(redeemScriptOutput)) {
-      throw new Error(
-        `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
-      );
-    }
-  };
-}
-
-const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
-const checkWitnessScript = scriptCheckerFactory(
-  payments.p2wsh,
-  'Witness script',
-);
-
-type isPaymentFunction = (script: Buffer) => boolean;
-
-function isPaymentFactory(payment: any): isPaymentFunction {
-  return (script: Buffer): boolean => {
-    try {
-      payment({ output: script });
-      return true;
-    } catch (err) {
-      return false;
-    }
-  };
-}
-const isP2WPKH = isPaymentFactory(payments.p2wpkh);
-const isP2PKH = isPaymentFactory(payments.p2pkh);
-const isP2MS = isPaymentFactory(payments.p2ms);
-const isP2PK = isPaymentFactory(payments.p2pk);
-
-function classifyScript(script: Buffer): string {
-  if (isP2WPKH(script)) return 'witnesspubkeyhash';
-  if (isP2PKH(script)) return 'pubkeyhash';
-  if (isP2MS(script)) return 'multisig';
-  if (isP2PK(script)) return 'pubkey';
-  return 'nonstandard';
-}
-
-interface GetScriptReturn {
-  script: Buffer | null;
-  isSegwit: boolean;
-  isP2SH: boolean;
-  isP2WSH: boolean;
-}
-function getScriptFromInput(
-  inputIndex: number,
-  input: PsbtInput,
-  cache: PsbtCache,
-): GetScriptReturn {
-  const unsignedTx = cache.__TX;
-  const res: GetScriptReturn = {
-    script: null,
-    isSegwit: false,
-    isP2SH: false,
-    isP2WSH: false,
-  };
-  if (input.nonWitnessUtxo) {
-    if (input.redeemScript) {
-      res.isP2SH = true;
-      res.script = input.redeemScript;
-    } else {
-      const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
-        cache,
-        input,
-        inputIndex,
-      );
-      const prevoutIndex = unsignedTx.ins[inputIndex].index;
-      res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
-    }
-  } else if (input.witnessUtxo) {
-    res.isSegwit = true;
-    res.isP2SH = !!input.redeemScript;
-    res.isP2WSH = !!input.witnessScript;
-    if (input.witnessScript) {
-      res.script = input.witnessScript;
-    } else if (input.redeemScript) {
-      res.script = payments.p2wpkh({
-        hash: input.redeemScript.slice(2),
-      }).output!;
-    } else {
-      res.script = payments.p2wpkh({
-        hash: input.witnessUtxo.script.slice(2),
-      }).output!;
-    }
-  }
-  return res;
-}
-
-function hasSigs(neededSigs: number, partialSig?: any[]): boolean {
-  if (!partialSig) return false;
-  if (partialSig.length > neededSigs) throw new Error('Too many signatures');
-  return partialSig.length === neededSigs;
-}
-
-function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
-  let buffer = Buffer.allocUnsafe(0);
-
-  function writeSlice(slice: Buffer): void {
-    buffer = Buffer.concat([buffer, Buffer.from(slice)]);
-  }
-
-  function writeVarInt(i: number): void {
-    const currentLen = buffer.length;
-    const varintLen = varuint.encodingLength(i);
-
-    buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
-    varuint.encode(i, buffer, currentLen);
-  }
-
-  function writeVarSlice(slice: Buffer): void {
-    writeVarInt(slice.length);
-    writeSlice(slice);
-  }
-
-  function writeVector(vector: Buffer[]): void {
-    writeVarInt(vector.length);
-    vector.forEach(writeVarSlice);
-  }
-
-  writeVector(witness);
-
-  return buffer;
-}
-
-function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
-  let offset = 0;
-
-  function readSlice(n: number): Buffer {
-    offset += n;
-    return buffer.slice(offset - n, offset);
-  }
-
-  function readVarInt(): number {
-    const vi = varuint.decode(buffer, offset);
-    offset += varuint.decode.bytes;
-    return vi;
-  }
-
-  function readVarSlice(): Buffer {
-    return readSlice(readVarInt());
-  }
-
-  function readVector(): Buffer[] {
-    const count = readVarInt();
-    const vector: Buffer[] = [];
-    for (let i = 0; i < count; i++) vector.push(readVarSlice());
-    return vector;
-  }
-
-  return readVector();
-}
-
-function range(n: number): number[] {
-  return [...Array(n).keys()];
-}
-
-function checkTxEmpty(tx: Transaction): void {
-  const isEmpty = tx.ins.every(
-    input =>
-      input.script &&
-      input.script.length === 0 &&
-      input.witness &&
-      input.witness.length === 0,
-  );
-  if (!isEmpty) {
-    throw new Error('Format Error: Transaction ScriptSigs are not empty');
-  }
-}
-
-function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
-  inputs.forEach(input => {
-    let throws = false;
-    if ((input.partialSig || []).length === 0) return;
-    input.partialSig!.forEach(pSig => {
-      const { hashType } = bscript.signature.decode(pSig.signature);
-      const whitelist: string[] = [];
-      const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY;
-      if (isAnyoneCanPay) whitelist.push('addInput');
-      const hashMod = hashType & 0x1f;
-      switch (hashMod) {
-        case Transaction.SIGHASH_ALL:
-          break;
-        case Transaction.SIGHASH_SINGLE:
-        case Transaction.SIGHASH_NONE:
-          whitelist.push('addOutput');
-          whitelist.push('setSequence');
-          break;
-      }
-      if (whitelist.indexOf(action) === -1) {
-        throws = true;
-      }
-    });
-    if (throws) {
-      throw new Error('Can not modify transaction, signatures exist.');
-    }
-  });
-}
-
-function nonWitnessUtxoTxFromCache(
-  cache: PsbtCache,
-  input: PsbtInput,
-  inputIndex: number,
-): Transaction {
-  if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
-    addNonWitnessTxCache(cache, input, inputIndex);
-  }
-  return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
-}
-
 function getInputAdder(
   cache: PsbtCache,
 ): (_inputData: TransactionInput, txBuf: Buffer) => Buffer {
@@ -1040,19 +831,199 @@ function getOutputAdder(
   };
 }
 
-function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void {
-  const feeRate = cache.__FEE_RATE || psbt.getFeeRate();
-  const vsize = cache.__EXTRACTED_TX!.virtualSize();
-  const satoshis = feeRate * vsize;
-  if (feeRate >= opts.maximumFeeRate) {
-    throw new Error(
-      `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
-        `fees, which is ${feeRate} satoshi per byte for a transaction ` +
-        `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
-        `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
-        `pass true to the first arg of extractTransaction.`,
-    );
+function getPayment(
+  script: Buffer,
+  scriptType: string,
+  partialSig: PartialSig[],
+): payments.Payment {
+  let payment: payments.Payment;
+  switch (scriptType) {
+    case 'multisig':
+      const sigs = getSortedSigs(script, partialSig);
+      payment = payments.p2ms({
+        output: script,
+        signatures: sigs,
+      });
+      break;
+    case 'pubkey':
+      payment = payments.p2pk({
+        output: script,
+        signature: partialSig[0].signature,
+      });
+      break;
+    case 'pubkeyhash':
+      payment = payments.p2pkh({
+        output: script,
+        pubkey: partialSig[0].pubkey,
+        signature: partialSig[0].signature,
+      });
+      break;
+    case 'witnesspubkeyhash':
+      payment = payments.p2wpkh({
+        output: script,
+        pubkey: partialSig[0].pubkey,
+        signature: partialSig[0].signature,
+      });
+      break;
   }
+  return payment!;
+}
+
+interface GetScriptReturn {
+  script: Buffer | null;
+  isSegwit: boolean;
+  isP2SH: boolean;
+  isP2WSH: boolean;
+}
+function getScriptFromInput(
+  inputIndex: number,
+  input: PsbtInput,
+  cache: PsbtCache,
+): GetScriptReturn {
+  const unsignedTx = cache.__TX;
+  const res: GetScriptReturn = {
+    script: null,
+    isSegwit: false,
+    isP2SH: false,
+    isP2WSH: false,
+  };
+  if (input.nonWitnessUtxo) {
+    if (input.redeemScript) {
+      res.isP2SH = true;
+      res.script = input.redeemScript;
+    } else {
+      const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
+        cache,
+        input,
+        inputIndex,
+      );
+      const prevoutIndex = unsignedTx.ins[inputIndex].index;
+      res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
+    }
+  } else if (input.witnessUtxo) {
+    res.isSegwit = true;
+    res.isP2SH = !!input.redeemScript;
+    res.isP2WSH = !!input.witnessScript;
+    if (input.witnessScript) {
+      res.script = input.witnessScript;
+    } else if (input.redeemScript) {
+      res.script = payments.p2wpkh({
+        hash: input.redeemScript.slice(2),
+      }).output!;
+    } else {
+      res.script = payments.p2wpkh({
+        hash: input.witnessUtxo.script.slice(2),
+      }).output!;
+    }
+  }
+  return res;
+}
+
+function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] {
+  const p2ms = payments.p2ms({ output: script });
+  // for each pubkey in order of p2ms script
+  return p2ms
+    .pubkeys!.map(pk => {
+      // filter partialSig array by pubkey being equal
+      return (
+        partialSig.filter(ps => {
+          return ps.pubkey.equals(pk);
+        })[0] || {}
+      ).signature;
+      // Any pubkey without a match will return undefined
+      // this last filter removes all the undefined items in the array.
+    })
+    .filter(v => !!v);
+}
+
+function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
+  let offset = 0;
+
+  function readSlice(n: number): Buffer {
+    offset += n;
+    return buffer.slice(offset - n, offset);
+  }
+
+  function readVarInt(): number {
+    const vi = varuint.decode(buffer, offset);
+    offset += varuint.decode.bytes;
+    return vi;
+  }
+
+  function readVarSlice(): Buffer {
+    return readSlice(readVarInt());
+  }
+
+  function readVector(): Buffer[] {
+    const count = readVarInt();
+    const vector: Buffer[] = [];
+    for (let i = 0; i < count; i++) vector.push(readVarSlice());
+    return vector;
+  }
+
+  return readVector();
+}
+
+function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
+  let buffer = Buffer.allocUnsafe(0);
+
+  function writeSlice(slice: Buffer): void {
+    buffer = Buffer.concat([buffer, Buffer.from(slice)]);
+  }
+
+  function writeVarInt(i: number): void {
+    const currentLen = buffer.length;
+    const varintLen = varuint.encodingLength(i);
+
+    buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
+    varuint.encode(i, buffer, currentLen);
+  }
+
+  function writeVarSlice(slice: Buffer): void {
+    writeVarInt(slice.length);
+    writeSlice(slice);
+  }
+
+  function writeVector(vector: Buffer[]): void {
+    writeVarInt(vector.length);
+    vector.forEach(writeVarSlice);
+  }
+
+  writeVector(witness);
+
+  return buffer;
+}
+
+function addNonWitnessTxCache(
+  cache: PsbtCache,
+  input: PsbtInput,
+  inputIndex: number,
+): void {
+  cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!;
+
+  const tx = Transaction.fromBuffer(input.nonWitnessUtxo!);
+  cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
+
+  const self = cache;
+  const selfIndex = inputIndex;
+  delete input.nonWitnessUtxo;
+  Object.defineProperty(input, 'nonWitnessUtxo', {
+    enumerable: true,
+    get(): Buffer {
+      const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+      const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex];
+      if (buf !== undefined) {
+        return buf;
+      } else {
+        const newBuf = txCache.toBuffer();
+        self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf;
+        return newBuf;
+      }
+    },
+    set(data: Buffer): void {
+      self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
+    },
+  });
 }
 
 function inputFinalizeGetAmts(
@@ -1083,13 +1054,25 @@ function inputFinalizeGetAmts(
   return inputAmount;
 }
 
-function check32Bit(num: number): void {
-  if (
-    typeof num !== 'number' ||
-    num !== Math.floor(num) ||
-    num > 0xffffffff ||
-    num < 0
-  ) {
-    throw new Error('Invalid 32 bit integer');
+function nonWitnessUtxoTxFromCache(
+  cache: PsbtCache,
+  input: PsbtInput,
+  inputIndex: number,
+): Transaction {
+  if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+    addNonWitnessTxCache(cache, input, inputIndex);
   }
+  return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+}
+
+function classifyScript(script: Buffer): string {
+  if (isP2WPKH(script)) return 'witnesspubkeyhash';
+  if (isP2PKH(script)) return 'pubkeyhash';
+  if (isP2MS(script)) return 'multisig';
+  if (isP2PK(script)) return 'pubkey';
+  return 'nonstandard';
+}
+
+function range(n: number): number[] {
+  return [...Array(n).keys()];
 }

From 0f76aa935ad1effdb2904f36e6bb849128df1dd9 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 13:02:34 +0900
Subject: [PATCH 077/111] Refactor: Use varint from BIP174

---
 src/psbt.js    | 2 +-
 ts_src/psbt.ts | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 8e6f1f2..aa73949 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -1,6 +1,7 @@
 'use strict';
 Object.defineProperty(exports, '__esModule', { value: true });
 const bip174_1 = require('bip174');
+const varuint = require('bip174/src/lib/converter/varint');
 const utils_1 = require('bip174/src/lib/utils');
 const address_1 = require('./address');
 const bufferutils_1 = require('./bufferutils');
@@ -10,7 +11,6 @@ const networks_1 = require('./networks');
 const payments = require('./payments');
 const bscript = require('./script');
 const transaction_1 = require('./transaction');
-const varuint = require('varuint-bitcoin');
 const DEFAULT_OPTS = {
   network: networks_1.bitcoin,
   maximumFeeRate: 5000,
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 617cde9..a78b37b 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,4 +1,5 @@
 import { Psbt as PsbtBase } from 'bip174';
+import * as varuint from 'bip174/src/lib/converter/varint';
 import {
   NonWitnessUtxo,
   PartialSig,
@@ -19,7 +20,6 @@ import { bitcoin as btcNetwork, Network } from './networks';
 import * as payments from './payments';
 import * as bscript from './script';
 import { Output, Transaction } from './transaction';
-const varuint = require('varuint-bitcoin');
 
 const DEFAULT_OPTS: PsbtOpts = {
   network: btcNetwork,
@@ -946,7 +946,7 @@ function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
 
   function readVarInt(): number {
     const vi = varuint.decode(buffer, offset);
-    offset += varuint.decode.bytes;
+    offset += (varuint.decode as any).bytes;
     return vi;
   }
 

From ba33f0317f3d102be69ad269eb584a12ff7d5bcc Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 13:55:02 +0900
Subject: [PATCH 078/111] Add check for spending more than you have

---
 src/psbt.js    | 36 ++++++++++++++++--------------------
 ts_src/psbt.ts | 45 ++++++++++++++++++++-------------------------
 2 files changed, 36 insertions(+), 45 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index aa73949..4c80a31 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -167,8 +167,7 @@ class Psbt extends bip174_1.Psbt {
     }
     if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
     const tx = c.__TX.clone();
-    inputFinalizeGetAmts(this.inputs, tx, c, true, false);
-    c.__EXTRACTED_TX = tx;
+    inputFinalizeGetAmts(this.inputs, tx, c, true);
     return tx;
   }
   getFeeRate() {
@@ -184,18 +183,7 @@ class Psbt extends bip174_1.Psbt {
     } else {
       tx = c.__TX.clone();
     }
-    const inputAmount = inputFinalizeGetAmts(
-      this.inputs,
-      tx,
-      c,
-      mustFinalize,
-      true,
-    );
-    c.__EXTRACTED_TX = tx;
-    const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0);
-    const fee = inputAmount - outputAmount;
-    const bytes = tx.virtualSize();
-    c.__FEE_RATE = Math.floor(fee / bytes);
+    inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize);
     return c.__FEE_RATE;
   }
   finalizeAllInputs() {
@@ -830,7 +818,7 @@ function addNonWitnessTxCache(cache, input, inputIndex) {
     },
   });
 }
-function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) {
+function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) {
   let inputAmount = 0;
   inputs.forEach((input, idx) => {
     if (mustFinalize && input.finalScriptSig)
@@ -840,22 +828,30 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) {
         input.finalScriptWitness,
       );
     }
-    if (getAmounts && input.witnessUtxo) {
+    if (input.witnessUtxo) {
       inputAmount += input.witnessUtxo.value;
-    } else if (getAmounts && input.nonWitnessUtxo) {
+    } else if (input.nonWitnessUtxo) {
       const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx);
       const vout = tx.ins[idx].index;
       const out = nwTx.outs[vout];
       inputAmount += out.value;
     }
   });
-  return inputAmount;
+  const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0);
+  const fee = inputAmount - outputAmount;
+  if (fee < 0) {
+    throw new Error('Outputs are spending more than Inputs');
+  }
+  const bytes = tx.virtualSize();
+  cache.__EXTRACTED_TX = tx;
+  cache.__FEE_RATE = Math.floor(fee / bytes);
 }
 function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
-  if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+  const c = cache.__NON_WITNESS_UTXO_TX_CACHE;
+  if (!c[inputIndex]) {
     addNonWitnessTxCache(cache, input, inputIndex);
   }
-  return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+  return c[inputIndex];
 }
 function classifyScript(script) {
   if (isP2WPKH(script)) return 'witnesspubkeyhash';
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index a78b37b..2e4c826 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -212,8 +212,7 @@ export class Psbt extends PsbtBase {
     }
     if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
     const tx = c.__TX.clone();
-    inputFinalizeGetAmts(this.inputs, tx, c, true, false);
-    c.__EXTRACTED_TX = tx;
+    inputFinalizeGetAmts(this.inputs, tx, c, true);
     return tx;
   }
 
@@ -230,22 +229,8 @@ export class Psbt extends PsbtBase {
     } else {
       tx = c.__TX.clone();
     }
-    const inputAmount = inputFinalizeGetAmts(
-      this.inputs,
-      tx,
-      c,
-      mustFinalize,
-      true,
-    );
-    c.__EXTRACTED_TX = tx;
-    const outputAmount = (tx.outs as Output[]).reduce(
-      (total, o) => total + o.value,
-      0,
-    );
-    const fee = inputAmount - outputAmount;
-    const bytes = tx.virtualSize();
-    c.__FEE_RATE = Math.floor(fee / bytes);
-    return c.__FEE_RATE;
+    inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize);
+    return c.__FEE_RATE!;
   }
 
   finalizeAllInputs(): {
@@ -1031,8 +1016,7 @@ function inputFinalizeGetAmts(
   tx: Transaction,
   cache: PsbtCache,
   mustFinalize: boolean,
-  getAmounts: boolean,
-): number {
+): void {
   let inputAmount = 0;
   inputs.forEach((input, idx) => {
     if (mustFinalize && input.finalScriptSig)
@@ -1042,16 +1026,26 @@ function inputFinalizeGetAmts(
         input.finalScriptWitness,
       );
     }
-    if (getAmounts && input.witnessUtxo) {
+    if (input.witnessUtxo) {
       inputAmount += input.witnessUtxo.value;
-    } else if (getAmounts && input.nonWitnessUtxo) {
+    } else if (input.nonWitnessUtxo) {
       const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx);
       const vout = tx.ins[idx].index;
       const out = nwTx.outs[vout] as Output;
       inputAmount += out.value;
     }
   });
-  return inputAmount;
+  const outputAmount = (tx.outs as Output[]).reduce(
+    (total, o) => total + o.value,
+    0,
+  );
+  const fee = inputAmount - outputAmount;
+  if (fee < 0) {
+    throw new Error('Outputs are spending more than Inputs');
+  }
+  const bytes = tx.virtualSize();
+  cache.__EXTRACTED_TX = tx;
+  cache.__FEE_RATE = Math.floor(fee / bytes);
 }
 
 function nonWitnessUtxoTxFromCache(
@@ -1059,10 +1053,11 @@ function nonWitnessUtxoTxFromCache(
   input: PsbtInput,
   inputIndex: number,
 ): Transaction {
-  if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) {
+  const c = cache.__NON_WITNESS_UTXO_TX_CACHE;
+  if (!c[inputIndex]) {
     addNonWitnessTxCache(cache, input, inputIndex);
   }
-  return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex];
+  return c[inputIndex];
 }
 
 function classifyScript(script: Buffer): string {

From b8c341dea0be5a8cd3feb313a71b697b31a99b4a Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 15:45:56 +0900
Subject: [PATCH 079/111] Finalize should chain this as well.

---
 src/psbt.js                           | 30 ++++++++++++---------
 test/integration/transactions-psbt.js | 23 ++++++++--------
 ts_src/psbt.ts                        | 38 ++++++++++++++-------------
 types/psbt.d.ts                       |  8 +++---
 4 files changed, 51 insertions(+), 48 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 4c80a31..58a56dd 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -187,14 +187,9 @@ class Psbt extends bip174_1.Psbt {
     return c.__FEE_RATE;
   }
   finalizeAllInputs() {
-    const inputResults = range(this.inputs.length).map(idx =>
-      this.finalizeInput(idx),
-    );
-    const result = inputResults.every(val => val === true);
-    return {
-      result,
-      inputResults,
-    };
+    utils_1.checkForInput(this.inputs, 0); // making sure we have at least one
+    range(this.inputs.length).forEach(idx => this.finalizeInput(idx));
+    return this;
   }
   finalizeInput(inputIndex) {
     const input = utils_1.checkForInput(this.inputs, inputIndex);
@@ -203,9 +198,10 @@ class Psbt extends bip174_1.Psbt {
       input,
       this.__CACHE,
     );
-    if (!script) return false;
+    if (!script) throw new Error(`No script found for input #${inputIndex}`);
     const scriptType = classifyScript(script);
-    if (!canFinalize(input, script, scriptType)) return false;
+    if (!canFinalize(input, script, scriptType))
+      throw new Error(`Can not finalize input #${inputIndex}`);
     const { finalScriptSig, finalScriptWitness } = getFinalScripts(
       script,
       scriptType,
@@ -218,9 +214,17 @@ class Psbt extends bip174_1.Psbt {
       this.addFinalScriptSigToInput(inputIndex, finalScriptSig);
     if (finalScriptWitness)
       this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
-    if (!finalScriptSig && !finalScriptWitness) return false;
+    if (!finalScriptSig && !finalScriptWitness)
+      throw new Error(`Unknown error finalizing input #${inputIndex}`);
     this.clearFinalizedInput(inputIndex);
-    return true;
+    return this;
+  }
+  validateAllSignatures() {
+    utils_1.checkForInput(this.inputs, 0); // making sure we have at least one
+    const results = range(this.inputs.length).map(idx =>
+      this.validateSignatures(idx),
+    );
+    return results.reduce((final, res) => res === true && final, true);
   }
   validateSignatures(inputIndex, pubkey) {
     const input = this.inputs[inputIndex];
@@ -261,7 +265,7 @@ class Psbt extends bip174_1.Psbt {
     // as input information is added, then eventually
     // optimize this method.
     const results = [];
-    for (const [i] of this.inputs.entries()) {
+    for (const i of range(this.inputs.length)) {
       try {
         this.signInput(i, keyPair);
         results.push(true);
diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js
index 37348c1..a7398a8 100644
--- a/test/integration/transactions-psbt.js
+++ b/test/integration/transactions-psbt.js
@@ -225,26 +225,25 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       } = inputData
       assert.deepStrictEqual({ hash, index, witnessUtxo, redeemScript }, inputData)
     }
+    const keyPair = p2sh.keys[0]
+    const outputData = {
+      script: p2sh.payment.output, // sending to myself for fun
+      value: 2e4
+    }
 
-    const psbt = new bitcoin.Psbt({ network: regtest })
+    const tx = new bitcoin.Psbt()
       .addInput(inputData)
-      .addOutput({
-        address: regtestUtils.RANDOM_ADDRESS,
-        value: 2e4
-      })
-      .signInput(0, p2sh.keys[0])
-
-    assert.strictEqual(psbt.validateSignatures(0), true)
-    psbt.finalizeAllInputs()
-
-    const tx = psbt.extractTransaction()
+      .addOutput(outputData)
+      .sign(keyPair)
+      .finalizeAllInputs()
+      .extractTransaction()
 
     // build and broadcast to the Bitcoin RegTest network
     await regtestUtils.broadcast(tx.toHex())
 
     await regtestUtils.verify({
       txId: tx.getId(),
-      address: regtestUtils.RANDOM_ADDRESS,
+      address: p2sh.payment.address,
       vout: 0,
       value: 2e4
     })
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 2e4c826..61ed15e 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -233,31 +233,24 @@ export class Psbt extends PsbtBase {
     return c.__FEE_RATE!;
   }
 
-  finalizeAllInputs(): {
-    result: boolean;
-    inputResults: boolean[];
-  } {
-    const inputResults = range(this.inputs.length).map(idx =>
-      this.finalizeInput(idx),
-    );
-    const result = inputResults.every(val => val === true);
-    return {
-      result,
-      inputResults,
-    };
+  finalizeAllInputs(): this {
+    checkForInput(this.inputs, 0); // making sure we have at least one
+    range(this.inputs.length).forEach(idx => this.finalizeInput(idx));
+    return this;
   }
 
-  finalizeInput(inputIndex: number): boolean {
+  finalizeInput(inputIndex: number): this {
     const input = checkForInput(this.inputs, inputIndex);
     const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
       this.__CACHE,
     );
-    if (!script) return false;
+    if (!script) throw new Error(`No script found for input #${inputIndex}`);
 
     const scriptType = classifyScript(script);
-    if (!canFinalize(input, script, scriptType)) return false;
+    if (!canFinalize(input, script, scriptType))
+      throw new Error(`Can not finalize input #${inputIndex}`);
 
     const { finalScriptSig, finalScriptWitness } = getFinalScripts(
       script,
@@ -272,10 +265,19 @@ export class Psbt extends PsbtBase {
       this.addFinalScriptSigToInput(inputIndex, finalScriptSig);
     if (finalScriptWitness)
       this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
-    if (!finalScriptSig && !finalScriptWitness) return false;
+    if (!finalScriptSig && !finalScriptWitness)
+      throw new Error(`Unknown error finalizing input #${inputIndex}`);
 
     this.clearFinalizedInput(inputIndex);
-    return true;
+    return this;
+  }
+
+  validateAllSignatures(): boolean {
+    checkForInput(this.inputs, 0); // making sure we have at least one
+    const results = range(this.inputs.length).map(idx =>
+      this.validateSignatures(idx),
+    );
+    return results.reduce((final, res) => res === true && final, true);
   }
 
   validateSignatures(inputIndex: number, pubkey?: Buffer): boolean {
@@ -319,7 +321,7 @@ export class Psbt extends PsbtBase {
     // as input information is added, then eventually
     // optimize this method.
     const results: boolean[] = [];
-    for (const [i] of this.inputs.entries()) {
+    for (const i of range(this.inputs.length)) {
       try {
         this.signInput(i, keyPair);
         results.push(true);
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 775f3b0..40571fa 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -20,11 +20,9 @@ export declare class Psbt extends PsbtBase {
     addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this;
     extractTransaction(disableFeeCheck?: boolean): Transaction;
     getFeeRate(): number;
-    finalizeAllInputs(): {
-        result: boolean;
-        inputResults: boolean[];
-    };
-    finalizeInput(inputIndex: number): boolean;
+    finalizeAllInputs(): this;
+    finalizeInput(inputIndex: number): this;
+    validateAllSignatures(): boolean;
     validateSignatures(inputIndex: number, pubkey?: Buffer): boolean;
     sign(keyPair: Signer): this;
     signAsync(keyPair: SignerAsync): Promise<void>;

From 01c7ac39b611e63cfff38d5292b295fe15e65dd3 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Tue, 9 Jul 2019 18:03:15 +0900
Subject: [PATCH 080/111] Add clone, addInputs, addOutputs

---
 src/psbt.js                           |  14 ++
 test/integration/transactions-psbt.js | 339 ++++++++++++++------------
 ts_src/psbt.ts                        |  17 ++
 types/psbt.d.ts                       |   3 +
 4 files changed, 221 insertions(+), 152 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 58a56dd..33a32a3 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -95,6 +95,12 @@ class Psbt extends bip174_1.Psbt {
   get inputCount() {
     return this.inputs.length;
   }
+  clone() {
+    // TODO: more efficient cloning
+    const res = Psbt.fromBuffer(this.toBuffer());
+    res.opts = JSON.parse(JSON.stringify(this.opts));
+    return res;
+  }
   setMaximumFeeRate(satoshiPerByte) {
     check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
     this.opts.maximumFeeRate = satoshiPerByte;
@@ -129,6 +135,10 @@ class Psbt extends bip174_1.Psbt {
     c.__EXTRACTED_TX = undefined;
     return this;
   }
+  addInputs(inputDatas) {
+    inputDatas.forEach(inputData => this.addInput(inputData));
+    return this;
+  }
   addInput(inputData) {
     checkInputsForPartialSig(this.inputs, 'addInput');
     const c = this.__CACHE;
@@ -138,6 +148,10 @@ class Psbt extends bip174_1.Psbt {
     c.__EXTRACTED_TX = undefined;
     return this;
   }
+  addOutputs(outputDatas) {
+    outputDatas.forEach(outputData => this.addOutput(outputData));
+    return this;
+  }
   addOutput(outputData) {
     checkInputsForPartialSig(this.inputs, 'addOutput');
     const { address } = outputData;
diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js
index a7398a8..96ae073 100644
--- a/test/integration/transactions-psbt.js
+++ b/test/integration/transactions-psbt.js
@@ -1,17 +1,19 @@
-const { describe, it } = require('mocha')
-const assert = require('assert')
-const bitcoin = require('../../')
-const regtestUtils = require('./_regtest')
-const regtest = regtestUtils.network
+const { describe, it } = require('mocha');
+const assert = require('assert');
+const bitcoin = require('../../');
+const regtestUtils = require('./_regtest');
+const regtest = regtestUtils.network;
 
 // See bottom of file for some helper functions used to make the payment objects needed.
 
 describe('bitcoinjs-lib (transactions with psbt)', () => {
   it('can create a 1-to-1 Transaction', () => {
-    const alice = bitcoin.ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr')
-    const psbt = new bitcoin.Psbt()
-    psbt.setVersion(2) // These are defaults. This line is not needed.
-    psbt.setLocktime(0) // These are defaults. This line is not needed.
+    const alice = bitcoin.ECPair.fromWIF(
+      'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr',
+    );
+    const psbt = new bitcoin.Psbt();
+    psbt.setVersion(2); // These are defaults. This line is not needed.
+    psbt.setLocktime(0); // These are defaults. This line is not needed.
     psbt.addInput({
       // if hash is string, txid, if hash is Buffer, is reversed compared to txid
       hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e',
@@ -21,18 +23,18 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       // non-segwit inputs now require passing the whole previous tx as Buffer
       nonWitnessUtxo: Buffer.from(
         '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' +
-        '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' +
-        'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' +
-        '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' +
-        '631e5e1e66009ce3710ceea5b1ad13ffffffff01' +
-        // value in satoshis (Int64LE) = 0x015f90 = 90000
-        '905f010000000000' +
-        // scriptPubkey length
-        '19' +
-        // scriptPubkey
-        '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' +
-        // locktime
-        '00000000',
+          '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' +
+          'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' +
+          '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' +
+          '631e5e1e66009ce3710ceea5b1ad13ffffffff01' +
+          // value in satoshis (Int64LE) = 0x015f90 = 90000
+          '905f010000000000' +
+          // scriptPubkey length
+          '19' +
+          // scriptPubkey
+          '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' +
+          // locktime
+          '00000000',
         'hex',
       ),
 
@@ -47,40 +49,50 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       // },
 
       // Not featured here: redeemScript. A Buffer of the redeemScript
-    })
+    });
     psbt.addOutput({
       address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp',
-      value: 80000
-    })
-    psbt.signInput(0, alice)
-    psbt.validateSignatures(0)
-    psbt.finalizeAllInputs()
+      value: 80000,
+    });
+    psbt.signInput(0, alice);
+    psbt.validateSignatures(0);
+    psbt.finalizeAllInputs();
     assert.strictEqual(
       psbt.extractTransaction().toHex(),
       '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' +
-      'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' +
-      'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' +
-      '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' +
-      'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' +
-      '08a22724efa6f6a07b0ec4c79aa88ac00000000',
-    )
-  })
+        'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' +
+        'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' +
+        '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' +
+        'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' +
+        '08a22724efa6f6a07b0ec4c79aa88ac00000000',
+    );
+  });
 
   it('can create (and broadcast via 3PBP) a typical Transaction', async () => {
     // these are { payment: Payment; keys: ECPair[] }
-    const alice1 = createPayment('p2pkh')
-    const alice2 = createPayment('p2pkh')
+    const alice1 = createPayment('p2pkh');
+    const alice2 = createPayment('p2pkh');
 
     // give Alice 2 unspent outputs
-    const inputData1 = await getInputData(5e4, alice1.payment, false, 'noredeem')
-    const inputData2 = await getInputData(7e4, alice2.payment, false, 'noredeem')
+    const inputData1 = await getInputData(
+      5e4,
+      alice1.payment,
+      false,
+      'noredeem',
+    );
+    const inputData2 = await getInputData(
+      7e4,
+      alice2.payment,
+      false,
+      'noredeem',
+    );
     {
       const {
         hash, // string of txid or Buffer of tx hash. (txid and hash are reverse order)
         index, // the output index of the txo you are spending
         nonWitnessUtxo, // the full previous transaction as a Buffer
-      } = inputData1
-      assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData1)
+      } = inputData1;
+      assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData1);
     }
 
     // network is only needed if you pass an address to addOutput
@@ -90,12 +102,12 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       .addInput(inputData2) // alice2 unspent
       .addOutput({
         address: 'mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf',
-        value: 8e4
+        value: 8e4,
       }) // the actual "spend"
       .addOutput({
         address: alice2.payment.address, // OR script, which is a Buffer.
-        value: 1e4
-      }) // Alice's change
+        value: 1e4,
+      }); // Alice's change
     // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee
 
     // Let's show a new feature with PSBT.
@@ -103,231 +115,248 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
     // (this is not necessary, but a nice feature)
 
     // encode to send out to the signers
-    const psbtBaseText = psbt.toBase64()
+    const psbtBaseText = psbt.toBase64();
 
     // each signer imports
-    const signer1 = bitcoin.Psbt.fromBase64(psbtBaseText)
-    const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText)
+    const signer1 = bitcoin.Psbt.fromBase64(psbtBaseText);
+    const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText);
 
     // Alice signs each input with the respective private keys
     // signInput and signInputAsync are better
     // (They take the input index explicitly as the first arg)
-    signer1.sign(alice1.keys[0])
-    signer2.sign(alice2.keys[0])
+    signer1.sign(alice1.keys[0]);
+    signer2.sign(alice2.keys[0]);
 
     // If your signer object's sign method returns a promise, use the following
     // await signer2.signAsync(alice2.keys[0])
 
     // encode to send back to combiner (signer 1 and 2 are not near each other)
-    const s1text = signer1.toBase64()
-    const s2text = signer2.toBase64()
+    const s1text = signer1.toBase64();
+    const s2text = signer2.toBase64();
 
-    const final1 = bitcoin.Psbt.fromBase64(s1text)
-    const final2 = bitcoin.Psbt.fromBase64(s2text)
+    const final1 = bitcoin.Psbt.fromBase64(s1text);
+    const final2 = bitcoin.Psbt.fromBase64(s2text);
 
     // final1.combine(final2) would give the exact same result
-    psbt.combine(final1, final2)
+    psbt.combine(final1, final2);
 
     // Finalizer wants to check all signatures are valid before finalizing.
     // If the finalizer wants to check for specific pubkeys, the second arg
     // can be passed. See the first multisig example below.
-    assert.strictEqual(psbt.validateSignatures(0), true)
-    assert.strictEqual(psbt.validateSignatures(1), true)
+    assert.strictEqual(psbt.validateSignatures(0), true);
+    assert.strictEqual(psbt.validateSignatures(1), true);
 
     // This step it new. Since we separate the signing operation and
     // the creation of the scriptSig and witness stack, we are able to
-    psbt.finalizeAllInputs()
+    psbt.finalizeAllInputs();
     // it returns an array of the success of each input, also a result attribute
     // which is true if all array items are true.
 
     // build and broadcast our RegTest network
-    await regtestUtils.broadcast(psbt.extractTransaction().toHex())
+    await regtestUtils.broadcast(psbt.extractTransaction().toHex());
     // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839
-  })
+  });
 
   it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => {
-    const alice1 = createPayment('p2pkh')
-    const inputData1 = await getInputData(2e5, alice1.payment, false, 'noredeem')
+    const alice1 = createPayment('p2pkh');
+    const inputData1 = await getInputData(
+      2e5,
+      alice1.payment,
+      false,
+      'noredeem',
+    );
 
-    const data = Buffer.from('bitcoinjs-lib', 'utf8')
-    const embed = bitcoin.payments.embed({ data: [data] })
+    const data = Buffer.from('bitcoinjs-lib', 'utf8');
+    const embed = bitcoin.payments.embed({ data: [data] });
 
     const psbt = new bitcoin.Psbt({ network: regtest })
       .addInput(inputData1)
       .addOutput({
         script: embed.output,
-        value: 1000
+        value: 1000,
       })
       .addOutput({
         address: regtestUtils.RANDOM_ADDRESS,
-        value: 1e5
+        value: 1e5,
       })
-      .signInput(0, alice1.keys[0])
+      .signInput(0, alice1.keys[0]);
 
-    assert.strictEqual(psbt.validateSignatures(0), true)
-    psbt.finalizeAllInputs()
+    assert.strictEqual(psbt.validateSignatures(0), true);
+    psbt.finalizeAllInputs();
 
     // build and broadcast to the RegTest network
-    await regtestUtils.broadcast(psbt.extractTransaction().toHex())
-  })
+    await regtestUtils.broadcast(psbt.extractTransaction().toHex());
+  });
 
   it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => {
-    const multisig = createPayment('p2sh-p2ms(2 of 4)')
-    const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh')
+    const multisig = createPayment('p2sh-p2ms(2 of 4)');
+    const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh');
     {
       const {
         hash,
         index,
         nonWitnessUtxo,
         redeemScript, // NEW: P2SH needs to give redeemScript when adding an input.
-      } = inputData1
-      assert.deepStrictEqual({ hash, index, nonWitnessUtxo, redeemScript }, inputData1)
+      } = inputData1;
+      assert.deepStrictEqual(
+        { hash, index, nonWitnessUtxo, redeemScript },
+        inputData1,
+      );
     }
 
     const psbt = new bitcoin.Psbt({ network: regtest })
       .addInput(inputData1)
       .addOutput({
         address: regtestUtils.RANDOM_ADDRESS,
-        value: 1e4
+        value: 1e4,
       })
       .signInput(0, multisig.keys[0])
-      .signInput(0, multisig.keys[2])
+      .signInput(0, multisig.keys[2]);
 
-    assert.strictEqual(psbt.validateSignatures(0), true)
-    assert.strictEqual(psbt.validateSignatures(0, multisig.keys[0].publicKey), true)
+    assert.strictEqual(psbt.validateSignatures(0), true);
+    assert.strictEqual(
+      psbt.validateSignatures(0, multisig.keys[0].publicKey),
+      true,
+    );
     assert.throws(() => {
-      psbt.validateSignatures(0, multisig.keys[3].publicKey)
-    }, new RegExp('No signatures for this pubkey'))
-    psbt.finalizeAllInputs()
+      psbt.validateSignatures(0, multisig.keys[3].publicKey);
+    }, new RegExp('No signatures for this pubkey'));
+    psbt.finalizeAllInputs();
 
-    const tx = psbt.extractTransaction()
+    const tx = psbt.extractTransaction();
 
     // build and broadcast to the Bitcoin RegTest network
-    await regtestUtils.broadcast(tx.toHex())
+    await regtestUtils.broadcast(tx.toHex());
 
     await regtestUtils.verify({
       txId: tx.getId(),
       address: regtestUtils.RANDOM_ADDRESS,
       vout: 0,
-      value: 1e4
-    })
-  })
+      value: 1e4,
+    });
+  });
 
   it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => {
-    const p2sh = createPayment('p2sh-p2wpkh')
-    const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh')
+    const p2sh = createPayment('p2sh-p2wpkh');
+    const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh');
+    const inputData2 = await getInputData(5e4, p2sh.payment, true, 'p2sh');
     {
       const {
         hash,
         index,
         witnessUtxo, // NEW: this is an object of the output being spent { script: Buffer; value: Satoshis; }
         redeemScript,
-      } = inputData
-      assert.deepStrictEqual({ hash, index, witnessUtxo, redeemScript }, inputData)
+      } = inputData;
+      assert.deepStrictEqual(
+        { hash, index, witnessUtxo, redeemScript },
+        inputData,
+      );
     }
-    const keyPair = p2sh.keys[0]
+    const keyPair = p2sh.keys[0];
     const outputData = {
       script: p2sh.payment.output, // sending to myself for fun
-      value: 2e4
-    }
+      value: 2e4,
+    };
+    const outputData2 = {
+      script: p2sh.payment.output, // sending to myself for fun
+      value: 7e4,
+    };
 
     const tx = new bitcoin.Psbt()
-      .addInput(inputData)
-      .addOutput(outputData)
+      .addInputs([inputData, inputData2])
+      .addOutputs([outputData, outputData2])
       .sign(keyPair)
       .finalizeAllInputs()
-      .extractTransaction()
+      .extractTransaction();
 
     // build and broadcast to the Bitcoin RegTest network
-    await regtestUtils.broadcast(tx.toHex())
+    await regtestUtils.broadcast(tx.toHex());
 
     await regtestUtils.verify({
       txId: tx.getId(),
       address: p2sh.payment.address,
       vout: 0,
-      value: 2e4
-    })
-  })
+      value: 2e4,
+    });
+  });
 
   it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => {
-
     // the only thing that changes is you don't give a redeemscript for input data
 
-    const p2wpkh = createPayment('p2wpkh')
-    const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem')
+    const p2wpkh = createPayment('p2wpkh');
+    const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem');
     {
-      const {
-        hash,
-        index,
-        witnessUtxo,
-      } = inputData
-      assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData)
+      const { hash, index, witnessUtxo } = inputData;
+      assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData);
     }
 
     const psbt = new bitcoin.Psbt({ network: regtest })
       .addInput(inputData)
       .addOutput({
         address: regtestUtils.RANDOM_ADDRESS,
-        value: 2e4
+        value: 2e4,
       })
-      .signInput(0, p2wpkh.keys[0])
+      .signInput(0, p2wpkh.keys[0]);
 
-    assert.strictEqual(psbt.validateSignatures(0), true)
-    psbt.finalizeAllInputs()
+    assert.strictEqual(psbt.validateSignatures(0), true);
+    psbt.finalizeAllInputs();
 
-    const tx = psbt.extractTransaction()
+    const tx = psbt.extractTransaction();
 
     // build and broadcast to the Bitcoin RegTest network
-    await regtestUtils.broadcast(tx.toHex())
+    await regtestUtils.broadcast(tx.toHex());
 
     await regtestUtils.verify({
       txId: tx.getId(),
       address: regtestUtils.RANDOM_ADDRESS,
       vout: 0,
-      value: 2e4
-    })
-  })
+      value: 2e4,
+    });
+  });
 
   it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => {
-    const p2wsh = createPayment('p2wsh-p2pk')
-    const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh')
+    const p2wsh = createPayment('p2wsh-p2pk');
+    const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh');
     {
       const {
         hash,
         index,
         witnessUtxo,
         witnessScript, // NEW: A Buffer of the witnessScript
-      } = inputData
-      assert.deepStrictEqual({ hash, index, witnessUtxo, witnessScript }, inputData)
+      } = inputData;
+      assert.deepStrictEqual(
+        { hash, index, witnessUtxo, witnessScript },
+        inputData,
+      );
     }
 
     const psbt = new bitcoin.Psbt({ network: regtest })
       .addInput(inputData)
       .addOutput({
         address: regtestUtils.RANDOM_ADDRESS,
-        value: 2e4
+        value: 2e4,
       })
-      .signInput(0, p2wsh.keys[0])
+      .signInput(0, p2wsh.keys[0]);
 
-    assert.strictEqual(psbt.validateSignatures(0), true)
-    psbt.finalizeAllInputs()
+    assert.strictEqual(psbt.validateSignatures(0), true);
+    psbt.finalizeAllInputs();
 
-    const tx = psbt.extractTransaction()
+    const tx = psbt.extractTransaction();
 
     // build and broadcast to the Bitcoin RegTest network
-    await regtestUtils.broadcast(tx.toHex())
+    await regtestUtils.broadcast(tx.toHex());
 
     await regtestUtils.verify({
       txId: tx.getId(),
       address: regtestUtils.RANDOM_ADDRESS,
       vout: 0,
-      value: 2e4
-    })
-  })
+      value: 2e4,
+    });
+  });
 
   it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => {
-    const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)')
-    const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh')
+    const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)');
+    const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh');
     {
       const {
         hash,
@@ -335,54 +364,60 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
         witnessUtxo,
         redeemScript,
         witnessScript,
-      } = inputData
-      assert.deepStrictEqual({ hash, index, witnessUtxo, redeemScript, witnessScript }, inputData)
+      } = inputData;
+      assert.deepStrictEqual(
+        { hash, index, witnessUtxo, redeemScript, witnessScript },
+        inputData,
+      );
     }
 
     const psbt = new bitcoin.Psbt({ network: regtest })
       .addInput(inputData)
       .addOutput({
         address: regtestUtils.RANDOM_ADDRESS,
-        value: 2e4
+        value: 2e4,
       })
       .signInput(0, p2sh.keys[0])
       .signInput(0, p2sh.keys[2])
-      .signInput(0, p2sh.keys[3])
+      .signInput(0, p2sh.keys[3]);
 
-    assert.strictEqual(psbt.validateSignatures(0), true)
-    assert.strictEqual(psbt.validateSignatures(0, p2sh.keys[3].publicKey), true)
+    assert.strictEqual(psbt.validateSignatures(0), true);
+    assert.strictEqual(
+      psbt.validateSignatures(0, p2sh.keys[3].publicKey),
+      true,
+    );
     assert.throws(() => {
-      psbt.validateSignatures(0, p2sh.keys[1].publicKey)
-    }, new RegExp('No signatures for this pubkey'))
-    psbt.finalizeAllInputs()
+      psbt.validateSignatures(0, p2sh.keys[1].publicKey);
+    }, new RegExp('No signatures for this pubkey'));
+    psbt.finalizeAllInputs();
 
-    const tx = psbt.extractTransaction()
+    const tx = psbt.extractTransaction();
 
     // build and broadcast to the Bitcoin RegTest network
-    await regtestUtils.broadcast(tx.toHex())
+    await regtestUtils.broadcast(tx.toHex());
 
     await regtestUtils.verify({
       txId: tx.getId(),
       address: regtestUtils.RANDOM_ADDRESS,
       vout: 0,
-      value: 2e4
-    })
-  })
-})
+      value: 2e4,
+    });
+  });
+});
 
 function createPayment(_type, network) {
-  network = network || regtest
+  network = network || regtest;
   const splitType = _type.split('-').reverse();
   const isMultisig = splitType[0].slice(0, 4) === 'p2ms';
   const keys = [];
   let m;
   if (isMultisig) {
-    const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/)
-    m = parseInt(match[1])
-    let n = parseInt(match[2])
+    const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/);
+    m = parseInt(match[1]);
+    let n = parseInt(match[2]);
     while (n > 1) {
       keys.push(bitcoin.ECPair.makeRandom({ network }));
-      n--
+      n--;
     }
   }
   keys.push(bitcoin.ECPair.makeRandom({ network }));
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 61ed15e..bfe340c 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -130,6 +130,13 @@ export class Psbt extends PsbtBase {
     return this.inputs.length;
   }
 
+  clone(): Psbt {
+    // TODO: more efficient cloning
+    const res = Psbt.fromBuffer(this.toBuffer());
+    res.opts = JSON.parse(JSON.stringify(this.opts));
+    return res;
+  }
+
   setMaximumFeeRate(satoshiPerByte: number): void {
     check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
     this.opts.maximumFeeRate = satoshiPerByte;
@@ -168,6 +175,11 @@ export class Psbt extends PsbtBase {
     return this;
   }
 
+  addInputs(inputDatas: TransactionInput[]): this {
+    inputDatas.forEach(inputData => this.addInput(inputData));
+    return this;
+  }
+
   addInput(inputData: TransactionInput): this {
     checkInputsForPartialSig(this.inputs, 'addInput');
     const c = this.__CACHE;
@@ -178,6 +190,11 @@ export class Psbt extends PsbtBase {
     return this;
   }
 
+  addOutputs(outputDatas: TransactionOutput[]): this {
+    outputDatas.forEach(outputData => this.addOutput(outputData));
+    return this;
+  }
+
   addOutput(outputData: TransactionOutput): this {
     checkInputsForPartialSig(this.inputs, 'addOutput');
     const { address } = outputData as any;
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 40571fa..10617c2 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -11,11 +11,14 @@ export declare class Psbt extends PsbtBase {
     private opts;
     constructor(opts?: PsbtOptsOptional);
     readonly inputCount: number;
+    clone(): Psbt;
     setMaximumFeeRate(satoshiPerByte: number): void;
     setVersion(version: number): this;
     setLocktime(locktime: number): this;
     setSequence(inputIndex: number, sequence: number): this;
+    addInputs(inputDatas: TransactionInput[]): this;
     addInput(inputData: TransactionInput): this;
+    addOutputs(outputDatas: TransactionOutput[]): this;
     addOutput(outputData: TransactionOutput): this;
     addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this;
     extractTransaction(disableFeeCheck?: boolean): Transaction;

From 75f5e8f03c5871c6d80a8f4c4e09ea00da8875d3 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Tue, 9 Jul 2019 14:35:40 +0700
Subject: [PATCH 081/111] Use Buffer notation in JSON

---
 test/fixtures/psbt.json | 36 ++++++++++++++++----------------
 test/psbt.js            | 46 ++++++++++++++---------------------------
 2 files changed, 34 insertions(+), 48 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 59a74cd..0525bf6 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -174,37 +174,37 @@
         "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=",
         "inputData": [
           {
-            "nonWitnessUtxo": "0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000",
-            "redeemScript": "5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae",
+            "nonWitnessUtxo": "Buffer.from('0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000', 'hex')",
+            "redeemScript": "Buffer.from('5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae', 'hex')",
             "bip32Derivation": [
               {
-                "masterFingerprint": "d90c6a4f",
-                "pubkey": "029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f",
+                "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')",
+                "pubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f', 'hex')",
                 "path": "m/0'/0'/0'"
               },
               {
-                "masterFingerprint": "d90c6a4f",
-                "pubkey": "02dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7",
+                "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')",
+                "pubkey": "Buffer.from('02dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7', 'hex')",
                 "path": "m/0'/0'/1'"
               }
             ]
           },
           {
             "witnessUtxo": {
-              "script": "a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887",
+              "script": "Buffer.from('a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887', 'hex')",
               "value": 200000000
             },
-            "redeemScript": "00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903",
-            "witnessScript": "522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae",
+            "redeemScript": "Buffer.from('00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903', 'hex')",
+            "witnessScript": "Buffer.from('522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae', 'hex')",
             "bip32Derivation": [
               {
-                "masterFingerprint": "d90c6a4f",
-                "pubkey": "023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73",
+                "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')",
+                "pubkey": "Buffer.from('023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73', 'hex')",
                 "path": "m/0'/0'/3'"
               },
               {
-                "masterFingerprint": "d90c6a4f",
-                "pubkey": "03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc",
+                "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')",
+                "pubkey": "Buffer.from('03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc', 'hex')",
                 "path": "m/0'/0'/2'"
               }
             ]
@@ -214,8 +214,8 @@
           {
             "bip32Derivation": [
               {
-                "masterFingerprint": "d90c6a4f",
-                "pubkey": "03a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58771",
+                "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')",
+                "pubkey": "Buffer.from('03a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58771', 'hex')",
                 "path": "m/0'/0'/4'"
               }
             ]
@@ -223,8 +223,8 @@
           {
             "bip32Derivation": [
               {
-                "masterFingerprint": "d90c6a4f",
-                "pubkey": "027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b50051096",
+                "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')",
+                "pubkey": "Buffer.from('027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b50051096', 'hex')",
                 "path": "m/0'/0'/5'"
               }
             ]
@@ -316,7 +316,7 @@
       {
         "description": "checks for hash and index",
         "inputData": {
-          "hash": "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f",
+          "hash": "Buffer.from('000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f', 'hex')",
           "index": 2
         },
         "equals": "cHNidP8BADMCAAAAAQABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4PAgAAAAD/////AAAAAAAAAAA="
diff --git a/test/psbt.js b/test/psbt.js
index 657a67a..25bbfa9 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -5,37 +5,23 @@ const ECPair = require('../src/ecpair')
 const Psbt = require('..').Psbt
 const NETWORKS = require('../src/networks')
 
-const fixtures = require('./fixtures/psbt')
+const initBuffers = object => JSON.parse(JSON.stringify(object), (key, value) => {
+  const regex = new RegExp(/^Buffer.from\(['"](.*)['"], ['"](.*)['"]\)$/)
+  const result = regex.exec(value)
+  if (!result) return value
+
+  const data = result[1]
+  const encoding = result[2]
+
+  return Buffer.from(data, encoding)
+})
+
+const fixtures = initBuffers(require('./fixtures/psbt'))
 
 const upperCaseFirstLetter = str => str.replace(/^./, s => s.toUpperCase())
 
 const b = hex => Buffer.from(hex, 'hex');
 
-const initBuffers = (attr, data) => {
-  if ([
-    'nonWitnessUtxo',
-    'redeemScript',
-    'witnessScript'
-  ].includes(attr)) {
-    data = b(data)
-  } else if (attr === 'bip32Derivation') {
-    data.masterFingerprint = b(data.masterFingerprint)
-    data.pubkey = b(data.pubkey)
-  } else if (attr === 'witnessUtxo') {
-    data.script = b(data.script)
-  } else if (attr === 'hash') {
-    if (
-      typeof data === 'string' &&
-      data.match(/^[0-9a-f]*$/i) &&
-      data.length % 2 === 0
-    ) {
-      data = b(data)
-    }
-  }
-
-  return data
-};
-
 describe(`Psbt`, () => {
   describe('BIP174 Test Vectors', () => {
     fixtures.bip174.invalid.forEach(f => {
@@ -94,9 +80,9 @@ describe(`Psbt`, () => {
                   adder = adder.bind(psbt)
                   const arg = data[attr]
                   if (Array.isArray(arg)) {
-                    arg.forEach(a => adder(i, initBuffers(attr, a)))
+                    arg.forEach(a => adder(i, a))
                   } else {
-                    adder(i, initBuffers(attr, arg))
+                    adder(i, arg)
                     if (attr === 'nonWitnessUtxo') {
                       const first = psbt.inputs[i].nonWitnessUtxo
                       psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[i] = undefined
@@ -320,7 +306,7 @@ describe(`Psbt`, () => {
   describe('addInput', () => {
     fixtures.addInput.checks.forEach(f => {
       for (const attr of Object.keys(f.inputData)) {
-        f.inputData[attr] = initBuffers(attr, f.inputData[attr])
+        f.inputData[attr] = f.inputData[attr]
       }
       it(f.description, () => {
         const psbt = new Psbt()
@@ -349,7 +335,7 @@ describe(`Psbt`, () => {
   describe('addOutput', () => {
     fixtures.addOutput.checks.forEach(f => {
       for (const attr of Object.keys(f.outputData)) {
-        f.outputData[attr] = initBuffers(attr, f.outputData[attr])
+        f.outputData[attr] = f.outputData[attr]
       }
       it(f.description, () => {
         const psbt = new Psbt()

From 0d9fa87943e23013aa9d76162afc36ed4a676435 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Tue, 9 Jul 2019 16:21:00 +0700
Subject: [PATCH 082/111] Move nonWitnessUtxo cache tests out into own test

---
 test/fixtures/psbt.json |  9 ++++++++-
 test/psbt.js            | 33 +++++++++++++++++++++++----------
 2 files changed, 31 insertions(+), 11 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 0525bf6..9e85b63 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -428,5 +428,12 @@
       "transaction": "020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000",
       "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA="
     }
-  ]
+  ],
+  "cache": {
+    "nonWitnessUtxo": {
+      "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuIgYClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8Q2QxqTwAAAIAAAACAAAAAgCIGAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXENkMak8AAACAAAAAgAEAAIAAAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
+      "nonWitnessUtxo": "Buffer.from('0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000', 'hex')",
+      "inputIndex": 0
+    }
+  }
 }
diff --git a/test/psbt.js b/test/psbt.js
index 25bbfa9..ce92dc6 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -83,16 +83,6 @@ describe(`Psbt`, () => {
                     arg.forEach(a => adder(i, a))
                   } else {
                     adder(i, arg)
-                    if (attr === 'nonWitnessUtxo') {
-                      const first = psbt.inputs[i].nonWitnessUtxo
-                      psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[i] = undefined
-                      const second = psbt.inputs[i].nonWitnessUtxo
-                      psbt.inputs[i].nonWitnessUtxo = Buffer.from([1,2,3])
-                      psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[i] = undefined
-                      const third = psbt.inputs[i].nonWitnessUtxo
-                      assert.ok(first.equals(second))
-                      assert.ok(first.equals(third))
-                    }
                   }
                 }
               }
@@ -473,4 +463,27 @@ describe(`Psbt`, () => {
       assert.ok(psbt.__CACHE.__TX);
     })
   })
+
+  describe('Cache', () => {
+    it('non-witness UTXOs are cached', () => {
+      const f = fixtures.cache.nonWitnessUtxo;
+      const psbt = Psbt.fromBase64(f.psbt)
+      const index = f.inputIndex;
+
+      // Cache is empty
+      assert.strictEqual(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index], undefined)
+
+      // Cache is populated
+      psbt.addNonWitnessUtxoToInput(index, f.nonWitnessUtxo)
+      const value = psbt.inputs[index].nonWitnessUtxo
+      assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value))
+      assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(f.nonWitnessUtxo))
+
+      // Cache is rebuilt from internal transaction object when cleared
+      psbt.inputs[index].nonWitnessUtxo = Buffer.from([1,2,3])
+      psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined
+      assert.ok(psbt.inputs[index].nonWitnessUtxo.equals(value))
+    })
+  })
 })
+

From fa897cf78e48942aebb8cfcc308e3af0be22490d Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Wed, 10 Jul 2019 10:19:26 +0900
Subject: [PATCH 083/111] Check signatures for sighash type before finalize

---
 src/psbt.js    | 11 +++++++++++
 ts_src/psbt.ts | 13 +++++++++++++
 2 files changed, 24 insertions(+)

diff --git a/src/psbt.js b/src/psbt.js
index 33a32a3..f352a8b 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -216,6 +216,7 @@ class Psbt extends bip174_1.Psbt {
     const scriptType = classifyScript(script);
     if (!canFinalize(input, script, scriptType))
       throw new Error(`Can not finalize input #${inputIndex}`);
+    checkPartialSigSighashes(input);
     const { finalScriptSig, finalScriptWitness } = getFinalScripts(
       script,
       scriptType,
@@ -446,6 +447,16 @@ function checkInputsForPartialSig(inputs, action) {
     }
   });
 }
+function checkPartialSigSighashes(input) {
+  if (!input.sighashType || !input.partialSig) return;
+  const { partialSig, sighashType } = input;
+  partialSig.forEach(pSig => {
+    const { hashType } = bscript.signature.decode(pSig.signature);
+    if (sighashType !== hashType) {
+      throw new Error('Signature sighash does not match input sighash type');
+    }
+  });
+}
 function checkScriptForPubkey(pubkey, script, action) {
   const pubkeyHash = crypto_1.hash160(pubkey);
   const decompiled = bscript.decompile(script);
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index bfe340c..acb35f6 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -269,6 +269,8 @@ export class Psbt extends PsbtBase {
     if (!canFinalize(input, script, scriptType))
       throw new Error(`Can not finalize input #${inputIndex}`);
 
+    checkPartialSigSighashes(input);
+
     const { finalScriptSig, finalScriptWitness } = getFinalScripts(
       script,
       scriptType,
@@ -547,6 +549,17 @@ function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
   });
 }
 
+function checkPartialSigSighashes(input: PsbtInput): void {
+  if (!input.sighashType || !input.partialSig) return;
+  const { partialSig, sighashType } = input;
+  partialSig.forEach(pSig => {
+    const { hashType } = bscript.signature.decode(pSig.signature);
+    if (sighashType !== hashType) {
+      throw new Error('Signature sighash does not match input sighash type');
+    }
+  });
+}
+
 function checkScriptForPubkey(
   pubkey: Buffer,
   script: Buffer,

From ccab2652f92329dd6a01ffed4ccbd96e5152acf1 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Wed, 10 Jul 2019 11:15:12 +0900
Subject: [PATCH 084/111] Add sighash checks for signer

---
 src/psbt.js     | 65 ++++++++++++++++++++++++++++++++++++++++++-------
 ts_src/psbt.ts  | 64 ++++++++++++++++++++++++++++++++++++++++++------
 types/psbt.d.ts |  8 +++---
 3 files changed, 117 insertions(+), 20 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index f352a8b..c7e8b55 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -273,7 +273,7 @@ class Psbt extends bip174_1.Psbt {
     }
     return results.every(res => res === true);
   }
-  sign(keyPair) {
+  sign(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
     // TODO: Add a pubkey/pubkeyhash cache to each input
@@ -282,7 +282,7 @@ class Psbt extends bip174_1.Psbt {
     const results = [];
     for (const i of range(this.inputs.length)) {
       try {
-        this.signInput(i, keyPair);
+        this.signInput(i, keyPair, sighashTypes);
         results.push(true);
       } catch (err) {
         results.push(false);
@@ -293,7 +293,7 @@ class Psbt extends bip174_1.Psbt {
     }
     return this;
   }
-  signAsync(keyPair) {
+  signAsync(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) {
     return new Promise((resolve, reject) => {
       if (!keyPair || !keyPair.publicKey)
         return reject(new Error('Need Signer to sign input'));
@@ -304,7 +304,7 @@ class Psbt extends bip174_1.Psbt {
       const promises = [];
       for (const [i] of this.inputs.entries()) {
         promises.push(
-          this.signInputAsync(i, keyPair).then(
+          this.signInputAsync(i, keyPair, sighashTypes).then(
             () => {
               results.push(true);
             },
@@ -322,7 +322,11 @@ class Psbt extends bip174_1.Psbt {
       });
     });
   }
-  signInput(inputIndex, keyPair) {
+  signInput(
+    inputIndex,
+    keyPair,
+    sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
+  ) {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
     const { hash, sighashType } = getHashAndSighashType(
@@ -330,6 +334,7 @@ class Psbt extends bip174_1.Psbt {
       inputIndex,
       keyPair.publicKey,
       this.__CACHE,
+      sighashTypes,
     );
     const partialSig = {
       pubkey: keyPair.publicKey,
@@ -337,7 +342,11 @@ class Psbt extends bip174_1.Psbt {
     };
     return this.addPartialSigToInput(inputIndex, partialSig);
   }
-  signInputAsync(inputIndex, keyPair) {
+  signInputAsync(
+    inputIndex,
+    keyPair,
+    sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
+  ) {
     return new Promise((resolve, reject) => {
       if (!keyPair || !keyPair.publicKey)
         return reject(new Error('Need Signer to sign input'));
@@ -346,6 +355,7 @@ class Psbt extends bip174_1.Psbt {
         inputIndex,
         keyPair.publicKey,
         this.__CACHE,
+        sighashTypes,
       );
       Promise.resolve(keyPair.sign(hash)).then(signature => {
         const partialSig = {
@@ -548,19 +558,37 @@ function getFinalScripts(
     finalScriptWitness,
   };
 }
-function getHashAndSighashType(inputs, inputIndex, pubkey, cache) {
+function getHashAndSighashType(
+  inputs,
+  inputIndex,
+  pubkey,
+  cache,
+  sighashTypes,
+) {
   const input = utils_1.checkForInput(inputs, inputIndex);
-  const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
+  const { hash, sighashType, script } = getHashForSig(
+    inputIndex,
+    input,
+    cache,
+    sighashTypes,
+  );
   checkScriptForPubkey(pubkey, script, 'sign');
   return {
     hash,
     sighashType,
   };
 }
-function getHashForSig(inputIndex, input, cache) {
+function getHashForSig(inputIndex, input, cache, sighashTypes) {
   const unsignedTx = cache.__TX;
   const sighashType =
     input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
+  if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
+    const str = sighashTypeToString(sighashType);
+    throw new Error(
+      `Sighash type is not allowed. Retry the sign method passing the ` +
+        `sighashTypes array of whitelisted types. Sighash type: ${str}`,
+    );
+  }
   let hash;
   let script;
   if (input.nonWitnessUtxo) {
@@ -800,6 +828,25 @@ function scriptWitnessToWitnessStack(buffer) {
   }
   return readVector();
 }
+function sighashTypeToString(sighashType) {
+  let text =
+    sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY
+      ? 'SIGHASH_ANYONECANPAY | '
+      : '';
+  const sigMod = sighashType & 0x1f;
+  switch (sigMod) {
+    case transaction_1.Transaction.SIGHASH_ALL:
+      text += 'SIGHASH_ALL';
+      break;
+    case transaction_1.Transaction.SIGHASH_SINGLE:
+      text += 'SIGHASH_SINGLE';
+      break;
+    case transaction_1.Transaction.SIGHASH_NONE:
+      text += 'SIGHASH_NONE';
+      break;
+  }
+  return text;
+}
 function witnessStackToScriptWitness(witness) {
   let buffer = Buffer.allocUnsafe(0);
   function writeSlice(slice) {
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index acb35f6..8fb1e97 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -332,7 +332,10 @@ export class Psbt extends PsbtBase {
     return results.every(res => res === true);
   }
 
-  sign(keyPair: Signer): this {
+  sign(
+    keyPair: Signer,
+    sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+  ): this {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
 
@@ -342,7 +345,7 @@ export class Psbt extends PsbtBase {
     const results: boolean[] = [];
     for (const i of range(this.inputs.length)) {
       try {
-        this.signInput(i, keyPair);
+        this.signInput(i, keyPair, sighashTypes);
         results.push(true);
       } catch (err) {
         results.push(false);
@@ -354,7 +357,10 @@ export class Psbt extends PsbtBase {
     return this;
   }
 
-  signAsync(keyPair: SignerAsync): Promise<void> {
+  signAsync(
+    keyPair: SignerAsync,
+    sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+  ): Promise<void> {
     return new Promise(
       (resolve, reject): any => {
         if (!keyPair || !keyPair.publicKey)
@@ -367,7 +373,7 @@ export class Psbt extends PsbtBase {
         const promises: Array<Promise<void>> = [];
         for (const [i] of this.inputs.entries()) {
           promises.push(
-            this.signInputAsync(i, keyPair).then(
+            this.signInputAsync(i, keyPair, sighashTypes).then(
               () => {
                 results.push(true);
               },
@@ -387,7 +393,11 @@ export class Psbt extends PsbtBase {
     );
   }
 
-  signInput(inputIndex: number, keyPair: Signer): this {
+  signInput(
+    inputIndex: number,
+    keyPair: Signer,
+    sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+  ): this {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
     const { hash, sighashType } = getHashAndSighashType(
@@ -395,6 +405,7 @@ export class Psbt extends PsbtBase {
       inputIndex,
       keyPair.publicKey,
       this.__CACHE,
+      sighashTypes,
     );
 
     const partialSig = {
@@ -405,7 +416,11 @@ export class Psbt extends PsbtBase {
     return this.addPartialSigToInput(inputIndex, partialSig);
   }
 
-  signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void> {
+  signInputAsync(
+    inputIndex: number,
+    keyPair: SignerAsync,
+    sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+  ): Promise<void> {
     return new Promise(
       (resolve, reject): void => {
         if (!keyPair || !keyPair.publicKey)
@@ -415,6 +430,7 @@ export class Psbt extends PsbtBase {
           inputIndex,
           keyPair.publicKey,
           this.__CACHE,
+          sighashTypes,
         );
 
         Promise.resolve(keyPair.sign(hash)).then(signature => {
@@ -683,12 +699,18 @@ function getHashAndSighashType(
   inputIndex: number,
   pubkey: Buffer,
   cache: PsbtCache,
+  sighashTypes: number[],
 ): {
   hash: Buffer;
   sighashType: number;
 } {
   const input = checkForInput(inputs, inputIndex);
-  const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
+  const { hash, sighashType, script } = getHashForSig(
+    inputIndex,
+    input,
+    cache,
+    sighashTypes,
+  );
   checkScriptForPubkey(pubkey, script, 'sign');
   return {
     hash,
@@ -700,6 +722,7 @@ function getHashForSig(
   inputIndex: number,
   input: PsbtInput,
   cache: PsbtCache,
+  sighashTypes?: number[],
 ): {
   script: Buffer;
   hash: Buffer;
@@ -707,6 +730,13 @@ function getHashForSig(
 } {
   const unsignedTx = cache.__TX;
   const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
+  if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
+    const str = sighashTypeToString(sighashType);
+    throw new Error(
+      `Sighash type is not allowed. Retry the sign method passing the ` +
+        `sighashTypes array of whitelisted types. Sighash type: ${str}`,
+    );
+  }
   let hash: Buffer;
   let script: Buffer;
 
@@ -981,6 +1011,26 @@ function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
   return readVector();
 }
 
+function sighashTypeToString(sighashType: number): string {
+  let text =
+    sighashType & Transaction.SIGHASH_ANYONECANPAY
+      ? 'SIGHASH_ANYONECANPAY | '
+      : '';
+  const sigMod = sighashType & 0x1f;
+  switch (sigMod) {
+    case Transaction.SIGHASH_ALL:
+      text += 'SIGHASH_ALL';
+      break;
+    case Transaction.SIGHASH_SINGLE:
+      text += 'SIGHASH_SINGLE';
+      break;
+    case Transaction.SIGHASH_NONE:
+      text += 'SIGHASH_NONE';
+      break;
+  }
+  return text;
+}
+
 function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
   let buffer = Buffer.allocUnsafe(0);
 
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 10617c2..95f6624 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -27,10 +27,10 @@ export declare class Psbt extends PsbtBase {
     finalizeInput(inputIndex: number): this;
     validateAllSignatures(): boolean;
     validateSignatures(inputIndex: number, pubkey?: Buffer): boolean;
-    sign(keyPair: Signer): this;
-    signAsync(keyPair: SignerAsync): Promise<void>;
-    signInput(inputIndex: number, keyPair: Signer): this;
-    signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>;
+    sign(keyPair: Signer, sighashTypes?: number[]): this;
+    signAsync(keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
+    signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this;
+    signInputAsync(inputIndex: number, keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
 }
 interface PsbtOptsOptional {
     network?: Network;

From a50ec330334d214c7bc1099912dd0d58715d5032 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Wed, 10 Jul 2019 16:43:51 +0900
Subject: [PATCH 085/111] Update dep

---
 package-lock.json | 6 +++---
 package.json      | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 768c79f..fce68c4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -200,9 +200,9 @@
       }
     },
     "bip174": {
-      "version": "0.0.13",
-      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.13.tgz",
-      "integrity": "sha512-jWP7Lb27Nmbk6gaZKhJZOyk5LqRWs9z+R2xzgu3W8/iZXIIP2kcR6fh5lNg7GGOiWUaqanWC9rjrDVrBVbXKww=="
+      "version": "0.0.14",
+      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.14.tgz",
+      "integrity": "sha512-v9cre0W4ZpAJS1v18WUJLE9yKdSZyenGpZBg7CXiZ5n35JPXganH92d4Yk8WXpRfbFZ4SMXTqKLEgpLPX1TmcA=="
     },
     "bip32": {
       "version": "2.0.3",
diff --git a/package.json b/package.json
index 1fadfcb..03e6d5f 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
-    "bip174": "0.0.13",
+    "bip174": "0.0.14",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",

From da5adcf88f90a00981a5ce0b0a54963173081731 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 10 Jul 2019 17:59:11 +0700
Subject: [PATCH 086/111] Refactor and cleanup validateSignatures tests

---
 test/fixtures/psbt.json |  7 +++++++
 test/psbt.js            | 34 ++++++++++++++++++++--------------
 2 files changed, 27 insertions(+), 14 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 9e85b63..c6eabd9 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -429,6 +429,13 @@
       "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA="
     }
   ],
+  "validateSignatures": {
+    "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
+    "index": 0,
+    "pubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f', 'hex')",
+    "incorrectPubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e02a', 'hex')",
+    "nonExistantIndex": 42
+  },
   "cache": {
     "nonWitnessUtxo": {
       "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuIgYClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8Q2QxqTwAAAIAAAACAAAAAgCIGAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXENkMak8AAACAAAAAgAEAAIAAAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
diff --git a/test/psbt.js b/test/psbt.js
index ce92dc6..94136af 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -130,20 +130,6 @@ describe(`Psbt`, () => {
         psbt.getFeeRate()
       }, new RegExp('PSBT must be finalized to calculate fee rate'))
 
-      const pubkey = Buffer.from(
-        '029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f',
-        'hex',
-      )
-      assert.strictEqual(psbt.validateSignatures(0), true)
-      assert.strictEqual(psbt.validateSignatures(0, pubkey), true)
-      assert.throws(() => {
-        pubkey[32] = 42
-        psbt.validateSignatures(0, pubkey)
-      }, new RegExp('No signatures for this pubkey'))
-      assert.throws(() => {
-        psbt.validateSignatures(42)
-      }, new RegExp('No signatures to validate'))
-
       psbt.finalizeAllInputs()
 
       assert.strictEqual(psbt.toBase64(), f.result)
@@ -401,6 +387,26 @@ describe(`Psbt`, () => {
     })
   })
 
+  describe('validateSignatures', () => {
+    const f = fixtures.validateSignatures
+    it('Correctly validates a signature', () => {
+      const psbt = Psbt.fromBase64(f.psbt)
+
+      assert.strictEqual(psbt.validateSignatures(f.index), true)
+      assert.throws(() => {
+        psbt.validateSignatures(f.nonExistantIndex)
+      }, new RegExp('No signatures to validate'))
+    })
+
+    it('Correctly validates a signature against a pubkey', () => {
+      const psbt = Psbt.fromBase64(f.psbt)
+      assert.strictEqual(psbt.validateSignatures(f.index, f.pubkey), true)
+      assert.throws(() => {
+        psbt.validateSignatures(f.index, f.incorrectPubkey)
+      }, new RegExp('No signatures for this pubkey'))
+    })
+  })
+
   describe('create 1-to-1 transaction', () => {
     const alice = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr')
     const psbt = new Psbt()

From 47b42e72f4e8bc2f20807ffba4b14c6c1f3d2040 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 10 Jul 2019 18:05:30 +0700
Subject: [PATCH 087/111] Refactor and cleanup getFeeRate tests

---
 test/fixtures/psbt.json |  3 +++
 test/psbt.js            | 21 +++++++++++++++++----
 2 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index c6eabd9..13f4389 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -436,6 +436,9 @@
     "incorrectPubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e02a', 'hex')",
     "nonExistantIndex": 42
   },
+  "getFeeRate": {
+    "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
+  },
   "cache": {
     "nonWitnessUtxo": {
       "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuIgYClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8Q2QxqTwAAAIAAAACAAAAAgCIGAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXENkMak8AAACAAAAAgAEAAIAAAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
diff --git a/test/psbt.js b/test/psbt.js
index 94136af..cc72e55 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -126,10 +126,6 @@ describe(`Psbt`, () => {
     it('Finalizes inputs and gives the expected PSBT', () => {
       const psbt =  Psbt.fromBase64(f.psbt)
 
-      assert.throws(() => {
-        psbt.getFeeRate()
-      }, new RegExp('PSBT must be finalized to calculate fee rate'))
-
       psbt.finalizeAllInputs()
 
       assert.strictEqual(psbt.toBase64(), f.result)
@@ -407,6 +403,23 @@ describe(`Psbt`, () => {
     })
   })
 
+  describe('getFeeRate', () => {
+    it('Throws error if called before inputs are finalized', () => {
+      const f = fixtures.getFeeRate
+      const psbt =  Psbt.fromBase64(f.psbt)
+
+      assert.throws(() => {
+        psbt.getFeeRate()
+      }, new RegExp('PSBT must be finalized to calculate fee rate'))
+
+      psbt.finalizeAllInputs()
+
+      assert.doesNotThrow(() => {
+        psbt.getFeeRate()
+      })
+    })
+  })
+
   describe('create 1-to-1 transaction', () => {
     const alice = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr')
     const psbt = new Psbt()

From d05144627588fd98ca7562864e2d891fc7927706 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 10 Jul 2019 18:12:56 +0700
Subject: [PATCH 088/111] Add P2MS test case to finalizer tests

---
 test/fixtures/psbt.json | 6 ++++++
 test/psbt.js            | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 13f4389..2333d65 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -293,8 +293,14 @@
     ],
     "finalizer": [
       {
+        "description": "BIP174 test case",
         "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
         "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA=="
+      },
+      {
+        "description": "P2MS input",
+        "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=",
+        "result": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAABB/0gAQBIMEUCIQDqqpZ1MJnPj6imVcAADCzzQirJB4VYJNaV0fXe55gekgIgNAtuInVXbYJffrCAUfAN2WuVnLMDSd4vD1kzFkddAuEBSDBFAiEA2cMFRBaNLyENM+J1Fn3msZkrctYIshkHU1jH3754FPwCIBK6ktH4o1rVJ7t6gGRZBwOfL4FoF8xD6nVFMIBd3v8YAUyLUiECxZrqO4BvDo4JmjuQB7gv9Trw2q0Ldeke1RClPHZEUQghAuF++G/r0Rd1y4Ml8hno5hOVQdX90WPyEXlIIMjR4HS2IQPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuURCEDveDmb+sjw+JBGUmQarmAOxT7AAWWelALXlbhMO31cc5UrgAA"
       }
     ],
     "extractor": [
diff --git a/test/psbt.js b/test/psbt.js
index cc72e55..e1840ce 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -123,7 +123,7 @@ describe(`Psbt`, () => {
   })
 
   fixtures.bip174.finalizer.forEach(f => {
-    it('Finalizes inputs and gives the expected PSBT', () => {
+    it(`Finalizes inputs and gives the expected PSBT: ${f.description}`, () => {
       const psbt =  Psbt.fromBase64(f.psbt)
 
       psbt.finalizeAllInputs()

From f6ab5b796f8e74353b85dfa8ae0d6cffa90b947d Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 10 Jul 2019 18:54:05 +0700
Subject: [PATCH 089/111] Move all BIP174 test cases into BIP14 describe block

---
 test/psbt.js | 129 +++++++++++++++++++++++++--------------------------
 1 file changed, 64 insertions(+), 65 deletions(-)

diff --git a/test/psbt.js b/test/psbt.js
index e1840ce..9f7cc1d 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -93,74 +93,74 @@ describe(`Psbt`, () => {
         assert.strictEqual(psbt.toBase64(), f.result)
       })
     })
-  })
 
-  fixtures.bip174.signer.forEach(f => {
-    it('Signs PSBT to the expected result', () => {
-      const psbt =  Psbt.fromBase64(f.psbt)
-
-      f.keys.forEach(({inputToSign, WIF}) => {
-        const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet);
-        psbt.signInput(inputToSign, keyPair);
+    fixtures.bip174.signer.forEach(f => {
+      it('Signs PSBT to the expected result', () => {
+        const psbt =  Psbt.fromBase64(f.psbt)
+  
+        f.keys.forEach(({inputToSign, WIF}) => {
+          const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet);
+          psbt.signInput(inputToSign, keyPair);
+        })
+  
+        assert.strictEqual(psbt.toBase64(), f.result)
       })
-
-      assert.strictEqual(psbt.toBase64(), f.result)
     })
-  })
-
-  fixtures.bip174.combiner.forEach(f => {
-    it('Combines two PSBTs to the expected result', () => {
-      const psbts =  f.psbts.map(psbt => Psbt.fromBase64(psbt))
-
-      psbts[0].combine(psbts[1])
-
-      // Produces a different Base64 string due to implemetation specific key-value ordering.
-      // That means this test will fail:
-      // assert.strictEqual(psbts[0].toBase64(), f.result)
-      // However, if we compare the actual PSBT properties we can see they are logically identical:
-      assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result))
+  
+    fixtures.bip174.combiner.forEach(f => {
+      it('Combines two PSBTs to the expected result', () => {
+        const psbts =  f.psbts.map(psbt => Psbt.fromBase64(psbt))
+  
+        psbts[0].combine(psbts[1])
+  
+        // Produces a different Base64 string due to implemetation specific key-value ordering.
+        // That means this test will fail:
+        // assert.strictEqual(psbts[0].toBase64(), f.result)
+        // However, if we compare the actual PSBT properties we can see they are logically identical:
+        assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result))
+      })
     })
-  })
-
-  fixtures.bip174.finalizer.forEach(f => {
-    it(`Finalizes inputs and gives the expected PSBT: ${f.description}`, () => {
-      const psbt =  Psbt.fromBase64(f.psbt)
-
-      psbt.finalizeAllInputs()
-
-      assert.strictEqual(psbt.toBase64(), f.result)
+  
+    fixtures.bip174.finalizer.forEach(f => {
+      it(`Finalizes inputs and gives the expected PSBT: ${f.description}`, () => {
+        const psbt =  Psbt.fromBase64(f.psbt)
+  
+        psbt.finalizeAllInputs()
+  
+        assert.strictEqual(psbt.toBase64(), f.result)
+      })
     })
-  })
-
-  fixtures.bip174.extractor.forEach(f => {
-    it('Extracts the expected transaction from a PSBT', () => {
-      const psbt1 =  Psbt.fromBase64(f.psbt)
-      const transaction1 = psbt1.extractTransaction(true).toHex()
-
-      const psbt2 =  Psbt.fromBase64(f.psbt)
-      const transaction2 = psbt2.extractTransaction().toHex()
-
-      assert.strictEqual(transaction1, transaction2)
-      assert.strictEqual(transaction1, f.transaction)
-
-      const psbt3 =  Psbt.fromBase64(f.psbt)
-      delete psbt3.inputs[0].finalScriptSig
-      delete psbt3.inputs[0].finalScriptWitness
-      assert.throws(() => {
-        psbt3.extractTransaction()
-      }, new RegExp('Not finalized'))
-
-      const psbt4 =  Psbt.fromBase64(f.psbt)
-      psbt4.setMaximumFeeRate(1)
-      assert.throws(() => {
-        psbt4.extractTransaction()
-      }, new RegExp('Warning: You are paying around [\\d.]+ in fees'))
-
-      const psbt5 =  Psbt.fromBase64(f.psbt)
-      psbt5.extractTransaction(true)
-      const fr1 = psbt5.getFeeRate()
-      const fr2 = psbt5.getFeeRate()
-      assert.strictEqual(fr1, fr2)
+  
+    fixtures.bip174.extractor.forEach(f => {
+      it('Extracts the expected transaction from a PSBT', () => {
+        const psbt1 =  Psbt.fromBase64(f.psbt)
+        const transaction1 = psbt1.extractTransaction(true).toHex()
+  
+        const psbt2 =  Psbt.fromBase64(f.psbt)
+        const transaction2 = psbt2.extractTransaction().toHex()
+  
+        assert.strictEqual(transaction1, transaction2)
+        assert.strictEqual(transaction1, f.transaction)
+  
+        const psbt3 =  Psbt.fromBase64(f.psbt)
+        delete psbt3.inputs[0].finalScriptSig
+        delete psbt3.inputs[0].finalScriptWitness
+        assert.throws(() => {
+          psbt3.extractTransaction()
+        }, new RegExp('Not finalized'))
+  
+        const psbt4 =  Psbt.fromBase64(f.psbt)
+        psbt4.setMaximumFeeRate(1)
+        assert.throws(() => {
+          psbt4.extractTransaction()
+        }, new RegExp('Warning: You are paying around [\\d.]+ in fees'))
+  
+        const psbt5 =  Psbt.fromBase64(f.psbt)
+        psbt5.extractTransaction(true)
+        const fr1 = psbt5.getFeeRate()
+        const fr2 = psbt5.getFeeRate()
+        assert.strictEqual(fr1, fr2)
+      })
     })
   })
 
@@ -504,5 +504,4 @@ describe(`Psbt`, () => {
       assert.ok(psbt.inputs[index].nonWitnessUtxo.equals(value))
     })
   })
-})
-
+})
\ No newline at end of file

From ec2c14b81f79ee4dfcae004535b5a35d966c3406 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 10 Jul 2019 19:20:10 +0700
Subject: [PATCH 090/111] Extract finalizeAllInputs test out of BIP174 test
 cases

---
 test/fixtures/psbt.json | 13 +++++++------
 test/psbt.js            | 14 +++++++++++++-
 2 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 2333d65..8092177 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -293,14 +293,8 @@
     ],
     "finalizer": [
       {
-        "description": "BIP174 test case",
         "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
         "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA=="
-      },
-      {
-        "description": "P2MS input",
-        "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=",
-        "result": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAABB/0gAQBIMEUCIQDqqpZ1MJnPj6imVcAADCzzQirJB4VYJNaV0fXe55gekgIgNAtuInVXbYJffrCAUfAN2WuVnLMDSd4vD1kzFkddAuEBSDBFAiEA2cMFRBaNLyENM+J1Fn3msZkrctYIshkHU1jH3754FPwCIBK6ktH4o1rVJ7t6gGRZBwOfL4FoF8xD6nVFMIBd3v8YAUyLUiECxZrqO4BvDo4JmjuQB7gv9Trw2q0Ldeke1RClPHZEUQghAuF++G/r0Rd1y4Ml8hno5hOVQdX90WPyEXlIIMjR4HS2IQPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuURCEDveDmb+sjw+JBGUmQarmAOxT7AAWWelALXlbhMO31cc5UrgAA"
       }
     ],
     "extractor": [
@@ -429,6 +423,13 @@
       }
     ]
   },
+  "finalizeAllInputs": [
+    {
+      "type": "P2MS",
+      "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=",
+      "result": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAABB/0gAQBIMEUCIQDqqpZ1MJnPj6imVcAADCzzQirJB4VYJNaV0fXe55gekgIgNAtuInVXbYJffrCAUfAN2WuVnLMDSd4vD1kzFkddAuEBSDBFAiEA2cMFRBaNLyENM+J1Fn3msZkrctYIshkHU1jH3754FPwCIBK6ktH4o1rVJ7t6gGRZBwOfL4FoF8xD6nVFMIBd3v8YAUyLUiECxZrqO4BvDo4JmjuQB7gv9Trw2q0Ldeke1RClPHZEUQghAuF++G/r0Rd1y4Ml8hno5hOVQdX90WPyEXlIIMjR4HS2IQPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuURCEDveDmb+sjw+JBGUmQarmAOxT7AAWWelALXlbhMO31cc5UrgAA"
+    }
+  ],
   "fromTransaction": [
     {
       "transaction": "020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000",
diff --git a/test/psbt.js b/test/psbt.js
index 9f7cc1d..6dd01a9 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -122,7 +122,7 @@ describe(`Psbt`, () => {
     })
   
     fixtures.bip174.finalizer.forEach(f => {
-      it(`Finalizes inputs and gives the expected PSBT: ${f.description}`, () => {
+      it("Finalizes inputs and gives the expected PSBT", () => {
         const psbt =  Psbt.fromBase64(f.psbt)
   
         psbt.finalizeAllInputs()
@@ -266,6 +266,18 @@ describe(`Psbt`, () => {
     })
   })
 
+  describe('finalizeAllInputs', () => {
+    fixtures.finalizeAllInputs.forEach(f => {
+      it(`Finalizes inputs of type "${f.type}"`, () => {
+        const psbt =  Psbt.fromBase64(f.psbt)
+
+        psbt.finalizeAllInputs()
+
+        assert.strictEqual(psbt.toBase64(), f.result)
+      })
+    })
+  })
+
   describe('fromTransaction', () => {
     fixtures.fromTransaction.forEach(f => {
       it('Creates the expected PSBT from a transaction buffer', () => {

From bc56ca0fa1210d991138e78ccb8ff0781581d415 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 10 Jul 2019 19:21:33 +0700
Subject: [PATCH 091/111] Test finalizeAllInputs against P2PK input

---
 test/fixtures/psbt.json | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 8092177..ad6ffdf 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -424,6 +424,11 @@
     ]
   },
   "finalizeAllInputs": [
+    {
+      "type": "P2PK",
+      "psbt": "cHNidP8BAFUCAAAAAZfdvR7JvbVsuXunG1P19tTYi8Z0l6WHiz+0jpzTNTt+AAAAAAD/////AYA4AQAAAAAAGXapFMjtQ0xCK7OPd32ZJTY2UcdpFIaJiKwAAAAAAAEAygIAAAABb7TMmNs5UwEEpccLHlaaM0w3HEHhh85NjrUObLDMuZsBAAAAa0gwRQIhANWUevBBcqfRpH67rDuFqI+KHqxViKXxmSM+6/jE5ZHjAiBgO/vQg2EsYhaOD5lQbmZYaymRLukA6SCoXhkN21hiOwEhAoKzWGkj1LJ2iOt5wj6jMOtAMGJH50ZjAYZU81uUu6y+/////wGQXwEAAAAAACMhAzpK4NeYX+xyBOKynjD3BEPDq7EN8brZXje488gxshXwrAAAAAAiAgM6SuDXmF/scgTisp4w9wRDw6uxDfG62V43uPPIMbIV8EgwRQIhAL3H/XEPRAZEbpjBwkuLqUKBSu1Inpb2rganXNFcY2JsAiASrXTM2xODEKp7m7RTzYqBchqlvbl88zO/CGW9SePj2gEAAA==",
+      "result": "cHNidP8BAFUCAAAAAZfdvR7JvbVsuXunG1P19tTYi8Z0l6WHiz+0jpzTNTt+AAAAAAD/////AYA4AQAAAAAAGXapFMjtQ0xCK7OPd32ZJTY2UcdpFIaJiKwAAAAAAAEAygIAAAABb7TMmNs5UwEEpccLHlaaM0w3HEHhh85NjrUObLDMuZsBAAAAa0gwRQIhANWUevBBcqfRpH67rDuFqI+KHqxViKXxmSM+6/jE5ZHjAiBgO/vQg2EsYhaOD5lQbmZYaymRLukA6SCoXhkN21hiOwEhAoKzWGkj1LJ2iOt5wj6jMOtAMGJH50ZjAYZU81uUu6y+/////wGQXwEAAAAAACMhAzpK4NeYX+xyBOKynjD3BEPDq7EN8brZXje488gxshXwrAAAAAABB0lIMEUCIQC9x/1xD0QGRG6YwcJLi6lCgUrtSJ6W9q4Gp1zRXGNibAIgEq10zNsTgxCqe5u0U82KgXIapb25fPMzvwhlvUnj49oBAAA="
+    },
     {
       "type": "P2MS",
       "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=",

From f55ee323861bcdd25ccd8c951bf578e7e0ac3965 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 10 Jul 2019 19:21:56 +0700
Subject: [PATCH 092/111] Test finalizeAllInputs against P2PKH input

---
 test/fixtures/psbt.json | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index ad6ffdf..c275f9b 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -429,6 +429,11 @@
       "psbt": "cHNidP8BAFUCAAAAAZfdvR7JvbVsuXunG1P19tTYi8Z0l6WHiz+0jpzTNTt+AAAAAAD/////AYA4AQAAAAAAGXapFMjtQ0xCK7OPd32ZJTY2UcdpFIaJiKwAAAAAAAEAygIAAAABb7TMmNs5UwEEpccLHlaaM0w3HEHhh85NjrUObLDMuZsBAAAAa0gwRQIhANWUevBBcqfRpH67rDuFqI+KHqxViKXxmSM+6/jE5ZHjAiBgO/vQg2EsYhaOD5lQbmZYaymRLukA6SCoXhkN21hiOwEhAoKzWGkj1LJ2iOt5wj6jMOtAMGJH50ZjAYZU81uUu6y+/////wGQXwEAAAAAACMhAzpK4NeYX+xyBOKynjD3BEPDq7EN8brZXje488gxshXwrAAAAAAiAgM6SuDXmF/scgTisp4w9wRDw6uxDfG62V43uPPIMbIV8EgwRQIhAL3H/XEPRAZEbpjBwkuLqUKBSu1Inpb2rganXNFcY2JsAiASrXTM2xODEKp7m7RTzYqBchqlvbl88zO/CGW9SePj2gEAAA==",
       "result": "cHNidP8BAFUCAAAAAZfdvR7JvbVsuXunG1P19tTYi8Z0l6WHiz+0jpzTNTt+AAAAAAD/////AYA4AQAAAAAAGXapFMjtQ0xCK7OPd32ZJTY2UcdpFIaJiKwAAAAAAAEAygIAAAABb7TMmNs5UwEEpccLHlaaM0w3HEHhh85NjrUObLDMuZsBAAAAa0gwRQIhANWUevBBcqfRpH67rDuFqI+KHqxViKXxmSM+6/jE5ZHjAiBgO/vQg2EsYhaOD5lQbmZYaymRLukA6SCoXhkN21hiOwEhAoKzWGkj1LJ2iOt5wj6jMOtAMGJH50ZjAYZU81uUu6y+/////wGQXwEAAAAAACMhAzpK4NeYX+xyBOKynjD3BEPDq7EN8brZXje488gxshXwrAAAAAABB0lIMEUCIQC9x/1xD0QGRG6YwcJLi6lCgUrtSJ6W9q4Gp1zRXGNibAIgEq10zNsTgxCqe5u0U82KgXIapb25fPMzvwhlvUnj49oBAAA="
     },
+    {
+      "type": "P2PKH",
+      "psbt": "cHNidP8BAFUCAAAAAaTbj9mE+B5Z8PLsuGUNGOzDqrtwdB08vvSccSCrezh+AAAAAAD/////AYA4AQAAAAAAGXapFDuK+0mR+qtL9tyadI72bKMwH+vuiKwAAAAAAAEAwAIAAAABks7XL87tkSusFA3L2u2CdJeNkqTMobehPNm/wkGQUzoBAAAAa0gwRQIhAMtzqC7axSA7/7nbio9NKQZz2ePuKeaF5T4c8JSXWEdgAiAID39I05hbJMNGSomrF7XVWEsEEHHzX/lkL3vnOOu4cAEhAvu+bfurbDCzxfOLxEhvz9ZyxPLdI1h9wMT8gl2nyLYV/////wGQXwEAAAAAABl2qRTH5239BfS9zrbwUtvpATwjua4LGYisAAAAACICA1oNX0U/6GwAuVI7JHhneD94sm/o+YrfTnRAhdRUMjoISDBFAiEAhk+HbvY6YShBCUmBVCk42sFWH9LTwUv2wbRC/tIuEAwCIED/UkklY3fpdDBN7qSBHFDyEOeHMUzXD0bvtFTAiKP7AQAA",
+      "result": "cHNidP8BAFUCAAAAAaTbj9mE+B5Z8PLsuGUNGOzDqrtwdB08vvSccSCrezh+AAAAAAD/////AYA4AQAAAAAAGXapFDuK+0mR+qtL9tyadI72bKMwH+vuiKwAAAAAAAEAwAIAAAABks7XL87tkSusFA3L2u2CdJeNkqTMobehPNm/wkGQUzoBAAAAa0gwRQIhAMtzqC7axSA7/7nbio9NKQZz2ePuKeaF5T4c8JSXWEdgAiAID39I05hbJMNGSomrF7XVWEsEEHHzX/lkL3vnOOu4cAEhAvu+bfurbDCzxfOLxEhvz9ZyxPLdI1h9wMT8gl2nyLYV/////wGQXwEAAAAAABl2qRTH5239BfS9zrbwUtvpATwjua4LGYisAAAAAAEHa0gwRQIhAIZPh272OmEoQQlJgVQpONrBVh/S08FL9sG0Qv7SLhAMAiBA/1JJJWN36XQwTe6kgRxQ8hDnhzFM1w9G77RUwIij+wEhA1oNX0U/6GwAuVI7JHhneD94sm/o+YrfTnRAhdRUMjoIAAA="
+    },
     {
       "type": "P2MS",
       "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=",

From 7377566f98c8497b6f261a34127d6b7bcb57328f Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 10 Jul 2019 19:22:38 +0700
Subject: [PATCH 093/111] Test finalizeAllInputs against P2SH-P2WPKH input

---
 test/fixtures/psbt.json | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index c275f9b..de514f9 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -438,6 +438,11 @@
       "type": "P2MS",
       "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=",
       "result": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAABB/0gAQBIMEUCIQDqqpZ1MJnPj6imVcAADCzzQirJB4VYJNaV0fXe55gekgIgNAtuInVXbYJffrCAUfAN2WuVnLMDSd4vD1kzFkddAuEBSDBFAiEA2cMFRBaNLyENM+J1Fn3msZkrctYIshkHU1jH3754FPwCIBK6ktH4o1rVJ7t6gGRZBwOfL4FoF8xD6nVFMIBd3v8YAUyLUiECxZrqO4BvDo4JmjuQB7gv9Trw2q0Ldeke1RClPHZEUQghAuF++G/r0Rd1y4Ml8hno5hOVQdX90WPyEXlIIMjR4HS2IQPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuURCEDveDmb+sjw+JBGUmQarmAOxT7AAWWelALXlbhMO31cc5UrgAA"
+    },
+    {
+      "type": "P2SH-P2WPKH",
+      "psbt": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAAiAgKj88rhJwk3Zxm0p0Rp+xC/6cxmj+I741DHPWPWN7iA+0cwRAIgTRhd9WUpoHYl9tUVmoJ336fJAJInIjdYsoatvRiW8hgCIGOYMlpKRHiHA428Sfa2CdAIIGGQCGhuIgIzj2FN6USnAQEEFgAU4sZupXPxqhcsOB1ghJxBvH4XcesAAA==",
+      "result": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAABBxcWABTixm6lc/GqFyw4HWCEnEG8fhdx6wAA"
     }
   ],
   "fromTransaction": [

From 876a241e0c6d1f4e69596de945bc453bcd9c3976 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Wed, 10 Jul 2019 19:23:01 +0700
Subject: [PATCH 094/111] Test finalizeAllInputs against P2WPKH input

---
 test/fixtures/psbt.json | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index de514f9..72d5662 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -443,6 +443,11 @@
       "type": "P2SH-P2WPKH",
       "psbt": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAAiAgKj88rhJwk3Zxm0p0Rp+xC/6cxmj+I741DHPWPWN7iA+0cwRAIgTRhd9WUpoHYl9tUVmoJ336fJAJInIjdYsoatvRiW8hgCIGOYMlpKRHiHA428Sfa2CdAIIGGQCGhuIgIzj2FN6USnAQEEFgAU4sZupXPxqhcsOB1ghJxBvH4XcesAAA==",
       "result": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAABBxcWABTixm6lc/GqFyw4HWCEnEG8fhdx6wAA"
+    },
+    {
+      "type": "P2WPKH",
+      "psbt": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAIgIDV7l7X08dWtzMR2saPXJo782Tot5+PQBMZZOm3GGTUHpIMEUCIQDxPCn2fwfxoYgGVyi5++mXNAmCX2wWRS7LuJB2qQkMVQIgHsxylMEWd7ladApdSpnEtSwWb4/QJGOnsDGZCPxvfucBAAA=",
+      "result": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAAQcAAAA="
     }
   ],
   "fromTransaction": [

From 9ee115b030b7effaf10b18c4e24e5aa9181efc8f Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 11 Jul 2019 10:11:51 +0900
Subject: [PATCH 095/111] assert the fee calculation is correct

---
 test/fixtures/psbt.json |  3 ++-
 test/psbt.js            | 34 ++++++++++++++++------------------
 2 files changed, 18 insertions(+), 19 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 72d5662..638eb19 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -464,7 +464,8 @@
     "nonExistantIndex": 42
   },
   "getFeeRate": {
-    "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
+    "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
+    "fee": 21
   },
   "cache": {
     "nonWitnessUtxo": {
diff --git a/test/psbt.js b/test/psbt.js
index 6dd01a9..0ccd8ef 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -97,22 +97,22 @@ describe(`Psbt`, () => {
     fixtures.bip174.signer.forEach(f => {
       it('Signs PSBT to the expected result', () => {
         const psbt =  Psbt.fromBase64(f.psbt)
-  
+
         f.keys.forEach(({inputToSign, WIF}) => {
           const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet);
           psbt.signInput(inputToSign, keyPair);
         })
-  
+
         assert.strictEqual(psbt.toBase64(), f.result)
       })
     })
-  
+
     fixtures.bip174.combiner.forEach(f => {
       it('Combines two PSBTs to the expected result', () => {
         const psbts =  f.psbts.map(psbt => Psbt.fromBase64(psbt))
-  
+
         psbts[0].combine(psbts[1])
-  
+
         // Produces a different Base64 string due to implemetation specific key-value ordering.
         // That means this test will fail:
         // assert.strictEqual(psbts[0].toBase64(), f.result)
@@ -120,41 +120,41 @@ describe(`Psbt`, () => {
         assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result))
       })
     })
-  
+
     fixtures.bip174.finalizer.forEach(f => {
       it("Finalizes inputs and gives the expected PSBT", () => {
         const psbt =  Psbt.fromBase64(f.psbt)
-  
+
         psbt.finalizeAllInputs()
-  
+
         assert.strictEqual(psbt.toBase64(), f.result)
       })
     })
-  
+
     fixtures.bip174.extractor.forEach(f => {
       it('Extracts the expected transaction from a PSBT', () => {
         const psbt1 =  Psbt.fromBase64(f.psbt)
         const transaction1 = psbt1.extractTransaction(true).toHex()
-  
+
         const psbt2 =  Psbt.fromBase64(f.psbt)
         const transaction2 = psbt2.extractTransaction().toHex()
-  
+
         assert.strictEqual(transaction1, transaction2)
         assert.strictEqual(transaction1, f.transaction)
-  
+
         const psbt3 =  Psbt.fromBase64(f.psbt)
         delete psbt3.inputs[0].finalScriptSig
         delete psbt3.inputs[0].finalScriptWitness
         assert.throws(() => {
           psbt3.extractTransaction()
         }, new RegExp('Not finalized'))
-  
+
         const psbt4 =  Psbt.fromBase64(f.psbt)
         psbt4.setMaximumFeeRate(1)
         assert.throws(() => {
           psbt4.extractTransaction()
         }, new RegExp('Warning: You are paying around [\\d.]+ in fees'))
-  
+
         const psbt5 =  Psbt.fromBase64(f.psbt)
         psbt5.extractTransaction(true)
         const fr1 = psbt5.getFeeRate()
@@ -426,9 +426,7 @@ describe(`Psbt`, () => {
 
       psbt.finalizeAllInputs()
 
-      assert.doesNotThrow(() => {
-        psbt.getFeeRate()
-      })
+      assert.strictEqual(psbt.getFeeRate(), f.fee)
     })
   })
 
@@ -516,4 +514,4 @@ describe(`Psbt`, () => {
       assert.ok(psbt.inputs[index].nonWitnessUtxo.equals(value))
     })
   })
-})
\ No newline at end of file
+})

From 266302a3ae29413362d8108ba18e6bfd515e9820 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 11 Jul 2019 10:21:36 +0900
Subject: [PATCH 096/111] Add P2WSH-P2PK finalize vector

---
 test/fixtures/psbt.json | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 638eb19..aeed39d 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -448,6 +448,11 @@
       "type": "P2WPKH",
       "psbt": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAIgIDV7l7X08dWtzMR2saPXJo782Tot5+PQBMZZOm3GGTUHpIMEUCIQDxPCn2fwfxoYgGVyi5++mXNAmCX2wWRS7LuJB2qQkMVQIgHsxylMEWd7ladApdSpnEtSwWb4/QJGOnsDGZCPxvfucBAAA=",
       "result": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAAQcAAAA="
+    },
+    {
+      "type": "P2WSH-P2PK",
+      "psbt": "cHNidP8BAKYCAAAAAlwKQ3suPWwEJ/zQ9sZsIioOcHKU1KoLMxlMNSXVIkEWAAAAAAD/////YYDJMap+mYgbTrCNAdpWHN+EkKvl+XYao/6co/EQfwMAAAAAAP////8CkF8BAAAAAAAWABRnPBAmVHz2HL+8/1U+QG5L2thjmjhKAAAAAAAAIgAg700yfFRyhWzQnPHIUb/XQqsjlpf4A0uw682pCVWuQ8IAAAAAAAEBKzB1AAAAAAAAIgAgth9oE4cDfC5aV58VgkW5CptHsIxppYzJV8C5kT6aTo8iAgNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06UgwRQIhALO0xRpuqP3aVkm+DPykrtqe6fPNSgNblp9K9MAwmtHJAiAHV5ZvZN8Vi49n/o9ISFyvtHsPXXPKqBxC9m2m2HlpYgEBBSMhA182CcWcQ9L2zg9j8jlt7OlaIATNDgNFMBKB1J14wzTprAABASuAOAEAAAAAACIAILYfaBOHA3wuWlefFYJFuQqbR7CMaaWMyVfAuZE+mk6PIgIDXzYJxZxD0vbOD2PyOW3s6VogBM0OA0UwEoHUnXjDNOlIMEUCIQC6XN6tpo9mYlZej4BXSSh5D1K6aILBfQ4WBWXUrISx6wIgVaxFUsz8y59xJ1V4sZ1qarHX9pZ+MJmLKbl2ZSadisoBAQUjIQNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06awAAAA=",
+      "result": "cHNidP8BAKYCAAAAAlwKQ3suPWwEJ/zQ9sZsIioOcHKU1KoLMxlMNSXVIkEWAAAAAAD/////YYDJMap+mYgbTrCNAdpWHN+EkKvl+XYao/6co/EQfwMAAAAAAP////8CkF8BAAAAAAAWABRnPBAmVHz2HL+8/1U+QG5L2thjmjhKAAAAAAAAIgAg700yfFRyhWzQnPHIUb/XQqsjlpf4A0uw682pCVWuQ8IAAAAAAAEBKzB1AAAAAAAAIgAgth9oE4cDfC5aV58VgkW5CptHsIxppYzJV8C5kT6aTo8BCG4CSDBFAiEAs7TFGm6o/dpWSb4M/KSu2p7p881KA1uWn0r0wDCa0ckCIAdXlm9k3xWLj2f+j0hIXK+0ew9dc8qoHEL2babYeWliASMhA182CcWcQ9L2zg9j8jlt7OlaIATNDgNFMBKB1J14wzTprAABASuAOAEAAAAAACIAILYfaBOHA3wuWlefFYJFuQqbR7CMaaWMyVfAuZE+mk6PAQhuAkgwRQIhALpc3q2mj2ZiVl6PgFdJKHkPUrpogsF9DhYFZdSshLHrAiBVrEVSzPzLn3EnVXixnWpqsdf2ln4wmYspuXZlJp2KygEjIQNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06awAAAA="
     }
   ],
   "fromTransaction": [

From 8a5104c33366da88d56b1102be261fe81a9e35d9 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 11 Jul 2019 11:09:05 +0900
Subject: [PATCH 097/111] Add tests

---
 test/fixtures/psbt.json | 18 ++++++++---
 test/psbt.js            | 68 ++++++++++++++++++++++++++++++++---------
 2 files changed, 67 insertions(+), 19 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index aeed39d..61ab114 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -314,7 +314,7 @@
         "exception": "Error adding input."
       },
       {
-        "description": "checks for hash and index",
+        "description": "should be equal",
         "inputData": {
           "hash": "Buffer.from('000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f', 'hex')",
           "index": 2
@@ -326,12 +326,19 @@
   "addOutput": {
     "checks": [
       {
-        "description": "checks for hash and index",
+        "description": "Checks value is number",
         "outputData": {
           "address": "1P2NFEBp32V2arRwZNww6tgXEV58FG94mr",
           "value": "xyz"
         },
         "exception": "Error adding output."
+      },
+      {
+        "description": "Adds output normally",
+        "outputData": {
+          "address": "1P2NFEBp32V2arRwZNww6tgXEV58FG94mr",
+          "value": 42
+        }
       }
     ]
   },
@@ -446,8 +453,8 @@
     },
     {
       "type": "P2WPKH",
-      "psbt": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAIgIDV7l7X08dWtzMR2saPXJo782Tot5+PQBMZZOm3GGTUHpIMEUCIQDxPCn2fwfxoYgGVyi5++mXNAmCX2wWRS7LuJB2qQkMVQIgHsxylMEWd7ladApdSpnEtSwWb4/QJGOnsDGZCPxvfucBAAA=",
-      "result": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAAQcAAAA="
+      "psbt": "cHNidP8BAFICAAAAAb5UIQFSoXPjqxSmuiSZkM5PjaycnrXQ6VB+u2+MzbSGAAAAAAD/////ASBOAAAAAAAAFgAUXsVBEaHSlhycDORbHHtBKwDo4zIAAAAAAAEBHzB1AAAAAAAAFgAUvIgag7HZu7Rjd/ugmJC/MHlZmAYiAgLN1zezMD4c4uegTbgfJ1GCtN5/hlJYaJt7e8mt1BVzIEcwRAIgXhgL5G81tXP7MAaKJtA0QaFu17bLocOGqxXmDoIfYUACIAOIynpoPS/dTz9Omg2h7v5kiql7ab0SPzWDdxpvpsUcAQAA",
+      "result": "cHNidP8BAFICAAAAAb5UIQFSoXPjqxSmuiSZkM5PjaycnrXQ6VB+u2+MzbSGAAAAAAD/////ASBOAAAAAAAAFgAUXsVBEaHSlhycDORbHHtBKwDo4zIAAAAAAAEBHzB1AAAAAAAAFgAUvIgag7HZu7Rjd/ugmJC/MHlZmAYBCGsCRzBEAiBeGAvkbzW1c/swBoom0DRBoW7Xtsuhw4arFeYOgh9hQAIgA4jKemg9L91PP06aDaHu/mSKqXtpvRI/NYN3Gm+mxRwBIQLN1zezMD4c4uegTbgfJ1GCtN5/hlJYaJt7e8mt1BVzIAAA"
     },
     {
       "type": "P2WSH-P2PK",
@@ -478,5 +485,8 @@
       "nonWitnessUtxo": "Buffer.from('0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000', 'hex')",
       "inputIndex": 0
     }
+  },
+  "clone": {
+    "psbt": "cHNidP8BAKYCAAAAAlwKQ3suPWwEJ/zQ9sZsIioOcHKU1KoLMxlMNSXVIkEWAAAAAAD/////YYDJMap+mYgbTrCNAdpWHN+EkKvl+XYao/6co/EQfwMAAAAAAP////8CkF8BAAAAAAAWABRnPBAmVHz2HL+8/1U+QG5L2thjmjhKAAAAAAAAIgAg700yfFRyhWzQnPHIUb/XQqsjlpf4A0uw682pCVWuQ8IAAAAAAAEBKzB1AAAAAAAAIgAgth9oE4cDfC5aV58VgkW5CptHsIxppYzJV8C5kT6aTo8iAgNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06UgwRQIhALO0xRpuqP3aVkm+DPykrtqe6fPNSgNblp9K9MAwmtHJAiAHV5ZvZN8Vi49n/o9ISFyvtHsPXXPKqBxC9m2m2HlpYgEBBSMhA182CcWcQ9L2zg9j8jlt7OlaIATNDgNFMBKB1J14wzTprAABASuAOAEAAAAAACIAILYfaBOHA3wuWlefFYJFuQqbR7CMaaWMyVfAuZE+mk6PIgIDXzYJxZxD0vbOD2PyOW3s6VogBM0OA0UwEoHUnXjDNOlIMEUCIQC6XN6tpo9mYlZej4BXSSh5D1K6aILBfQ4WBWXUrISx6wIgVaxFUsz8y59xJ1V4sZ1qarHX9pZ+MJmLKbl2ZSadisoBAQUjIQNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06awAAAA="
   }
 }
diff --git a/test/psbt.js b/test/psbt.js
index 0ccd8ef..99bd9c1 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -28,7 +28,7 @@ describe(`Psbt`, () => {
       it(`Invalid: ${f.description}`, () => {
         assert.throws(() => {
           Psbt.fromBase64(f.psbt)
-        }, {message: f.errorMessage})
+        }, new RegExp(f.errorMessage))
       })
     })
 
@@ -46,7 +46,7 @@ describe(`Psbt`, () => {
         const psbt =  Psbt.fromBase64(f.psbt)
         assert.throws(() => {
           psbt.signInput(f.inputToCheck, keyPair)
-        }, {message: f.errorMessage})
+        }, new RegExp(f.errorMessage))
       })
     })
 
@@ -181,7 +181,7 @@ describe(`Psbt`, () => {
             f.shouldThrow.inputToCheck,
             ECPair.fromWIF(f.shouldThrow.WIF),
           )
-        }, {message: f.shouldThrow.errorMessage})
+        }, new RegExp(f.shouldThrow.errorMessage))
         assert.rejects(async () => {
           await psbtThatShouldThrow.signInputAsync(
             f.shouldThrow.inputToCheck,
@@ -208,7 +208,7 @@ describe(`Psbt`, () => {
             f.shouldThrow.inputToCheck,
             ECPair.fromWIF(f.shouldThrow.WIF),
           )
-        }, {message: f.shouldThrow.errorMessage})
+        }, new RegExp(f.shouldThrow.errorMessage))
         assert.throws(() => {
           psbtThatShouldThrow.signInput(
             f.shouldThrow.inputToCheck,
@@ -276,6 +276,23 @@ describe(`Psbt`, () => {
         assert.strictEqual(psbt.toBase64(), f.result)
       })
     })
+    it('fails if no script found', () => {
+      const psbt = new Psbt()
+      psbt.addInput({
+        hash: '0000000000000000000000000000000000000000000000000000000000000000',
+        index: 0
+      })
+      assert.throws(() => {
+        psbt.finalizeAllInputs()
+      }, new RegExp('No script found for input #0'))
+      psbt.addWitnessUtxoToInput(0, {
+        script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d', 'hex'),
+        value: 2e5
+      })
+      assert.throws(() => {
+        psbt.finalizeAllInputs()
+      }, new RegExp('Can not finalize input #0'))
+    })
   })
 
   describe('fromTransaction', () => {
@@ -289,9 +306,6 @@ describe(`Psbt`, () => {
 
   describe('addInput', () => {
     fixtures.addInput.checks.forEach(f => {
-      for (const attr of Object.keys(f.inputData)) {
-        f.inputData[attr] = f.inputData[attr]
-      }
       it(f.description, () => {
         const psbt = new Psbt()
 
@@ -299,13 +313,14 @@ describe(`Psbt`, () => {
           assert.throws(() => {
             psbt.addInput(f.inputData)
           }, new RegExp(f.exception))
+          assert.throws(() => {
+            psbt.addInputs([f.inputData])
+          }, new RegExp(f.exception))
         } else {
           assert.doesNotThrow(() => {
-            psbt.addInput(f.inputData)
+            psbt.addInputs([f.inputData])
             if (f.equals) {
               assert.strictEqual(psbt.toBase64(), f.equals)
-            } else {
-              console.log(psbt.toBase64())
             }
           })
           assert.throws(() => {
@@ -318,9 +333,6 @@ describe(`Psbt`, () => {
 
   describe('addOutput', () => {
     fixtures.addOutput.checks.forEach(f => {
-      for (const attr of Object.keys(f.outputData)) {
-        f.outputData[attr] = f.outputData[attr]
-      }
       it(f.description, () => {
         const psbt = new Psbt()
 
@@ -328,10 +340,15 @@ describe(`Psbt`, () => {
           assert.throws(() => {
             psbt.addOutput(f.outputData)
           }, new RegExp(f.exception))
+          assert.throws(() => {
+            psbt.addOutputs([f.outputData])
+          }, new RegExp(f.exception))
         } else {
           assert.doesNotThrow(() => {
             psbt.addOutput(f.outputData)
-            console.log(psbt.toBase64())
+          })
+          assert.doesNotThrow(() => {
+            psbt.addOutputs([f.outputData])
           })
         }
       })
@@ -381,7 +398,26 @@ describe(`Psbt`, () => {
 
       assert.throws(() => {
         psbt.setSequence(1, 0)
-      }, {message: 'Input index too high'})
+      }, new RegExp('Input index too high'))
+    })
+  })
+
+  describe('clone', () => {
+    it('Should clone a psbt exactly with no reference', () => {
+      const f = fixtures.clone
+      const psbt = Psbt.fromBase64(f.psbt)
+      const notAClone = Object.assign(new Psbt(), psbt) // references still active
+      const clone = psbt.clone()
+
+      assert.strictEqual(psbt.validateAllSignatures(), true)
+
+      assert.strictEqual(clone.toBase64(), psbt.toBase64())
+      assert.strictEqual(clone.toBase64(), notAClone.toBase64())
+      assert.strictEqual(psbt.toBase64(), notAClone.toBase64())
+      psbt.globalMap.unsignedTx[3] = 0xff
+      assert.notStrictEqual(clone.toBase64(), psbt.toBase64())
+      assert.notStrictEqual(clone.toBase64(), notAClone.toBase64())
+      assert.strictEqual(psbt.toBase64(), notAClone.toBase64())
     })
   })
 
@@ -427,6 +463,8 @@ describe(`Psbt`, () => {
       psbt.finalizeAllInputs()
 
       assert.strictEqual(psbt.getFeeRate(), f.fee)
+      psbt.__CACHE.__FEE_RATE = undefined
+      assert.strictEqual(psbt.getFeeRate(), f.fee)
     })
   })
 

From 2f1609b9189d877810a80d40a4c8e6653f440434 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 11 Jul 2019 11:28:09 +0900
Subject: [PATCH 098/111] Fix: P2WPKH was signing with nonWitnessUtxo

---
 src/psbt.js             | 23 +++++++++++++----------
 test/fixtures/psbt.json |  2 +-
 ts_src/psbt.ts          | 25 +++++++++++++++----------
 3 files changed, 29 insertions(+), 21 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index c7e8b55..752714f 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -404,6 +404,7 @@ const isP2MS = isPaymentFactory(payments.p2ms);
 const isP2PK = isPaymentFactory(payments.p2pk);
 const isP2PKH = isPaymentFactory(payments.p2pkh);
 const isP2WPKH = isPaymentFactory(payments.p2wpkh);
+const isP2WSHScript = isPaymentFactory(payments.p2wsh);
 function check32Bit(num) {
   if (
     typeof num !== 'number' ||
@@ -611,19 +612,16 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) {
       // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
       checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
       script = input.redeemScript;
-      hash = unsignedTx.hashForSignature(
-        inputIndex,
-        input.redeemScript,
-        sighashType,
-      );
     } else {
       script = prevout.script;
-      hash = unsignedTx.hashForSignature(
-        inputIndex,
-        prevout.script,
-        sighashType,
+    }
+    if (isP2WPKH(script) || isP2WSHScript(script)) {
+      throw new Error(
+        `Input #${inputIndex} has nonWitnessUtxo but segwit script: ` +
+          `${script.toString('hex')}`,
       );
     }
+    hash = unsignedTx.hashForSignature(inputIndex, script, sighashType);
   } else if (input.witnessUtxo) {
     let _script; // so we don't shadow the `let script` above
     if (input.redeemScript) {
@@ -647,7 +645,7 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) {
         sighashType,
       );
       script = _script;
-    } else {
+    } else if (isP2WSHScript(_script)) {
       if (!input.witnessScript)
         throw new Error('Segwit input needs witnessScript if not P2WPKH');
       checkWitnessScript(inputIndex, _script, input.witnessScript);
@@ -659,6 +657,11 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) {
       );
       // want to make sure the script we return is the actual meaningful script
       script = input.witnessScript;
+    } else {
+      throw new Error(
+        `Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
+          `${_script.toString('hex')}`,
+      );
     }
   } else {
     throw new Error('Need a Utxo input item for signing');
diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 61ab114..fbad821 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -121,7 +121,7 @@
     "failSignChecks": [
       {
         "description": "A Witness UTXO is provided for a non-witness input",
-        "errorMessage": "Segwit input needs witnessScript if not P2WPKH",
+        "errorMessage": "Input #0 has witnessUtxo but non-segwit script",
         "psbt": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEBItPf9QUAAAAAGXapFNSO0xELlAFMsRS9Mtb00GbcdCVriKwAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=",
         "inputToCheck": 0
       },
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 8fb1e97..928ca04 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -509,6 +509,7 @@ const isP2MS = isPaymentFactory(payments.p2ms);
 const isP2PK = isPaymentFactory(payments.p2pk);
 const isP2PKH = isPaymentFactory(payments.p2pkh);
 const isP2WPKH = isPaymentFactory(payments.p2wpkh);
+const isP2WSHScript = isPaymentFactory(payments.p2wsh);
 
 function check32Bit(num: number): void {
   if (
@@ -764,19 +765,18 @@ function getHashForSig(
       // If a redeemScript is provided, the scriptPubKey must be for that redeemScript
       checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
       script = input.redeemScript;
-      hash = unsignedTx.hashForSignature(
-        inputIndex,
-        input.redeemScript,
-        sighashType,
-      );
     } else {
       script = prevout.script;
-      hash = unsignedTx.hashForSignature(
-        inputIndex,
-        prevout.script,
-        sighashType,
+    }
+
+    if (isP2WPKH(script) || isP2WSHScript(script)) {
+      throw new Error(
+        `Input #${inputIndex} has nonWitnessUtxo but segwit script: ` +
+          `${script.toString('hex')}`,
       );
     }
+
+    hash = unsignedTx.hashForSignature(inputIndex, script, sighashType);
   } else if (input.witnessUtxo) {
     let _script: Buffer; // so we don't shadow the `let script` above
     if (input.redeemScript) {
@@ -800,7 +800,7 @@ function getHashForSig(
         sighashType,
       );
       script = _script;
-    } else {
+    } else if (isP2WSHScript(_script)) {
       if (!input.witnessScript)
         throw new Error('Segwit input needs witnessScript if not P2WPKH');
       checkWitnessScript(inputIndex, _script, input.witnessScript);
@@ -812,6 +812,11 @@ function getHashForSig(
       );
       // want to make sure the script we return is the actual meaningful script
       script = input.witnessScript;
+    } else {
+      throw new Error(
+        `Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
+          `${_script.toString('hex')}`,
+      );
     }
   } else {
     throw new Error('Need a Utxo input item for signing');

From 1feef9569c60f5c7eaf0072a8fd79a81e2cc44b7 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 11 Jul 2019 17:17:49 +0900
Subject: [PATCH 099/111] Composition over inheritance

---
 package-lock.json |   6 +-
 package.json      |   2 +-
 src/psbt.js       | 199 ++++++++++++++++++++++++---------
 test/psbt.js      |  12 +-
 ts_src/psbt.ts    | 277 +++++++++++++++++++++++++++++++++++-----------
 types/psbt.d.ts   |  36 +++++-
 6 files changed, 402 insertions(+), 130 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index fce68c4..fabd361 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -200,9 +200,9 @@
       }
     },
     "bip174": {
-      "version": "0.0.14",
-      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.14.tgz",
-      "integrity": "sha512-v9cre0W4ZpAJS1v18WUJLE9yKdSZyenGpZBg7CXiZ5n35JPXganH92d4Yk8WXpRfbFZ4SMXTqKLEgpLPX1TmcA=="
+      "version": "0.0.15",
+      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.15.tgz",
+      "integrity": "sha512-mK/s9p7i+PG7W2s2cAedNVk1NDZQn9wAoq1DlsS2+1zz5TXR3TRTzqRqm9BQtOXwbkxkhfLwlmsOjuiRdAYkdg=="
     },
     "bip32": {
       "version": "2.0.3",
diff --git a/package.json b/package.json
index 03e6d5f..a32021a 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
-    "bip174": "0.0.14",
+    "bip174": "0.0.15",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",
diff --git a/src/psbt.js b/src/psbt.js
index 752714f..578e17c 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -15,9 +15,9 @@ const DEFAULT_OPTS = {
   network: networks_1.bitcoin,
   maximumFeeRate: 5000,
 };
-class Psbt extends bip174_1.Psbt {
-  constructor(opts = {}) {
-    super();
+class Psbt {
+  constructor(opts = {}, data = new bip174_1.Psbt()) {
+    this.data = data;
     this.__CACHE = {
       __NON_WITNESS_UTXO_TX_CACHE: [],
       __NON_WITNESS_UTXO_BUF_CACHE: [],
@@ -27,11 +27,11 @@ class Psbt extends bip174_1.Psbt {
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     const c = this.__CACHE;
-    c.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx);
-    this.setVersion(2);
+    c.__TX = transaction_1.Transaction.fromBuffer(data.globalMap.unsignedTx);
+    if (this.data.inputs.length === 0) this.setVersion(2);
     // set cache
-    delete this.globalMap.unsignedTx;
-    Object.defineProperty(this.globalMap, 'unsignedTx', {
+    delete data.globalMap.unsignedTx;
+    Object.defineProperty(data.globalMap, 'unsignedTx', {
       enumerable: true,
       get() {
         const buf = c.__TX_BUF_CACHE;
@@ -42,8 +42,8 @@ class Psbt extends bip174_1.Psbt {
           return c.__TX_BUF_CACHE;
         }
       },
-      set(data) {
-        c.__TX_BUF_CACHE = data;
+      set(_data) {
+        c.__TX_BUF_CACHE = _data;
       },
     });
     // Make data hidden when enumerating
@@ -55,29 +55,38 @@ class Psbt extends bip174_1.Psbt {
     dpew(this, '__CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
-  static fromTransaction(txBuf) {
+  static fromTransaction(txBuf, opts = {}) {
     const tx = transaction_1.Transaction.fromBuffer(txBuf);
     checkTxEmpty(tx);
-    const psbt = new this();
+    const psbtBase = new bip174_1.Psbt();
+    const psbt = new Psbt(opts, psbtBase);
     psbt.__CACHE.__TX = tx;
     checkTxForDupeIns(tx, psbt.__CACHE);
     let inputCount = tx.ins.length;
     let outputCount = tx.outs.length;
     while (inputCount > 0) {
-      psbt.inputs.push({
-        keyVals: [],
+      psbtBase.inputs.push({
+        unknownKeyVals: [],
       });
       inputCount--;
     }
     while (outputCount > 0) {
-      psbt.outputs.push({
-        keyVals: [],
+      psbtBase.outputs.push({
+        unknownKeyVals: [],
       });
       outputCount--;
     }
     return psbt;
   }
-  static fromBuffer(buffer) {
+  static fromBase64(data, opts = {}) {
+    const buffer = Buffer.from(data, 'base64');
+    return this.fromBuffer(buffer, opts);
+  }
+  static fromHex(data, opts = {}) {
+    const buffer = Buffer.from(data, 'hex');
+    return this.fromBuffer(buffer, opts);
+  }
+  static fromBuffer(buffer, opts = {}) {
     let tx;
     const txCountGetter = txBuf => {
       tx = transaction_1.Transaction.fromBuffer(txBuf);
@@ -87,17 +96,22 @@ class Psbt extends bip174_1.Psbt {
         outputCount: tx.outs.length,
       };
     };
-    const psbt = super.fromBuffer(buffer, txCountGetter);
+    const psbtBase = bip174_1.Psbt.fromBuffer(buffer, txCountGetter);
+    const psbt = new Psbt(opts, psbtBase);
     psbt.__CACHE.__TX = tx;
     checkTxForDupeIns(tx, psbt.__CACHE);
     return psbt;
   }
   get inputCount() {
-    return this.inputs.length;
+    return this.data.inputs.length;
+  }
+  combine(...those) {
+    this.data.combine(...those.map(o => o.data));
+    return this;
   }
   clone() {
     // TODO: more efficient cloning
-    const res = Psbt.fromBuffer(this.toBuffer());
+    const res = Psbt.fromBuffer(this.data.toBuffer());
     res.opts = JSON.parse(JSON.stringify(this.opts));
     return res;
   }
@@ -107,7 +121,7 @@ class Psbt extends bip174_1.Psbt {
   }
   setVersion(version) {
     check32Bit(version);
-    checkInputsForPartialSig(this.inputs, 'setVersion');
+    checkInputsForPartialSig(this.data.inputs, 'setVersion');
     const c = this.__CACHE;
     c.__TX.version = version;
     c.__TX_BUF_CACHE = undefined;
@@ -116,7 +130,7 @@ class Psbt extends bip174_1.Psbt {
   }
   setLocktime(locktime) {
     check32Bit(locktime);
-    checkInputsForPartialSig(this.inputs, 'setLocktime');
+    checkInputsForPartialSig(this.data.inputs, 'setLocktime');
     const c = this.__CACHE;
     c.__TX.locktime = locktime;
     c.__TX_BUF_CACHE = undefined;
@@ -125,7 +139,7 @@ class Psbt extends bip174_1.Psbt {
   }
   setSequence(inputIndex, sequence) {
     check32Bit(sequence);
-    checkInputsForPartialSig(this.inputs, 'setSequence');
+    checkInputsForPartialSig(this.data.inputs, 'setSequence');
     const c = this.__CACHE;
     if (c.__TX.ins.length <= inputIndex) {
       throw new Error('Input index too high');
@@ -140,10 +154,15 @@ class Psbt extends bip174_1.Psbt {
     return this;
   }
   addInput(inputData) {
-    checkInputsForPartialSig(this.inputs, 'addInput');
+    checkInputsForPartialSig(this.data.inputs, 'addInput');
     const c = this.__CACHE;
     const inputAdder = getInputAdder(c);
-    super.addInput(inputData, inputAdder);
+    this.data.addInput(inputData, inputAdder);
+    const inputIndex = this.data.inputs.length - 1;
+    const input = this.data.inputs[inputIndex];
+    if (input.nonWitnessUtxo) {
+      addNonWitnessTxCache(this.__CACHE, input, inputIndex);
+    }
     c.__FEE_RATE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
@@ -153,7 +172,7 @@ class Psbt extends bip174_1.Psbt {
     return this;
   }
   addOutput(outputData) {
-    checkInputsForPartialSig(this.inputs, 'addOutput');
+    checkInputsForPartialSig(this.data.inputs, 'addOutput');
     const { address } = outputData;
     if (typeof address === 'string') {
       const { network } = this.opts;
@@ -162,30 +181,24 @@ class Psbt extends bip174_1.Psbt {
     }
     const c = this.__CACHE;
     const outputAdder = getOutputAdder(c);
-    super.addOutput(outputData, true, outputAdder);
+    this.data.addOutput(outputData, outputAdder, true);
     c.__FEE_RATE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
   }
-  addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
-    super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
-    const input = this.inputs[inputIndex];
-    addNonWitnessTxCache(this.__CACHE, input, inputIndex);
-    return this;
-  }
   extractTransaction(disableFeeCheck) {
-    if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
+    if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized');
     const c = this.__CACHE;
     if (!disableFeeCheck) {
       checkFees(this, c, this.opts);
     }
     if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
     const tx = c.__TX.clone();
-    inputFinalizeGetAmts(this.inputs, tx, c, true);
+    inputFinalizeGetAmts(this.data.inputs, tx, c, true);
     return tx;
   }
   getFeeRate() {
-    if (!this.inputs.every(isFinalized))
+    if (!this.data.inputs.every(isFinalized))
       throw new Error('PSBT must be finalized to calculate fee rate');
     const c = this.__CACHE;
     if (c.__FEE_RATE) return c.__FEE_RATE;
@@ -197,16 +210,16 @@ class Psbt extends bip174_1.Psbt {
     } else {
       tx = c.__TX.clone();
     }
-    inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize);
+    inputFinalizeGetAmts(this.data.inputs, tx, c, mustFinalize);
     return c.__FEE_RATE;
   }
   finalizeAllInputs() {
-    utils_1.checkForInput(this.inputs, 0); // making sure we have at least one
-    range(this.inputs.length).forEach(idx => this.finalizeInput(idx));
+    utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one
+    range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx));
     return this;
   }
   finalizeInput(inputIndex) {
-    const input = utils_1.checkForInput(this.inputs, inputIndex);
+    const input = utils_1.checkForInput(this.data.inputs, inputIndex);
     const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
@@ -226,23 +239,23 @@ class Psbt extends bip174_1.Psbt {
       isP2WSH,
     );
     if (finalScriptSig)
-      this.addFinalScriptSigToInput(inputIndex, finalScriptSig);
+      this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
     if (finalScriptWitness)
-      this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
+      this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
     if (!finalScriptSig && !finalScriptWitness)
       throw new Error(`Unknown error finalizing input #${inputIndex}`);
-    this.clearFinalizedInput(inputIndex);
+    this.data.clearFinalizedInput(inputIndex);
     return this;
   }
   validateAllSignatures() {
-    utils_1.checkForInput(this.inputs, 0); // making sure we have at least one
-    const results = range(this.inputs.length).map(idx =>
+    utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one
+    const results = range(this.data.inputs.length).map(idx =>
       this.validateSignatures(idx),
     );
     return results.reduce((final, res) => res === true && final, true);
   }
   validateSignatures(inputIndex, pubkey) {
-    const input = this.inputs[inputIndex];
+    const input = this.data.inputs[inputIndex];
     const partialSig = (input || {}).partialSig;
     if (!input || !partialSig || partialSig.length < 1)
       throw new Error('No signatures to validate');
@@ -280,7 +293,7 @@ class Psbt extends bip174_1.Psbt {
     // as input information is added, then eventually
     // optimize this method.
     const results = [];
-    for (const i of range(this.inputs.length)) {
+    for (const i of range(this.data.inputs.length)) {
       try {
         this.signInput(i, keyPair, sighashTypes);
         results.push(true);
@@ -302,7 +315,7 @@ class Psbt extends bip174_1.Psbt {
       // optimize this method.
       const results = [];
       const promises = [];
-      for (const [i] of this.inputs.entries()) {
+      for (const [i] of this.data.inputs.entries()) {
         promises.push(
           this.signInputAsync(i, keyPair, sighashTypes).then(
             () => {
@@ -330,7 +343,7 @@ class Psbt extends bip174_1.Psbt {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
     const { hash, sighashType } = getHashAndSighashType(
-      this.inputs,
+      this.data.inputs,
       inputIndex,
       keyPair.publicKey,
       this.__CACHE,
@@ -340,7 +353,8 @@ class Psbt extends bip174_1.Psbt {
       pubkey: keyPair.publicKey,
       signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
     };
-    return this.addPartialSigToInput(inputIndex, partialSig);
+    this.data.addPartialSigToInput(inputIndex, partialSig);
+    return this;
   }
   signInputAsync(
     inputIndex,
@@ -351,7 +365,7 @@ class Psbt extends bip174_1.Psbt {
       if (!keyPair || !keyPair.publicKey)
         return reject(new Error('Need Signer to sign input'));
       const { hash, sighashType } = getHashAndSighashType(
-        this.inputs,
+        this.data.inputs,
         inputIndex,
         keyPair.publicKey,
         this.__CACHE,
@@ -362,11 +376,94 @@ class Psbt extends bip174_1.Psbt {
           pubkey: keyPair.publicKey,
           signature: bscript.signature.encode(signature, sighashType),
         };
-        this.addPartialSigToInput(inputIndex, partialSig);
+        this.data.addPartialSigToInput(inputIndex, partialSig);
         resolve();
       });
     });
   }
+  toBuffer() {
+    return this.data.toBuffer();
+  }
+  toHex() {
+    return this.data.toHex();
+  }
+  toBase64() {
+    return this.data.toBase64();
+  }
+  addGlobalXpubToGlobal(globalXpub) {
+    this.data.addGlobalXpubToGlobal(globalXpub);
+    return this;
+  }
+  addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
+    this.data.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
+    const input = this.data.inputs[inputIndex];
+    addNonWitnessTxCache(this.__CACHE, input, inputIndex);
+    return this;
+  }
+  addWitnessUtxoToInput(inputIndex, witnessUtxo) {
+    this.data.addWitnessUtxoToInput(inputIndex, witnessUtxo);
+    return this;
+  }
+  addPartialSigToInput(inputIndex, partialSig) {
+    this.data.addPartialSigToInput(inputIndex, partialSig);
+    return this;
+  }
+  addSighashTypeToInput(inputIndex, sighashType) {
+    this.data.addSighashTypeToInput(inputIndex, sighashType);
+    return this;
+  }
+  addRedeemScriptToInput(inputIndex, redeemScript) {
+    this.data.addRedeemScriptToInput(inputIndex, redeemScript);
+    return this;
+  }
+  addWitnessScriptToInput(inputIndex, witnessScript) {
+    this.data.addWitnessScriptToInput(inputIndex, witnessScript);
+    return this;
+  }
+  addBip32DerivationToInput(inputIndex, bip32Derivation) {
+    this.data.addBip32DerivationToInput(inputIndex, bip32Derivation);
+    return this;
+  }
+  addFinalScriptSigToInput(inputIndex, finalScriptSig) {
+    this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
+    return this;
+  }
+  addFinalScriptWitnessToInput(inputIndex, finalScriptWitness) {
+    this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
+    return this;
+  }
+  addPorCommitmentToInput(inputIndex, porCommitment) {
+    this.data.addPorCommitmentToInput(inputIndex, porCommitment);
+    return this;
+  }
+  addRedeemScriptToOutput(outputIndex, redeemScript) {
+    this.data.addRedeemScriptToOutput(outputIndex, redeemScript);
+    return this;
+  }
+  addWitnessScriptToOutput(outputIndex, witnessScript) {
+    this.data.addWitnessScriptToOutput(outputIndex, witnessScript);
+    return this;
+  }
+  addBip32DerivationToOutput(outputIndex, bip32Derivation) {
+    this.data.addBip32DerivationToOutput(outputIndex, bip32Derivation);
+    return this;
+  }
+  addUnknownKeyValToGlobal(keyVal) {
+    this.data.addUnknownKeyValToGlobal(keyVal);
+    return this;
+  }
+  addUnknownKeyValToInput(inputIndex, keyVal) {
+    this.data.addUnknownKeyValToInput(inputIndex, keyVal);
+    return this;
+  }
+  addUnknownKeyValToOutput(outputIndex, keyVal) {
+    this.data.addUnknownKeyValToOutput(outputIndex, keyVal);
+    return this;
+  }
+  clearFinalizedInput(inputIndex) {
+    this.data.clearFinalizedInput(inputIndex);
+    return this;
+  }
 }
 exports.Psbt = Psbt;
 function canFinalize(input, script, scriptType) {
diff --git a/test/psbt.js b/test/psbt.js
index 99bd9c1..1804e67 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -143,8 +143,8 @@ describe(`Psbt`, () => {
         assert.strictEqual(transaction1, f.transaction)
 
         const psbt3 =  Psbt.fromBase64(f.psbt)
-        delete psbt3.inputs[0].finalScriptSig
-        delete psbt3.inputs[0].finalScriptWitness
+        delete psbt3.data.inputs[0].finalScriptSig
+        delete psbt3.data.inputs[0].finalScriptWitness
         assert.throws(() => {
           psbt3.extractTransaction()
         }, new RegExp('Not finalized'))
@@ -414,7 +414,7 @@ describe(`Psbt`, () => {
       assert.strictEqual(clone.toBase64(), psbt.toBase64())
       assert.strictEqual(clone.toBase64(), notAClone.toBase64())
       assert.strictEqual(psbt.toBase64(), notAClone.toBase64())
-      psbt.globalMap.unsignedTx[3] = 0xff
+      psbt.data.globalMap.unsignedTx[3] = 0xff
       assert.notStrictEqual(clone.toBase64(), psbt.toBase64())
       assert.notStrictEqual(clone.toBase64(), notAClone.toBase64())
       assert.strictEqual(psbt.toBase64(), notAClone.toBase64())
@@ -542,14 +542,14 @@ describe(`Psbt`, () => {
 
       // Cache is populated
       psbt.addNonWitnessUtxoToInput(index, f.nonWitnessUtxo)
-      const value = psbt.inputs[index].nonWitnessUtxo
+      const value = psbt.data.inputs[index].nonWitnessUtxo
       assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value))
       assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(f.nonWitnessUtxo))
 
       // Cache is rebuilt from internal transaction object when cleared
-      psbt.inputs[index].nonWitnessUtxo = Buffer.from([1,2,3])
+      psbt.data.inputs[index].nonWitnessUtxo = Buffer.from([1,2,3])
       psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined
-      assert.ok(psbt.inputs[index].nonWitnessUtxo.equals(value))
+      assert.ok(psbt.data.inputs[index].nonWitnessUtxo.equals(value))
     })
   })
 })
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 928ca04..5a73031 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,11 +1,21 @@
 import { Psbt as PsbtBase } from 'bip174';
 import * as varuint from 'bip174/src/lib/converter/varint';
 import {
+  Bip32Derivation,
+  FinalScriptSig,
+  FinalScriptWitness,
+  GlobalXpub,
+  KeyValue,
   NonWitnessUtxo,
   PartialSig,
+  PorCommitment,
   PsbtInput,
+  RedeemScript,
+  SighashType,
   TransactionInput,
   TransactionOutput,
+  WitnessScript,
+  WitnessUtxo,
 } from 'bip174/src/lib/interfaces';
 import { checkForInput } from 'bip174/src/lib/utils';
 import { toOutputScript } from './address';
@@ -26,37 +36,42 @@ const DEFAULT_OPTS: PsbtOpts = {
   maximumFeeRate: 5000, // satoshi per byte
 };
 
-export class Psbt extends PsbtBase {
-  static fromTransaction<T extends typeof PsbtBase>(
-    this: T,
-    txBuf: Buffer,
-  ): InstanceType<T> {
+export class Psbt {
+  static fromTransaction(txBuf: Buffer, opts: PsbtOptsOptional = {}): Psbt {
     const tx = Transaction.fromBuffer(txBuf);
     checkTxEmpty(tx);
-    const psbt = new this() as Psbt;
+    const psbtBase = new PsbtBase();
+    const psbt = new Psbt(opts, psbtBase);
     psbt.__CACHE.__TX = tx;
     checkTxForDupeIns(tx, psbt.__CACHE);
     let inputCount = tx.ins.length;
     let outputCount = tx.outs.length;
     while (inputCount > 0) {
-      psbt.inputs.push({
-        keyVals: [],
+      psbtBase.inputs.push({
+        unknownKeyVals: [],
       });
       inputCount--;
     }
     while (outputCount > 0) {
-      psbt.outputs.push({
-        keyVals: [],
+      psbtBase.outputs.push({
+        unknownKeyVals: [],
       });
       outputCount--;
     }
-    return psbt as InstanceType<T>;
+    return psbt;
   }
 
-  static fromBuffer<T extends typeof PsbtBase>(
-    this: T,
-    buffer: Buffer,
-  ): InstanceType<T> {
+  static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt {
+    const buffer = Buffer.from(data, 'base64');
+    return this.fromBuffer(buffer, opts);
+  }
+
+  static fromHex(data: string, opts: PsbtOptsOptional = {}): Psbt {
+    const buffer = Buffer.from(data, 'hex');
+    return this.fromBuffer(buffer, opts);
+  }
+
+  static fromBuffer(buffer: Buffer, opts: PsbtOptsOptional = {}): Psbt {
     let tx: Transaction | undefined;
     const txCountGetter = (
       txBuf: Buffer,
@@ -71,10 +86,11 @@ export class Psbt extends PsbtBase {
         outputCount: tx.outs.length,
       };
     };
-    const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt;
+    const psbtBase = PsbtBase.fromBuffer(buffer, txCountGetter);
+    const psbt = new Psbt(opts, psbtBase);
     psbt.__CACHE.__TX = tx!;
     checkTxForDupeIns(tx!, psbt.__CACHE);
-    return psbt as InstanceType<T>;
+    return psbt;
   }
 
   private __CACHE: PsbtCache = {
@@ -85,17 +101,19 @@ export class Psbt extends PsbtBase {
   };
   private opts: PsbtOpts;
 
-  constructor(opts: PsbtOptsOptional = {}) {
-    super();
+  constructor(
+    opts: PsbtOptsOptional = {},
+    readonly data: PsbtBase = new PsbtBase(),
+  ) {
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     const c = this.__CACHE;
-    c.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!);
-    this.setVersion(2);
+    c.__TX = Transaction.fromBuffer(data.globalMap.unsignedTx!);
+    if (this.data.inputs.length === 0) this.setVersion(2);
 
     // set cache
-    delete this.globalMap.unsignedTx;
-    Object.defineProperty(this.globalMap, 'unsignedTx', {
+    delete data.globalMap.unsignedTx;
+    Object.defineProperty(data.globalMap, 'unsignedTx', {
       enumerable: true,
       get(): Buffer {
         const buf = c.__TX_BUF_CACHE;
@@ -106,8 +124,8 @@ export class Psbt extends PsbtBase {
           return c.__TX_BUF_CACHE;
         }
       },
-      set(data: Buffer): void {
-        c.__TX_BUF_CACHE = data;
+      set(_data: Buffer): void {
+        c.__TX_BUF_CACHE = _data;
       },
     });
 
@@ -127,12 +145,17 @@ export class Psbt extends PsbtBase {
   }
 
   get inputCount(): number {
-    return this.inputs.length;
+    return this.data.inputs.length;
+  }
+
+  combine(...those: Psbt[]): this {
+    this.data.combine(...those.map(o => o.data));
+    return this;
   }
 
   clone(): Psbt {
     // TODO: more efficient cloning
-    const res = Psbt.fromBuffer(this.toBuffer());
+    const res = Psbt.fromBuffer(this.data.toBuffer());
     res.opts = JSON.parse(JSON.stringify(this.opts));
     return res;
   }
@@ -144,7 +167,7 @@ export class Psbt extends PsbtBase {
 
   setVersion(version: number): this {
     check32Bit(version);
-    checkInputsForPartialSig(this.inputs, 'setVersion');
+    checkInputsForPartialSig(this.data.inputs, 'setVersion');
     const c = this.__CACHE;
     c.__TX.version = version;
     c.__TX_BUF_CACHE = undefined;
@@ -154,7 +177,7 @@ export class Psbt extends PsbtBase {
 
   setLocktime(locktime: number): this {
     check32Bit(locktime);
-    checkInputsForPartialSig(this.inputs, 'setLocktime');
+    checkInputsForPartialSig(this.data.inputs, 'setLocktime');
     const c = this.__CACHE;
     c.__TX.locktime = locktime;
     c.__TX_BUF_CACHE = undefined;
@@ -164,7 +187,7 @@ export class Psbt extends PsbtBase {
 
   setSequence(inputIndex: number, sequence: number): this {
     check32Bit(sequence);
-    checkInputsForPartialSig(this.inputs, 'setSequence');
+    checkInputsForPartialSig(this.data.inputs, 'setSequence');
     const c = this.__CACHE;
     if (c.__TX.ins.length <= inputIndex) {
       throw new Error('Input index too high');
@@ -181,10 +204,16 @@ export class Psbt extends PsbtBase {
   }
 
   addInput(inputData: TransactionInput): this {
-    checkInputsForPartialSig(this.inputs, 'addInput');
+    checkInputsForPartialSig(this.data.inputs, 'addInput');
     const c = this.__CACHE;
     const inputAdder = getInputAdder(c);
-    super.addInput(inputData, inputAdder);
+    this.data.addInput(inputData, inputAdder);
+
+    const inputIndex = this.data.inputs.length - 1;
+    const input = this.data.inputs[inputIndex];
+    if (input.nonWitnessUtxo) {
+      addNonWitnessTxCache(this.__CACHE, input, inputIndex);
+    }
     c.__FEE_RATE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
@@ -196,7 +225,7 @@ export class Psbt extends PsbtBase {
   }
 
   addOutput(outputData: TransactionOutput): this {
-    checkInputsForPartialSig(this.inputs, 'addOutput');
+    checkInputsForPartialSig(this.data.inputs, 'addOutput');
     const { address } = outputData as any;
     if (typeof address === 'string') {
       const { network } = this.opts;
@@ -205,36 +234,26 @@ export class Psbt extends PsbtBase {
     }
     const c = this.__CACHE;
     const outputAdder = getOutputAdder(c);
-    super.addOutput(outputData, true, outputAdder);
+    this.data.addOutput(outputData, outputAdder, true);
     c.__FEE_RATE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
   }
 
-  addNonWitnessUtxoToInput(
-    inputIndex: number,
-    nonWitnessUtxo: NonWitnessUtxo,
-  ): this {
-    super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
-    const input = this.inputs[inputIndex];
-    addNonWitnessTxCache(this.__CACHE, input, inputIndex);
-    return this;
-  }
-
   extractTransaction(disableFeeCheck?: boolean): Transaction {
-    if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
+    if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized');
     const c = this.__CACHE;
     if (!disableFeeCheck) {
       checkFees(this, c, this.opts);
     }
     if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
     const tx = c.__TX.clone();
-    inputFinalizeGetAmts(this.inputs, tx, c, true);
+    inputFinalizeGetAmts(this.data.inputs, tx, c, true);
     return tx;
   }
 
   getFeeRate(): number {
-    if (!this.inputs.every(isFinalized))
+    if (!this.data.inputs.every(isFinalized))
       throw new Error('PSBT must be finalized to calculate fee rate');
     const c = this.__CACHE;
     if (c.__FEE_RATE) return c.__FEE_RATE;
@@ -246,18 +265,18 @@ export class Psbt extends PsbtBase {
     } else {
       tx = c.__TX.clone();
     }
-    inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize);
+    inputFinalizeGetAmts(this.data.inputs, tx, c, mustFinalize);
     return c.__FEE_RATE!;
   }
 
   finalizeAllInputs(): this {
-    checkForInput(this.inputs, 0); // making sure we have at least one
-    range(this.inputs.length).forEach(idx => this.finalizeInput(idx));
+    checkForInput(this.data.inputs, 0); // making sure we have at least one
+    range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx));
     return this;
   }
 
   finalizeInput(inputIndex: number): this {
-    const input = checkForInput(this.inputs, inputIndex);
+    const input = checkForInput(this.data.inputs, inputIndex);
     const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
       inputIndex,
       input,
@@ -281,26 +300,26 @@ export class Psbt extends PsbtBase {
     );
 
     if (finalScriptSig)
-      this.addFinalScriptSigToInput(inputIndex, finalScriptSig);
+      this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
     if (finalScriptWitness)
-      this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
+      this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
     if (!finalScriptSig && !finalScriptWitness)
       throw new Error(`Unknown error finalizing input #${inputIndex}`);
 
-    this.clearFinalizedInput(inputIndex);
+    this.data.clearFinalizedInput(inputIndex);
     return this;
   }
 
   validateAllSignatures(): boolean {
-    checkForInput(this.inputs, 0); // making sure we have at least one
-    const results = range(this.inputs.length).map(idx =>
+    checkForInput(this.data.inputs, 0); // making sure we have at least one
+    const results = range(this.data.inputs.length).map(idx =>
       this.validateSignatures(idx),
     );
     return results.reduce((final, res) => res === true && final, true);
   }
 
   validateSignatures(inputIndex: number, pubkey?: Buffer): boolean {
-    const input = this.inputs[inputIndex];
+    const input = this.data.inputs[inputIndex];
     const partialSig = (input || {}).partialSig;
     if (!input || !partialSig || partialSig.length < 1)
       throw new Error('No signatures to validate');
@@ -343,7 +362,7 @@ export class Psbt extends PsbtBase {
     // as input information is added, then eventually
     // optimize this method.
     const results: boolean[] = [];
-    for (const i of range(this.inputs.length)) {
+    for (const i of range(this.data.inputs.length)) {
       try {
         this.signInput(i, keyPair, sighashTypes);
         results.push(true);
@@ -371,7 +390,7 @@ export class Psbt extends PsbtBase {
         // optimize this method.
         const results: boolean[] = [];
         const promises: Array<Promise<void>> = [];
-        for (const [i] of this.inputs.entries()) {
+        for (const [i] of this.data.inputs.entries()) {
           promises.push(
             this.signInputAsync(i, keyPair, sighashTypes).then(
               () => {
@@ -401,7 +420,7 @@ export class Psbt extends PsbtBase {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
     const { hash, sighashType } = getHashAndSighashType(
-      this.inputs,
+      this.data.inputs,
       inputIndex,
       keyPair.publicKey,
       this.__CACHE,
@@ -413,7 +432,8 @@ export class Psbt extends PsbtBase {
       signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
     };
 
-    return this.addPartialSigToInput(inputIndex, partialSig);
+    this.data.addPartialSigToInput(inputIndex, partialSig);
+    return this;
   }
 
   signInputAsync(
@@ -426,7 +446,7 @@ export class Psbt extends PsbtBase {
         if (!keyPair || !keyPair.publicKey)
           return reject(new Error('Need Signer to sign input'));
         const { hash, sighashType } = getHashAndSighashType(
-          this.inputs,
+          this.data.inputs,
           inputIndex,
           keyPair.publicKey,
           this.__CACHE,
@@ -439,12 +459,143 @@ export class Psbt extends PsbtBase {
             signature: bscript.signature.encode(signature, sighashType),
           };
 
-          this.addPartialSigToInput(inputIndex, partialSig);
+          this.data.addPartialSigToInput(inputIndex, partialSig);
           resolve();
         });
       },
     );
   }
+
+  toBuffer(): Buffer {
+    return this.data.toBuffer();
+  }
+
+  toHex(): string {
+    return this.data.toHex();
+  }
+
+  toBase64(): string {
+    return this.data.toBase64();
+  }
+
+  addGlobalXpubToGlobal(globalXpub: GlobalXpub): this {
+    this.data.addGlobalXpubToGlobal(globalXpub);
+    return this;
+  }
+
+  addNonWitnessUtxoToInput(
+    inputIndex: number,
+    nonWitnessUtxo: NonWitnessUtxo,
+  ): this {
+    this.data.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
+    const input = this.data.inputs[inputIndex];
+    addNonWitnessTxCache(this.__CACHE, input, inputIndex);
+    return this;
+  }
+
+  addWitnessUtxoToInput(inputIndex: number, witnessUtxo: WitnessUtxo): this {
+    this.data.addWitnessUtxoToInput(inputIndex, witnessUtxo);
+    return this;
+  }
+
+  addPartialSigToInput(inputIndex: number, partialSig: PartialSig): this {
+    this.data.addPartialSigToInput(inputIndex, partialSig);
+    return this;
+  }
+
+  addSighashTypeToInput(inputIndex: number, sighashType: SighashType): this {
+    this.data.addSighashTypeToInput(inputIndex, sighashType);
+    return this;
+  }
+
+  addRedeemScriptToInput(inputIndex: number, redeemScript: RedeemScript): this {
+    this.data.addRedeemScriptToInput(inputIndex, redeemScript);
+    return this;
+  }
+
+  addWitnessScriptToInput(
+    inputIndex: number,
+    witnessScript: WitnessScript,
+  ): this {
+    this.data.addWitnessScriptToInput(inputIndex, witnessScript);
+    return this;
+  }
+
+  addBip32DerivationToInput(
+    inputIndex: number,
+    bip32Derivation: Bip32Derivation,
+  ): this {
+    this.data.addBip32DerivationToInput(inputIndex, bip32Derivation);
+    return this;
+  }
+
+  addFinalScriptSigToInput(
+    inputIndex: number,
+    finalScriptSig: FinalScriptSig,
+  ): this {
+    this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
+    return this;
+  }
+
+  addFinalScriptWitnessToInput(
+    inputIndex: number,
+    finalScriptWitness: FinalScriptWitness,
+  ): this {
+    this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
+    return this;
+  }
+
+  addPorCommitmentToInput(
+    inputIndex: number,
+    porCommitment: PorCommitment,
+  ): this {
+    this.data.addPorCommitmentToInput(inputIndex, porCommitment);
+    return this;
+  }
+
+  addRedeemScriptToOutput(
+    outputIndex: number,
+    redeemScript: RedeemScript,
+  ): this {
+    this.data.addRedeemScriptToOutput(outputIndex, redeemScript);
+    return this;
+  }
+
+  addWitnessScriptToOutput(
+    outputIndex: number,
+    witnessScript: WitnessScript,
+  ): this {
+    this.data.addWitnessScriptToOutput(outputIndex, witnessScript);
+    return this;
+  }
+
+  addBip32DerivationToOutput(
+    outputIndex: number,
+    bip32Derivation: Bip32Derivation,
+  ): this {
+    this.data.addBip32DerivationToOutput(outputIndex, bip32Derivation);
+    return this;
+  }
+
+  addUnknownKeyValToGlobal(keyVal: KeyValue): this {
+    this.data.addUnknownKeyValToGlobal(keyVal);
+    return this;
+  }
+
+  addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this {
+    this.data.addUnknownKeyValToInput(inputIndex, keyVal);
+    return this;
+  }
+
+  addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this {
+    this.data.addUnknownKeyValToOutput(outputIndex, keyVal);
+    return this;
+  }
+
+  clearFinalizedInput(inputIndex: number): this {
+    this.data.clearFinalizedInput(inputIndex);
+    return this;
+  }
 }
 
 interface PsbtCache {
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 95f6624..533b8fd 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -1,16 +1,20 @@
 /// <reference types="node" />
 import { Psbt as PsbtBase } from 'bip174';
-import { NonWitnessUtxo, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces';
+import { Bip32Derivation, FinalScriptSig, FinalScriptWitness, GlobalXpub, KeyValue, NonWitnessUtxo, PartialSig, PorCommitment, RedeemScript, SighashType, TransactionInput, TransactionOutput, WitnessScript, WitnessUtxo } from 'bip174/src/lib/interfaces';
 import { Signer, SignerAsync } from './ecpair';
 import { Network } from './networks';
 import { Transaction } from './transaction';
-export declare class Psbt extends PsbtBase {
-    static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>;
-    static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>;
+export declare class Psbt {
+    readonly data: PsbtBase;
+    static fromTransaction(txBuf: Buffer, opts?: PsbtOptsOptional): Psbt;
+    static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt;
+    static fromHex(data: string, opts?: PsbtOptsOptional): Psbt;
+    static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt;
     private __CACHE;
     private opts;
-    constructor(opts?: PsbtOptsOptional);
+    constructor(opts?: PsbtOptsOptional, data?: PsbtBase);
     readonly inputCount: number;
+    combine(...those: Psbt[]): this;
     clone(): Psbt;
     setMaximumFeeRate(satoshiPerByte: number): void;
     setVersion(version: number): this;
@@ -20,7 +24,6 @@ export declare class Psbt extends PsbtBase {
     addInput(inputData: TransactionInput): this;
     addOutputs(outputDatas: TransactionOutput[]): this;
     addOutput(outputData: TransactionOutput): this;
-    addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this;
     extractTransaction(disableFeeCheck?: boolean): Transaction;
     getFeeRate(): number;
     finalizeAllInputs(): this;
@@ -31,6 +34,27 @@ export declare class Psbt extends PsbtBase {
     signAsync(keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
     signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this;
     signInputAsync(inputIndex: number, keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
+    toBuffer(): Buffer;
+    toHex(): string;
+    toBase64(): string;
+    addGlobalXpubToGlobal(globalXpub: GlobalXpub): this;
+    addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this;
+    addWitnessUtxoToInput(inputIndex: number, witnessUtxo: WitnessUtxo): this;
+    addPartialSigToInput(inputIndex: number, partialSig: PartialSig): this;
+    addSighashTypeToInput(inputIndex: number, sighashType: SighashType): this;
+    addRedeemScriptToInput(inputIndex: number, redeemScript: RedeemScript): this;
+    addWitnessScriptToInput(inputIndex: number, witnessScript: WitnessScript): this;
+    addBip32DerivationToInput(inputIndex: number, bip32Derivation: Bip32Derivation): this;
+    addFinalScriptSigToInput(inputIndex: number, finalScriptSig: FinalScriptSig): this;
+    addFinalScriptWitnessToInput(inputIndex: number, finalScriptWitness: FinalScriptWitness): this;
+    addPorCommitmentToInput(inputIndex: number, porCommitment: PorCommitment): this;
+    addRedeemScriptToOutput(outputIndex: number, redeemScript: RedeemScript): this;
+    addWitnessScriptToOutput(outputIndex: number, witnessScript: WitnessScript): this;
+    addBip32DerivationToOutput(outputIndex: number, bip32Derivation: Bip32Derivation): this;
+    addUnknownKeyValToGlobal(keyVal: KeyValue): this;
+    addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this;
+    addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this;
+    clearFinalizedInput(inputIndex: number): this;
 }
 interface PsbtOptsOptional {
     network?: Network;

From f25938d3ca8abf8fcbac2074f81b629661bd1e03 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 11 Jul 2019 16:24:35 +0700
Subject: [PATCH 100/111] Test signing a non-whitelisted sighashtype

---
 test/fixtures/psbt.json |   9 +++
 test/psbt.js            | 160 +++++++++++++++++++++++-----------------
 2 files changed, 101 insertions(+), 68 deletions(-)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index fbad821..1a37031 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -427,6 +427,15 @@
           "inputToCheck": 0,
           "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
         }
+      },
+      {
+        "description": "allows signing non-whitelisted sighashtype when explicitly passed in",
+        "shouldSign": {
+          "psbt": "cHNidP8BADMBAAAAAYaq+PdOUY2PnV9kZKa82XlqrPByOqwH2TRg2+LQdqs2AAAAAAD/////AAAAAAAAAQEgAOH1BQAAAAAXqRSTNeWHqa9INvSnQ120SZeJc+6JSocBAwSBAAAAAQQWABQvLKRyDqYsPYImhD3eURpDGL10RwAA",
+          "sighashTypes": [129],
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
+        }
       }
     ]
   },
diff --git a/test/psbt.js b/test/psbt.js
index 1804e67..8ed7b51 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -167,26 +167,32 @@ describe(`Psbt`, () => {
   describe('signInputAsync', () => {
     fixtures.signInput.checks.forEach(f => {
       it(f.description, async () => {
-        const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
-        assert.doesNotReject(async () => {
-          await psbtThatShouldsign.signInputAsync(
-            f.shouldSign.inputToCheck,
-            ECPair.fromWIF(f.shouldSign.WIF),
-          )
-        })
+        if (f.shouldSign) {
+          const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+          assert.doesNotReject(async () => {
+            await psbtThatShouldsign.signInputAsync(
+              f.shouldSign.inputToCheck,
+              ECPair.fromWIF(f.shouldSign.WIF),
+              f.shouldSign.sighashTypes || undefined,
+            )
+          })
+        }
 
-        const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
-        assert.rejects(async () => {
-          await psbtThatShouldThrow.signInputAsync(
-            f.shouldThrow.inputToCheck,
-            ECPair.fromWIF(f.shouldThrow.WIF),
-          )
-        }, new RegExp(f.shouldThrow.errorMessage))
-        assert.rejects(async () => {
-          await psbtThatShouldThrow.signInputAsync(
-            f.shouldThrow.inputToCheck,
-          )
-        }, new RegExp('Need Signer to sign input'))
+        if (f.shouldThrow) {
+          const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+          assert.rejects(async () => {
+            await psbtThatShouldThrow.signInputAsync(
+              f.shouldThrow.inputToCheck,
+              ECPair.fromWIF(f.shouldThrow.WIF),
+              f.shouldThrow.sighashTypes || undefined,
+            )
+          }, new RegExp(f.shouldThrow.errorMessage))
+          assert.rejects(async () => {
+            await psbtThatShouldThrow.signInputAsync(
+              f.shouldThrow.inputToCheck,
+            )
+          }, new RegExp('Need Signer to sign input'))
+        }
       })
     })
   })
@@ -194,26 +200,32 @@ describe(`Psbt`, () => {
   describe('signInput', () => {
     fixtures.signInput.checks.forEach(f => {
       it(f.description, () => {
-        const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
-        assert.doesNotThrow(() => {
-          psbtThatShouldsign.signInput(
-            f.shouldSign.inputToCheck,
-            ECPair.fromWIF(f.shouldSign.WIF),
-          )
-        })
+        if (f.shouldSign) {
+          const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+          assert.doesNotThrow(() => {
+            psbtThatShouldsign.signInput(
+              f.shouldSign.inputToCheck,
+              ECPair.fromWIF(f.shouldSign.WIF),
+              f.shouldSign.sighashTypes || undefined,
+            )
+          })
+        }
 
-        const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
-        assert.throws(() => {
-          psbtThatShouldThrow.signInput(
-            f.shouldThrow.inputToCheck,
-            ECPair.fromWIF(f.shouldThrow.WIF),
-          )
-        }, new RegExp(f.shouldThrow.errorMessage))
-        assert.throws(() => {
-          psbtThatShouldThrow.signInput(
-            f.shouldThrow.inputToCheck,
-          )
-        }, new RegExp('Need Signer to sign input'))
+        if (f.shouldThrow) {
+          const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+          assert.throws(() => {
+            psbtThatShouldThrow.signInput(
+              f.shouldThrow.inputToCheck,
+              ECPair.fromWIF(f.shouldThrow.WIF),
+              f.shouldThrow.sighashTypes || undefined,
+            )
+          }, new RegExp(f.shouldThrow.errorMessage))
+          assert.throws(() => {
+            psbtThatShouldThrow.signInput(
+              f.shouldThrow.inputToCheck,
+            )
+          }, new RegExp('Need Signer to sign input'))
+        }
       })
     })
   })
@@ -222,22 +234,28 @@ describe(`Psbt`, () => {
     fixtures.signInput.checks.forEach(f => {
       if (f.description === 'checks the input exists') return
       it(f.description, async () => {
-        const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
-        assert.doesNotReject(async () => {
-          await psbtThatShouldsign.signAsync(
-            ECPair.fromWIF(f.shouldSign.WIF),
-          )
-        })
+        if (f.shouldSign) {
+          const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+          assert.doesNotReject(async () => {
+            await psbtThatShouldsign.signAsync(
+              ECPair.fromWIF(f.shouldSign.WIF),
+              f.shouldSign.sighashTypes || undefined,
+            )
+          })
+        }
 
-        const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
-        assert.rejects(async () => {
-          await psbtThatShouldThrow.signAsync(
-            ECPair.fromWIF(f.shouldThrow.WIF),
-          )
-        }, new RegExp('No inputs were signed'))
-        assert.rejects(async () => {
-          await psbtThatShouldThrow.signAsync()
-        }, new RegExp('Need Signer to sign input'))
+        if (f.shouldThrow) {
+          const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+          assert.rejects(async () => {
+            await psbtThatShouldThrow.signAsync(
+              ECPair.fromWIF(f.shouldThrow.WIF),
+              f.shouldThrow.sighashTypes || undefined,
+            )
+          }, new RegExp('No inputs were signed'))
+          assert.rejects(async () => {
+            await psbtThatShouldThrow.signAsync()
+          }, new RegExp('Need Signer to sign input'))
+        }
       })
     })
   })
@@ -246,22 +264,28 @@ describe(`Psbt`, () => {
     fixtures.signInput.checks.forEach(f => {
       if (f.description === 'checks the input exists') return
       it(f.description, () => {
-        const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
-        assert.doesNotThrow(() => {
-          psbtThatShouldsign.sign(
-            ECPair.fromWIF(f.shouldSign.WIF),
-          )
-        })
+        if (f.shouldSign) {
+          const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+          assert.doesNotThrow(() => {
+            psbtThatShouldsign.sign(
+              ECPair.fromWIF(f.shouldSign.WIF),
+              f.shouldSign.sighashTypes || undefined,
+            )
+          })
+        }
 
-        const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
-        assert.throws(() => {
-          psbtThatShouldThrow.sign(
-            ECPair.fromWIF(f.shouldThrow.WIF),
-          )
-        }, new RegExp('No inputs were signed'))
-        assert.throws(() => {
-          psbtThatShouldThrow.sign()
-        }, new RegExp('Need Signer to sign input'))
+        if (f.shouldThrow) {
+          const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+          assert.throws(() => {
+            psbtThatShouldThrow.sign(
+              ECPair.fromWIF(f.shouldThrow.WIF),
+              f.shouldThrow.sighashTypes || undefined,
+            )
+          }, new RegExp('No inputs were signed'))
+          assert.throws(() => {
+            psbtThatShouldThrow.sign()
+          }, new RegExp('Need Signer to sign input'))
+        }
       })
     })
   })

From d790288048eb73082fdfddb5ab4bff19e2b17987 Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 11 Jul 2019 16:25:30 +0700
Subject: [PATCH 101/111] Test the sighashtype is checked when signing

---
 test/fixtures/psbt.json | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 1a37031..a29bc8b 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -428,6 +428,15 @@
           "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
         }
       },
+      {
+        "description": "checks the sighash type",
+        "shouldThrow": {
+          "errorMessage": "Sighash type is not allowed. Retry the sign method passing the sighashTypes array of whitelisted types. Sighash type: SIGHASH_ANYONECANPAY | SIGHASH_ALL",
+          "psbt": "cHNidP8BADMBAAAAAYaq+PdOUY2PnV9kZKa82XlqrPByOqwH2TRg2+LQdqs2AAAAAAD/////AAAAAAAAAQEgAOH1BQAAAAAXqRSTNeWHqa9INvSnQ120SZeJc+6JSocBAwSBAAAAAQQWABQvLKRyDqYsPYImhD3eURpDGL10RwAA",
+          "inputToCheck": 0,
+          "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
+        }
+      },
       {
         "description": "allows signing non-whitelisted sighashtype when explicitly passed in",
         "shouldSign": {

From 71ddd656a3bc6a0416d9f4e486143aefc4110d77 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 18 Jul 2019 11:43:24 +0900
Subject: [PATCH 102/111] Modify for new BIP174 interface system

---
 package-lock.json |   5 +-
 package.json      |   2 +-
 src/psbt.js       | 241 ++++++++++++++--------------------
 test/psbt.js      |  28 ++--
 ts_src/psbt.ts    | 324 +++++++++++++++++-----------------------------
 types/psbt.d.ts   |  20 +--
 6 files changed, 231 insertions(+), 389 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index fabd361..3a7f8be 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -200,9 +200,8 @@
       }
     },
     "bip174": {
-      "version": "0.0.15",
-      "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.15.tgz",
-      "integrity": "sha512-mK/s9p7i+PG7W2s2cAedNVk1NDZQn9wAoq1DlsS2+1zz5TXR3TRTzqRqm9BQtOXwbkxkhfLwlmsOjuiRdAYkdg=="
+      "version": "git+https://github.com/bitcoinjs/bip174.git#5137e367c7a3a4e281ee01574f88977cdd4be896",
+      "from": "git+https://github.com/bitcoinjs/bip174.git#interface"
     },
     "bip32": {
       "version": "2.0.3",
diff --git a/package.json b/package.json
index a32021a..0b91e4e 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
-    "bip174": "0.0.15",
+    "bip174": "git+https://github.com/bitcoinjs/bip174.git#interface",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",
diff --git a/src/psbt.js b/src/psbt.js
index 578e17c..97e9e9d 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -16,7 +16,7 @@ const DEFAULT_OPTS = {
   maximumFeeRate: 5000,
 };
 class Psbt {
-  constructor(opts = {}, data = new bip174_1.Psbt()) {
+  constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) {
     this.data = data;
     this.__CACHE = {
       __NON_WITNESS_UTXO_TX_CACHE: [],
@@ -27,11 +27,11 @@ class Psbt {
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     const c = this.__CACHE;
-    c.__TX = transaction_1.Transaction.fromBuffer(data.globalMap.unsignedTx);
+    c.__TX = this.data.globalMap.unsignedTx.tx;
     if (this.data.inputs.length === 0) this.setVersion(2);
     // set cache
-    delete data.globalMap.unsignedTx;
-    Object.defineProperty(data.globalMap, 'unsignedTx', {
+    this.unsignedTx = Buffer.from([]);
+    Object.defineProperty(this, 'unsignedTx', {
       enumerable: true,
       get() {
         const buf = c.__TX_BUF_CACHE;
@@ -56,24 +56,20 @@ class Psbt {
     dpew(this, 'opts', false, true);
   }
   static fromTransaction(txBuf, opts = {}) {
-    const tx = transaction_1.Transaction.fromBuffer(txBuf);
-    checkTxEmpty(tx);
-    const psbtBase = new bip174_1.Psbt();
+    const tx = new PsbtTransaction(txBuf);
+    checkTxEmpty(tx.tx);
+    const psbtBase = new bip174_1.Psbt(tx);
     const psbt = new Psbt(opts, psbtBase);
-    psbt.__CACHE.__TX = tx;
-    checkTxForDupeIns(tx, psbt.__CACHE);
-    let inputCount = tx.ins.length;
-    let outputCount = tx.outs.length;
+    psbt.__CACHE.__TX = tx.tx;
+    checkTxForDupeIns(tx.tx, psbt.__CACHE);
+    let inputCount = tx.tx.ins.length;
+    let outputCount = tx.tx.outs.length;
     while (inputCount > 0) {
-      psbtBase.inputs.push({
-        unknownKeyVals: [],
-      });
+      psbtBase.inputs.push({});
       inputCount--;
     }
     while (outputCount > 0) {
-      psbtBase.outputs.push({
-        unknownKeyVals: [],
-      });
+      psbtBase.outputs.push({});
       outputCount--;
     }
     return psbt;
@@ -87,16 +83,8 @@ class Psbt {
     return this.fromBuffer(buffer, opts);
   }
   static fromBuffer(buffer, opts = {}) {
-    let tx;
-    const txCountGetter = txBuf => {
-      tx = transaction_1.Transaction.fromBuffer(txBuf);
-      checkTxEmpty(tx);
-      return {
-        inputCount: tx.ins.length,
-        outputCount: tx.outs.length,
-      };
-    };
-    const psbtBase = bip174_1.Psbt.fromBuffer(buffer, txCountGetter);
+    const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer);
+    const tx = psbtBase.globalMap.unsignedTx.tx;
     const psbt = new Psbt(opts, psbtBase);
     psbt.__CACHE.__TX = tx;
     checkTxForDupeIns(tx, psbt.__CACHE);
@@ -156,8 +144,9 @@ class Psbt {
   addInput(inputData) {
     checkInputsForPartialSig(this.data.inputs, 'addInput');
     const c = this.__CACHE;
-    const inputAdder = getInputAdder(c);
-    this.data.addInput(inputData, inputAdder);
+    this.data.addInput(inputData);
+    const txIn = c.__TX.ins[c.__TX.ins.length - 1];
+    checkTxInputCache(c, txIn);
     const inputIndex = this.data.inputs.length - 1;
     const input = this.data.inputs[inputIndex];
     if (input.nonWitnessUtxo) {
@@ -180,8 +169,7 @@ class Psbt {
       outputData = Object.assign(outputData, { script });
     }
     const c = this.__CACHE;
-    const outputAdder = getOutputAdder(c);
-    this.data.addOutput(outputData, outputAdder, true);
+    this.data.addOutput(outputData);
     c.__FEE_RATE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
@@ -238,10 +226,9 @@ class Psbt {
       isP2SH,
       isP2WSH,
     );
-    if (finalScriptSig)
-      this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
+    if (finalScriptSig) this.data.updateInput(inputIndex, { finalScriptSig });
     if (finalScriptWitness)
-      this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
+      this.data.updateInput(inputIndex, { finalScriptWitness });
     if (!finalScriptSig && !finalScriptWitness)
       throw new Error(`Unknown error finalizing input #${inputIndex}`);
     this.data.clearFinalizedInput(inputIndex);
@@ -349,11 +336,13 @@ class Psbt {
       this.__CACHE,
       sighashTypes,
     );
-    const partialSig = {
-      pubkey: keyPair.publicKey,
-      signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
-    };
-    this.data.addPartialSigToInput(inputIndex, partialSig);
+    const partialSig = [
+      {
+        pubkey: keyPair.publicKey,
+        signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
+      },
+    ];
+    this.data.updateInput(inputIndex, { partialSig });
     return this;
   }
   signInputAsync(
@@ -372,11 +361,13 @@ class Psbt {
         sighashTypes,
       );
       Promise.resolve(keyPair.sign(hash)).then(signature => {
-        const partialSig = {
-          pubkey: keyPair.publicKey,
-          signature: bscript.signature.encode(signature, sighashType),
-        };
-        this.data.addPartialSigToInput(inputIndex, partialSig);
+        const partialSig = [
+          {
+            pubkey: keyPair.publicKey,
+            signature: bscript.signature.encode(signature, sighashType),
+          },
+        ];
+        this.data.updateInput(inputIndex, { partialSig });
         resolve();
       });
     });
@@ -390,62 +381,23 @@ class Psbt {
   toBase64() {
     return this.data.toBase64();
   }
-  addGlobalXpubToGlobal(globalXpub) {
-    this.data.addGlobalXpubToGlobal(globalXpub);
+  updateGlobal(updateData) {
+    this.data.updateGlobal(updateData);
     return this;
   }
-  addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
-    this.data.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
-    const input = this.data.inputs[inputIndex];
-    addNonWitnessTxCache(this.__CACHE, input, inputIndex);
+  updateInput(inputIndex, updateData) {
+    this.data.updateInput(inputIndex, updateData);
+    if (updateData.nonWitnessUtxo) {
+      addNonWitnessTxCache(
+        this.__CACHE,
+        this.data.inputs[inputIndex],
+        inputIndex,
+      );
+    }
     return this;
   }
-  addWitnessUtxoToInput(inputIndex, witnessUtxo) {
-    this.data.addWitnessUtxoToInput(inputIndex, witnessUtxo);
-    return this;
-  }
-  addPartialSigToInput(inputIndex, partialSig) {
-    this.data.addPartialSigToInput(inputIndex, partialSig);
-    return this;
-  }
-  addSighashTypeToInput(inputIndex, sighashType) {
-    this.data.addSighashTypeToInput(inputIndex, sighashType);
-    return this;
-  }
-  addRedeemScriptToInput(inputIndex, redeemScript) {
-    this.data.addRedeemScriptToInput(inputIndex, redeemScript);
-    return this;
-  }
-  addWitnessScriptToInput(inputIndex, witnessScript) {
-    this.data.addWitnessScriptToInput(inputIndex, witnessScript);
-    return this;
-  }
-  addBip32DerivationToInput(inputIndex, bip32Derivation) {
-    this.data.addBip32DerivationToInput(inputIndex, bip32Derivation);
-    return this;
-  }
-  addFinalScriptSigToInput(inputIndex, finalScriptSig) {
-    this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
-    return this;
-  }
-  addFinalScriptWitnessToInput(inputIndex, finalScriptWitness) {
-    this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
-    return this;
-  }
-  addPorCommitmentToInput(inputIndex, porCommitment) {
-    this.data.addPorCommitmentToInput(inputIndex, porCommitment);
-    return this;
-  }
-  addRedeemScriptToOutput(outputIndex, redeemScript) {
-    this.data.addRedeemScriptToOutput(outputIndex, redeemScript);
-    return this;
-  }
-  addWitnessScriptToOutput(outputIndex, witnessScript) {
-    this.data.addWitnessScriptToOutput(outputIndex, witnessScript);
-    return this;
-  }
-  addBip32DerivationToOutput(outputIndex, bip32Derivation) {
-    this.data.addBip32DerivationToOutput(outputIndex, bip32Derivation);
+  updateOutput(outputIndex, updateData) {
+    this.data.updateOutput(outputIndex, updateData);
     return this;
   }
   addUnknownKeyValToGlobal(keyVal) {
@@ -466,6 +418,54 @@ class Psbt {
   }
 }
 exports.Psbt = Psbt;
+const transactionFromBuffer = buffer => new PsbtTransaction(buffer);
+class PsbtTransaction {
+  constructor(buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
+    this.tx = transaction_1.Transaction.fromBuffer(buffer);
+    if (this.tx.ins.some(input => input.script.length !== 0)) {
+      throw new Error('Format Error: Transaction ScriptSigs are not empty');
+    }
+    Object.defineProperty(this, 'tx', {
+      enumerable: false,
+      writable: true,
+    });
+  }
+  getInputOutputCounts() {
+    return {
+      inputCount: this.tx.ins.length,
+      outputCount: this.tx.outs.length,
+    };
+  }
+  addInput(input) {
+    if (
+      input.hash === undefined ||
+      input.index === undefined ||
+      (!Buffer.isBuffer(input.hash) && typeof input.hash !== 'string') ||
+      typeof input.index !== 'number'
+    ) {
+      throw new Error('Error adding input.');
+    }
+    const hash =
+      typeof input.hash === 'string'
+        ? bufferutils_1.reverseBuffer(Buffer.from(input.hash, 'hex'))
+        : input.hash;
+    this.tx.addInput(hash, input.index, input.sequence);
+  }
+  addOutput(output) {
+    if (
+      output.script === undefined ||
+      output.value === undefined ||
+      !Buffer.isBuffer(output.script) ||
+      typeof output.value !== 'number'
+    ) {
+      throw new Error('Error adding output.');
+    }
+    this.tx.addOutput(output.script, output.value);
+  }
+  toBuffer() {
+    return this.tx.toBuffer();
+  }
+}
 function canFinalize(input, script, scriptType) {
   switch (scriptType) {
     case 'pubkey':
@@ -769,55 +769,6 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) {
     hash,
   };
 }
-function getInputAdder(cache) {
-  const selfCache = cache;
-  return (_inputData, txBuf) => {
-    if (
-      !txBuf ||
-      _inputData.hash === undefined ||
-      _inputData.index === undefined ||
-      (!Buffer.isBuffer(_inputData.hash) &&
-        typeof _inputData.hash !== 'string') ||
-      typeof _inputData.index !== 'number'
-    ) {
-      throw new Error('Error adding input.');
-    }
-    const prevHash = Buffer.isBuffer(_inputData.hash)
-      ? _inputData.hash
-      : bufferutils_1.reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
-    // Check if input already exists in cache.
-    const input = { hash: prevHash, index: _inputData.index };
-    checkTxInputCache(selfCache, input);
-    selfCache.__TX.ins.push(
-      Object.assign({}, input, {
-        script: Buffer.alloc(0),
-        sequence:
-          _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE,
-        witness: [],
-      }),
-    );
-    return selfCache.__TX.toBuffer();
-  };
-}
-function getOutputAdder(cache) {
-  const selfCache = cache;
-  return (_outputData, txBuf) => {
-    if (
-      !txBuf ||
-      _outputData.script === undefined ||
-      _outputData.value === undefined ||
-      !Buffer.isBuffer(_outputData.script) ||
-      typeof _outputData.value !== 'number'
-    ) {
-      throw new Error('Error adding output.');
-    }
-    selfCache.__TX.outs.push({
-      script: _outputData.script,
-      value: _outputData.value,
-    });
-    return selfCache.__TX.toBuffer();
-  };
-}
 function getPayment(script, scriptType, partialSig) {
   let payment;
   switch (scriptType) {
diff --git a/test/psbt.js b/test/psbt.js
index 8ed7b51..e52fae2 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -72,20 +72,8 @@ describe(`Psbt`, () => {
           const fixtureData = f[`${inputOrOutput}Data`]
           if (fixtureData) {
             for (const [i, data] of fixtureData.entries()) {
-              const attrs = Object.keys(data)
-              for (const attr of attrs) {
-                const upperAttr = upperCaseFirstLetter(attr)
-                let adder = psbt[`add${upperAttr}To${upperCaseFirstLetter(inputOrOutput)}`]
-                if (adder !== undefined) {
-                  adder = adder.bind(psbt)
-                  const arg = data[attr]
-                  if (Array.isArray(arg)) {
-                    arg.forEach(a => adder(i, a))
-                  } else {
-                    adder(i, arg)
-                  }
-                }
-              }
+              const txt = upperCaseFirstLetter(inputOrOutput)
+              psbt[`update${txt}`](i, data)
             }
           }
         }
@@ -309,9 +297,11 @@ describe(`Psbt`, () => {
       assert.throws(() => {
         psbt.finalizeAllInputs()
       }, new RegExp('No script found for input #0'))
-      psbt.addWitnessUtxoToInput(0, {
-        script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d', 'hex'),
-        value: 2e5
+      psbt.updateInput(0, {
+        witnessUtxo: {
+          script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d', 'hex'),
+          value: 2e5
+        }
       })
       assert.throws(() => {
         psbt.finalizeAllInputs()
@@ -438,7 +428,7 @@ describe(`Psbt`, () => {
       assert.strictEqual(clone.toBase64(), psbt.toBase64())
       assert.strictEqual(clone.toBase64(), notAClone.toBase64())
       assert.strictEqual(psbt.toBase64(), notAClone.toBase64())
-      psbt.data.globalMap.unsignedTx[3] = 0xff
+      psbt.__CACHE.__TX.version |= 0xff0000
       assert.notStrictEqual(clone.toBase64(), psbt.toBase64())
       assert.notStrictEqual(clone.toBase64(), notAClone.toBase64())
       assert.strictEqual(psbt.toBase64(), notAClone.toBase64())
@@ -565,7 +555,7 @@ describe(`Psbt`, () => {
       assert.strictEqual(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index], undefined)
 
       // Cache is populated
-      psbt.addNonWitnessUtxoToInput(index, f.nonWitnessUtxo)
+      psbt.updateInput(index, { nonWitnessUtxo: f.nonWitnessUtxo })
       const value = psbt.data.inputs[index].nonWitnessUtxo
       assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value))
       assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(f.nonWitnessUtxo))
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 5a73031..0c20bc2 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,21 +1,16 @@
 import { Psbt as PsbtBase } from 'bip174';
 import * as varuint from 'bip174/src/lib/converter/varint';
 import {
-  Bip32Derivation,
-  FinalScriptSig,
-  FinalScriptWitness,
-  GlobalXpub,
   KeyValue,
-  NonWitnessUtxo,
   PartialSig,
-  PorCommitment,
+  PsbtGlobalUpdate,
   PsbtInput,
-  RedeemScript,
-  SighashType,
+  PsbtInputUpdate,
+  PsbtOutputUpdate,
+  Transaction as ITransaction,
+  TransactionFromBuffer,
   TransactionInput,
   TransactionOutput,
-  WitnessScript,
-  WitnessUtxo,
 } from 'bip174/src/lib/interfaces';
 import { checkForInput } from 'bip174/src/lib/utils';
 import { toOutputScript } from './address';
@@ -38,24 +33,20 @@ const DEFAULT_OPTS: PsbtOpts = {
 
 export class Psbt {
   static fromTransaction(txBuf: Buffer, opts: PsbtOptsOptional = {}): Psbt {
-    const tx = Transaction.fromBuffer(txBuf);
-    checkTxEmpty(tx);
-    const psbtBase = new PsbtBase();
+    const tx = new PsbtTransaction(txBuf);
+    checkTxEmpty(tx.tx);
+    const psbtBase = new PsbtBase(tx);
     const psbt = new Psbt(opts, psbtBase);
-    psbt.__CACHE.__TX = tx;
-    checkTxForDupeIns(tx, psbt.__CACHE);
-    let inputCount = tx.ins.length;
-    let outputCount = tx.outs.length;
+    psbt.__CACHE.__TX = tx.tx;
+    checkTxForDupeIns(tx.tx, psbt.__CACHE);
+    let inputCount = tx.tx.ins.length;
+    let outputCount = tx.tx.outs.length;
     while (inputCount > 0) {
-      psbtBase.inputs.push({
-        unknownKeyVals: [],
-      });
+      psbtBase.inputs.push({});
       inputCount--;
     }
     while (outputCount > 0) {
-      psbtBase.outputs.push({
-        unknownKeyVals: [],
-      });
+      psbtBase.outputs.push({});
       outputCount--;
     }
     return psbt;
@@ -72,27 +63,16 @@ export class Psbt {
   }
 
   static fromBuffer(buffer: Buffer, opts: PsbtOptsOptional = {}): Psbt {
-    let tx: Transaction | undefined;
-    const txCountGetter = (
-      txBuf: Buffer,
-    ): {
-      inputCount: number;
-      outputCount: number;
-    } => {
-      tx = Transaction.fromBuffer(txBuf);
-      checkTxEmpty(tx);
-      return {
-        inputCount: tx.ins.length,
-        outputCount: tx.outs.length,
-      };
-    };
-    const psbtBase = PsbtBase.fromBuffer(buffer, txCountGetter);
+    const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer);
+    const tx: Transaction = (psbtBase.globalMap.unsignedTx as PsbtTransaction)
+      .tx;
     const psbt = new Psbt(opts, psbtBase);
-    psbt.__CACHE.__TX = tx!;
-    checkTxForDupeIns(tx!, psbt.__CACHE);
+    psbt.__CACHE.__TX = tx;
+    checkTxForDupeIns(tx, psbt.__CACHE);
     return psbt;
   }
 
+  unsignedTx: Buffer;
   private __CACHE: PsbtCache = {
     __NON_WITNESS_UTXO_TX_CACHE: [],
     __NON_WITNESS_UTXO_BUF_CACHE: [],
@@ -103,17 +83,17 @@ export class Psbt {
 
   constructor(
     opts: PsbtOptsOptional = {},
-    readonly data: PsbtBase = new PsbtBase(),
+    readonly data: PsbtBase = new PsbtBase(new PsbtTransaction()),
   ) {
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     const c = this.__CACHE;
-    c.__TX = Transaction.fromBuffer(data.globalMap.unsignedTx!);
+    c.__TX = (this.data.globalMap.unsignedTx as PsbtTransaction).tx;
     if (this.data.inputs.length === 0) this.setVersion(2);
 
     // set cache
-    delete data.globalMap.unsignedTx;
-    Object.defineProperty(data.globalMap, 'unsignedTx', {
+    this.unsignedTx = Buffer.from([]);
+    Object.defineProperty(this, 'unsignedTx', {
       enumerable: true,
       get(): Buffer {
         const buf = c.__TX_BUF_CACHE;
@@ -206,8 +186,9 @@ export class Psbt {
   addInput(inputData: TransactionInput): this {
     checkInputsForPartialSig(this.data.inputs, 'addInput');
     const c = this.__CACHE;
-    const inputAdder = getInputAdder(c);
-    this.data.addInput(inputData, inputAdder);
+    this.data.addInput(inputData);
+    const txIn = c.__TX.ins[c.__TX.ins.length - 1];
+    checkTxInputCache(c, txIn);
 
     const inputIndex = this.data.inputs.length - 1;
     const input = this.data.inputs[inputIndex];
@@ -233,8 +214,7 @@ export class Psbt {
       outputData = Object.assign(outputData, { script });
     }
     const c = this.__CACHE;
-    const outputAdder = getOutputAdder(c);
-    this.data.addOutput(outputData, outputAdder, true);
+    this.data.addOutput(outputData);
     c.__FEE_RATE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
@@ -299,10 +279,9 @@ export class Psbt {
       isP2WSH,
     );
 
-    if (finalScriptSig)
-      this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
+    if (finalScriptSig) this.data.updateInput(inputIndex, { finalScriptSig });
     if (finalScriptWitness)
-      this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
+      this.data.updateInput(inputIndex, { finalScriptWitness });
     if (!finalScriptSig && !finalScriptWitness)
       throw new Error(`Unknown error finalizing input #${inputIndex}`);
 
@@ -427,12 +406,14 @@ export class Psbt {
       sighashTypes,
     );
 
-    const partialSig = {
-      pubkey: keyPair.publicKey,
-      signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
-    };
+    const partialSig = [
+      {
+        pubkey: keyPair.publicKey,
+        signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
+      },
+    ];
 
-    this.data.addPartialSigToInput(inputIndex, partialSig);
+    this.data.updateInput(inputIndex, { partialSig });
     return this;
   }
 
@@ -454,12 +435,14 @@ export class Psbt {
         );
 
         Promise.resolve(keyPair.sign(hash)).then(signature => {
-          const partialSig = {
-            pubkey: keyPair.publicKey,
-            signature: bscript.signature.encode(signature, sighashType),
-          };
+          const partialSig = [
+            {
+              pubkey: keyPair.publicKey,
+              signature: bscript.signature.encode(signature, sighashType),
+            },
+          ];
 
-          this.data.addPartialSigToInput(inputIndex, partialSig);
+          this.data.updateInput(inputIndex, { partialSig });
           resolve();
         });
       },
@@ -478,102 +461,25 @@ export class Psbt {
     return this.data.toBase64();
   }
 
-  addGlobalXpubToGlobal(globalXpub: GlobalXpub): this {
-    this.data.addGlobalXpubToGlobal(globalXpub);
+  updateGlobal(updateData: PsbtGlobalUpdate): this {
+    this.data.updateGlobal(updateData);
     return this;
   }
 
-  addNonWitnessUtxoToInput(
-    inputIndex: number,
-    nonWitnessUtxo: NonWitnessUtxo,
-  ): this {
-    this.data.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
-    const input = this.data.inputs[inputIndex];
-    addNonWitnessTxCache(this.__CACHE, input, inputIndex);
+  updateInput(inputIndex: number, updateData: PsbtInputUpdate): this {
+    this.data.updateInput(inputIndex, updateData);
+    if (updateData.nonWitnessUtxo) {
+      addNonWitnessTxCache(
+        this.__CACHE,
+        this.data.inputs[inputIndex],
+        inputIndex,
+      );
+    }
     return this;
   }
 
-  addWitnessUtxoToInput(inputIndex: number, witnessUtxo: WitnessUtxo): this {
-    this.data.addWitnessUtxoToInput(inputIndex, witnessUtxo);
-    return this;
-  }
-
-  addPartialSigToInput(inputIndex: number, partialSig: PartialSig): this {
-    this.data.addPartialSigToInput(inputIndex, partialSig);
-    return this;
-  }
-
-  addSighashTypeToInput(inputIndex: number, sighashType: SighashType): this {
-    this.data.addSighashTypeToInput(inputIndex, sighashType);
-    return this;
-  }
-
-  addRedeemScriptToInput(inputIndex: number, redeemScript: RedeemScript): this {
-    this.data.addRedeemScriptToInput(inputIndex, redeemScript);
-    return this;
-  }
-
-  addWitnessScriptToInput(
-    inputIndex: number,
-    witnessScript: WitnessScript,
-  ): this {
-    this.data.addWitnessScriptToInput(inputIndex, witnessScript);
-    return this;
-  }
-
-  addBip32DerivationToInput(
-    inputIndex: number,
-    bip32Derivation: Bip32Derivation,
-  ): this {
-    this.data.addBip32DerivationToInput(inputIndex, bip32Derivation);
-    return this;
-  }
-
-  addFinalScriptSigToInput(
-    inputIndex: number,
-    finalScriptSig: FinalScriptSig,
-  ): this {
-    this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
-    return this;
-  }
-
-  addFinalScriptWitnessToInput(
-    inputIndex: number,
-    finalScriptWitness: FinalScriptWitness,
-  ): this {
-    this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
-    return this;
-  }
-
-  addPorCommitmentToInput(
-    inputIndex: number,
-    porCommitment: PorCommitment,
-  ): this {
-    this.data.addPorCommitmentToInput(inputIndex, porCommitment);
-    return this;
-  }
-
-  addRedeemScriptToOutput(
-    outputIndex: number,
-    redeemScript: RedeemScript,
-  ): this {
-    this.data.addRedeemScriptToOutput(outputIndex, redeemScript);
-    return this;
-  }
-
-  addWitnessScriptToOutput(
-    outputIndex: number,
-    witnessScript: WitnessScript,
-  ): this {
-    this.data.addWitnessScriptToOutput(outputIndex, witnessScript);
-    return this;
-  }
-
-  addBip32DerivationToOutput(
-    outputIndex: number,
-    bip32Derivation: Bip32Derivation,
-  ): this {
-    this.data.addBip32DerivationToOutput(outputIndex, bip32Derivation);
+  updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this {
+    this.data.updateOutput(outputIndex, updateData);
     return this;
   }
 
@@ -618,6 +524,67 @@ interface PsbtOpts {
   maximumFeeRate: number;
 }
 
+const transactionFromBuffer: TransactionFromBuffer = (
+  buffer: Buffer,
+): ITransaction => new PsbtTransaction(buffer);
+
+class PsbtTransaction implements ITransaction {
+  tx: Transaction;
+  constructor(buffer: Buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
+    this.tx = Transaction.fromBuffer(buffer);
+    if (this.tx.ins.some(input => input.script.length !== 0)) {
+      throw new Error('Format Error: Transaction ScriptSigs are not empty');
+    }
+    Object.defineProperty(this, 'tx', {
+      enumerable: false,
+      writable: true,
+    });
+  }
+
+  getInputOutputCounts(): {
+    inputCount: number;
+    outputCount: number;
+  } {
+    return {
+      inputCount: this.tx.ins.length,
+      outputCount: this.tx.outs.length,
+    };
+  }
+
+  addInput(input: any): void {
+    if (
+      (input as any).hash === undefined ||
+      (input as any).index === undefined ||
+      (!Buffer.isBuffer((input as any).hash) &&
+        typeof (input as any).hash !== 'string') ||
+      typeof (input as any).index !== 'number'
+    ) {
+      throw new Error('Error adding input.');
+    }
+    const hash =
+      typeof input.hash === 'string'
+        ? reverseBuffer(Buffer.from(input.hash, 'hex'))
+        : input.hash;
+    this.tx.addInput(hash, input.index, input.sequence);
+  }
+
+  addOutput(output: any): void {
+    if (
+      (output as any).script === undefined ||
+      (output as any).value === undefined ||
+      !Buffer.isBuffer((output as any).script) ||
+      typeof (output as any).value !== 'number'
+    ) {
+      throw new Error('Error adding output.');
+    }
+    this.tx.addOutput(output.script, output.value);
+  }
+
+  toBuffer(): Buffer {
+    return this.tx.toBuffer();
+  }
+}
+
 function canFinalize(
   input: PsbtInput,
   script: Buffer,
@@ -979,61 +946,6 @@ function getHashForSig(
   };
 }
 
-function getInputAdder(
-  cache: PsbtCache,
-): (_inputData: TransactionInput, txBuf: Buffer) => Buffer {
-  const selfCache = cache;
-  return (_inputData: TransactionInput, txBuf: Buffer): Buffer => {
-    if (
-      !txBuf ||
-      (_inputData as any).hash === undefined ||
-      (_inputData as any).index === undefined ||
-      (!Buffer.isBuffer((_inputData as any).hash) &&
-        typeof (_inputData as any).hash !== 'string') ||
-      typeof (_inputData as any).index !== 'number'
-    ) {
-      throw new Error('Error adding input.');
-    }
-    const prevHash = Buffer.isBuffer(_inputData.hash)
-      ? _inputData.hash
-      : reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
-
-    // Check if input already exists in cache.
-    const input = { hash: prevHash, index: _inputData.index };
-    checkTxInputCache(selfCache, input);
-
-    selfCache.__TX.ins.push({
-      ...input,
-      script: Buffer.alloc(0),
-      sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE,
-      witness: [],
-    });
-    return selfCache.__TX.toBuffer();
-  };
-}
-
-function getOutputAdder(
-  cache: PsbtCache,
-): (_outputData: TransactionOutput, txBuf: Buffer) => Buffer {
-  const selfCache = cache;
-  return (_outputData: TransactionOutput, txBuf: Buffer): Buffer => {
-    if (
-      !txBuf ||
-      (_outputData as any).script === undefined ||
-      (_outputData as any).value === undefined ||
-      !Buffer.isBuffer((_outputData as any).script) ||
-      typeof (_outputData as any).value !== 'number'
-    ) {
-      throw new Error('Error adding output.');
-    }
-    selfCache.__TX.outs.push({
-      script: (_outputData as any).script!,
-      value: _outputData.value,
-    });
-    return selfCache.__TX.toBuffer();
-  };
-}
-
 function getPayment(
   script: Buffer,
   scriptType: string,
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 533b8fd..e537656 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -1,6 +1,6 @@
 /// <reference types="node" />
 import { Psbt as PsbtBase } from 'bip174';
-import { Bip32Derivation, FinalScriptSig, FinalScriptWitness, GlobalXpub, KeyValue, NonWitnessUtxo, PartialSig, PorCommitment, RedeemScript, SighashType, TransactionInput, TransactionOutput, WitnessScript, WitnessUtxo } from 'bip174/src/lib/interfaces';
+import { KeyValue, PsbtGlobalUpdate, PsbtInputUpdate, PsbtOutputUpdate, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces';
 import { Signer, SignerAsync } from './ecpair';
 import { Network } from './networks';
 import { Transaction } from './transaction';
@@ -10,6 +10,7 @@ export declare class Psbt {
     static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt;
     static fromHex(data: string, opts?: PsbtOptsOptional): Psbt;
     static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt;
+    unsignedTx: Buffer;
     private __CACHE;
     private opts;
     constructor(opts?: PsbtOptsOptional, data?: PsbtBase);
@@ -37,20 +38,9 @@ export declare class Psbt {
     toBuffer(): Buffer;
     toHex(): string;
     toBase64(): string;
-    addGlobalXpubToGlobal(globalXpub: GlobalXpub): this;
-    addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this;
-    addWitnessUtxoToInput(inputIndex: number, witnessUtxo: WitnessUtxo): this;
-    addPartialSigToInput(inputIndex: number, partialSig: PartialSig): this;
-    addSighashTypeToInput(inputIndex: number, sighashType: SighashType): this;
-    addRedeemScriptToInput(inputIndex: number, redeemScript: RedeemScript): this;
-    addWitnessScriptToInput(inputIndex: number, witnessScript: WitnessScript): this;
-    addBip32DerivationToInput(inputIndex: number, bip32Derivation: Bip32Derivation): this;
-    addFinalScriptSigToInput(inputIndex: number, finalScriptSig: FinalScriptSig): this;
-    addFinalScriptWitnessToInput(inputIndex: number, finalScriptWitness: FinalScriptWitness): this;
-    addPorCommitmentToInput(inputIndex: number, porCommitment: PorCommitment): this;
-    addRedeemScriptToOutput(outputIndex: number, redeemScript: RedeemScript): this;
-    addWitnessScriptToOutput(outputIndex: number, witnessScript: WitnessScript): this;
-    addBip32DerivationToOutput(outputIndex: number, bip32Derivation: Bip32Derivation): this;
+    updateGlobal(updateData: PsbtGlobalUpdate): this;
+    updateInput(inputIndex: number, updateData: PsbtInputUpdate): this;
+    updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this;
     addUnknownKeyValToGlobal(keyVal: KeyValue): this;
     addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this;
     addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this;

From 19a33f7da868a65434c9dc486478f732ff771faf Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 18 Jul 2019 14:20:44 +0900
Subject: [PATCH 103/111] Add comments and remove fromTransaction

---
 src/psbt.js             | 76 +++++++++++++++++++++++++++-----------
 test/fixtures/psbt.json |  6 ---
 test/psbt.js            | 14 -------
 ts_src/psbt.ts          | 81 ++++++++++++++++++++++++++++-------------
 types/psbt.d.ts         | 37 +++++++++++++++++--
 5 files changed, 144 insertions(+), 70 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 97e9e9d..a3191b4 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -11,10 +11,54 @@ const networks_1 = require('./networks');
 const payments = require('./payments');
 const bscript = require('./script');
 const transaction_1 = require('./transaction');
+/**
+ * These are the default arguments for a Psbt instance.
+ */
 const DEFAULT_OPTS = {
+  /**
+   * A bitcoinjs Network object. This is only used if you pass an `address`
+   * parameter to addOutput. Otherwise it is not needed and can be left default.
+   */
   network: networks_1.bitcoin,
+  /**
+   * When extractTransaction is called, the fee rate is checked.
+   * THIS IS NOT TO BE RELIED ON.
+   * It is only here as a last ditch effort to prevent sending a 500 BTC fee etc.
+   */
   maximumFeeRate: 5000,
 };
+/**
+ * Psbt class can parse and generate a PSBT binary based off of the BIP174.
+ * There are 6 roles that this class fulfills. (Explained in BIP174)
+ *
+ * Creator: This can be done with `new Psbt()`
+ * Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`,
+ *   `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to
+ *   add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`,
+ *   `psbt.updateInput(itemObject)`, `psbt.updateOutput(itemObject)`
+ *   addInput requires hash: Buffer | string; and index: number; as attributes
+ *   and can also include any attributes that are used in updateInput method.
+ *   addOutput requires script: Buffer; and value: number; and likewise can include
+ *   data for updateOutput.
+ *   For a list of what attributes should be what types. Check the bip174 library.
+ *   Also, check the integration tests for some examples of usage.
+ * Signer: There are a few methods. sign and signAsync, which will search all input
+ *   information for your pubkey or pubkeyhash, and only sign inputs where it finds
+ *   your info. Or you can explicitly sign a specific input with signInput and
+ *   signInputAsync. For the async methods you can create a SignerAsync object
+ *   and use something like a hardware wallet to sign with. (You must implement this)
+ * Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)`
+ *   the psbt calling combine will always have precedence when a conflict occurs.
+ *   Combine checks if the internal bitcoin transaction is the same, so be sure that
+ *   all sequences, version, locktime, etc. are the same before combining.
+ * Input Finalizer: This role is fairly important. Not only does it need to construct
+ *   the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
+ *   Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()`
+ *   Running any finalize method will delete any data in the input(s) that are no longer
+ *   needed due to the finalized scripts containing the information.
+ * Transaction Extractor: This role will perform some checks before returning a
+ *   Transaction object. Such as fee rate not being larger than maximumFeeRate etc.
+ */
 class Psbt {
   constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) {
     this.data = data;
@@ -55,25 +99,6 @@ class Psbt {
     dpew(this, '__CACHE', false, true);
     dpew(this, 'opts', false, true);
   }
-  static fromTransaction(txBuf, opts = {}) {
-    const tx = new PsbtTransaction(txBuf);
-    checkTxEmpty(tx.tx);
-    const psbtBase = new bip174_1.Psbt(tx);
-    const psbt = new Psbt(opts, psbtBase);
-    psbt.__CACHE.__TX = tx.tx;
-    checkTxForDupeIns(tx.tx, psbt.__CACHE);
-    let inputCount = tx.tx.ins.length;
-    let outputCount = tx.tx.outs.length;
-    while (inputCount > 0) {
-      psbtBase.inputs.push({});
-      inputCount--;
-    }
-    while (outputCount > 0) {
-      psbtBase.outputs.push({});
-      outputCount--;
-    }
-    return psbt;
-  }
   static fromBase64(data, opts = {}) {
     const buffer = Buffer.from(data, 'base64');
     return this.fromBuffer(buffer, opts);
@@ -418,13 +443,20 @@ class Psbt {
   }
 }
 exports.Psbt = Psbt;
+/**
+ * This function is needed to pass to the bip174 base class's fromBuffer.
+ * It takes the "transaction buffer" portion of the psbt buffer and returns a
+ * Transaction (From the bip174 library) interface.
+ */
 const transactionFromBuffer = buffer => new PsbtTransaction(buffer);
+/**
+ * This class implements the Transaction interface from bip174 library.
+ * It contains a bitcoinjs-lib Transaction object.
+ */
 class PsbtTransaction {
   constructor(buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
     this.tx = transaction_1.Transaction.fromBuffer(buffer);
-    if (this.tx.ins.some(input => input.script.length !== 0)) {
-      throw new Error('Format Error: Transaction ScriptSigs are not empty');
-    }
+    checkTxEmpty(this.tx);
     Object.defineProperty(this, 'tx', {
       enumerable: false,
       writable: true,
diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index a29bc8b..8d83e9d 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -480,12 +480,6 @@
       "result": "cHNidP8BAKYCAAAAAlwKQ3suPWwEJ/zQ9sZsIioOcHKU1KoLMxlMNSXVIkEWAAAAAAD/////YYDJMap+mYgbTrCNAdpWHN+EkKvl+XYao/6co/EQfwMAAAAAAP////8CkF8BAAAAAAAWABRnPBAmVHz2HL+8/1U+QG5L2thjmjhKAAAAAAAAIgAg700yfFRyhWzQnPHIUb/XQqsjlpf4A0uw682pCVWuQ8IAAAAAAAEBKzB1AAAAAAAAIgAgth9oE4cDfC5aV58VgkW5CptHsIxppYzJV8C5kT6aTo8BCG4CSDBFAiEAs7TFGm6o/dpWSb4M/KSu2p7p881KA1uWn0r0wDCa0ckCIAdXlm9k3xWLj2f+j0hIXK+0ew9dc8qoHEL2babYeWliASMhA182CcWcQ9L2zg9j8jlt7OlaIATNDgNFMBKB1J14wzTprAABASuAOAEAAAAAACIAILYfaBOHA3wuWlefFYJFuQqbR7CMaaWMyVfAuZE+mk6PAQhuAkgwRQIhALpc3q2mj2ZiVl6PgFdJKHkPUrpogsF9DhYFZdSshLHrAiBVrEVSzPzLn3EnVXixnWpqsdf2ln4wmYspuXZlJp2KygEjIQNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06awAAAA="
     }
   ],
-  "fromTransaction": [
-    {
-      "transaction": "020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000",
-      "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA="
-    }
-  ],
   "validateSignatures": {
     "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
     "index": 0,
diff --git a/test/psbt.js b/test/psbt.js
index e52fae2..1837760 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -309,15 +309,6 @@ describe(`Psbt`, () => {
     })
   })
 
-  describe('fromTransaction', () => {
-    fixtures.fromTransaction.forEach(f => {
-      it('Creates the expected PSBT from a transaction buffer', () => {
-        const psbt = Psbt.fromTransaction(Buffer.from(f.transaction, 'hex'))
-        assert.strictEqual(psbt.toBase64(), f.result)
-      })
-    })
-  })
-
   describe('addInput', () => {
     fixtures.addInput.checks.forEach(f => {
       it(f.description, () => {
@@ -521,11 +512,6 @@ describe(`Psbt`, () => {
   })
 
   describe('Method return types', () => {
-    it('fromTransaction returns Psbt type (not base class)', () => {
-      const psbt = Psbt.fromTransaction(Buffer.from([2,0,0,0,0,0,0,0,0,0]));
-      assert.strictEqual(psbt instanceof Psbt, true);
-      assert.ok(psbt.__CACHE.__TX);
-    })
     it('fromBuffer returns Psbt type (not base class)', () => {
       const psbt = Psbt.fromBuffer(Buffer.from(
         '70736274ff01000a01000000000000000000000000', 'hex' //cHNidP8BAAoBAAAAAAAAAAAAAAAA
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 0c20bc2..97f615f 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -26,32 +26,56 @@ import * as payments from './payments';
 import * as bscript from './script';
 import { Output, Transaction } from './transaction';
 
+/**
+ * These are the default arguments for a Psbt instance.
+ */
 const DEFAULT_OPTS: PsbtOpts = {
+  /**
+   * A bitcoinjs Network object. This is only used if you pass an `address`
+   * parameter to addOutput. Otherwise it is not needed and can be left default.
+   */
   network: btcNetwork,
+  /**
+   * When extractTransaction is called, the fee rate is checked.
+   * THIS IS NOT TO BE RELIED ON.
+   * It is only here as a last ditch effort to prevent sending a 500 BTC fee etc.
+   */
   maximumFeeRate: 5000, // satoshi per byte
 };
 
+/**
+ * Psbt class can parse and generate a PSBT binary based off of the BIP174.
+ * There are 6 roles that this class fulfills. (Explained in BIP174)
+ *
+ * Creator: This can be done with `new Psbt()`
+ * Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`,
+ *   `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to
+ *   add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`,
+ *   `psbt.updateInput(itemObject)`, `psbt.updateOutput(itemObject)`
+ *   addInput requires hash: Buffer | string; and index: number; as attributes
+ *   and can also include any attributes that are used in updateInput method.
+ *   addOutput requires script: Buffer; and value: number; and likewise can include
+ *   data for updateOutput.
+ *   For a list of what attributes should be what types. Check the bip174 library.
+ *   Also, check the integration tests for some examples of usage.
+ * Signer: There are a few methods. sign and signAsync, which will search all input
+ *   information for your pubkey or pubkeyhash, and only sign inputs where it finds
+ *   your info. Or you can explicitly sign a specific input with signInput and
+ *   signInputAsync. For the async methods you can create a SignerAsync object
+ *   and use something like a hardware wallet to sign with. (You must implement this)
+ * Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)`
+ *   the psbt calling combine will always have precedence when a conflict occurs.
+ *   Combine checks if the internal bitcoin transaction is the same, so be sure that
+ *   all sequences, version, locktime, etc. are the same before combining.
+ * Input Finalizer: This role is fairly important. Not only does it need to construct
+ *   the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
+ *   Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()`
+ *   Running any finalize method will delete any data in the input(s) that are no longer
+ *   needed due to the finalized scripts containing the information.
+ * Transaction Extractor: This role will perform some checks before returning a
+ *   Transaction object. Such as fee rate not being larger than maximumFeeRate etc.
+ */
 export class Psbt {
-  static fromTransaction(txBuf: Buffer, opts: PsbtOptsOptional = {}): Psbt {
-    const tx = new PsbtTransaction(txBuf);
-    checkTxEmpty(tx.tx);
-    const psbtBase = new PsbtBase(tx);
-    const psbt = new Psbt(opts, psbtBase);
-    psbt.__CACHE.__TX = tx.tx;
-    checkTxForDupeIns(tx.tx, psbt.__CACHE);
-    let inputCount = tx.tx.ins.length;
-    let outputCount = tx.tx.outs.length;
-    while (inputCount > 0) {
-      psbtBase.inputs.push({});
-      inputCount--;
-    }
-    while (outputCount > 0) {
-      psbtBase.outputs.push({});
-      outputCount--;
-    }
-    return psbt;
-  }
-
   static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt {
     const buffer = Buffer.from(data, 'base64');
     return this.fromBuffer(buffer, opts);
@@ -356,7 +380,7 @@ export class Psbt {
   }
 
   signAsync(
-    keyPair: SignerAsync,
+    keyPair: Signer | SignerAsync,
     sighashTypes: number[] = [Transaction.SIGHASH_ALL],
   ): Promise<void> {
     return new Promise(
@@ -419,7 +443,7 @@ export class Psbt {
 
   signInputAsync(
     inputIndex: number,
-    keyPair: SignerAsync,
+    keyPair: Signer | SignerAsync,
     sighashTypes: number[] = [Transaction.SIGHASH_ALL],
   ): Promise<void> {
     return new Promise(
@@ -524,17 +548,24 @@ interface PsbtOpts {
   maximumFeeRate: number;
 }
 
+/**
+ * This function is needed to pass to the bip174 base class's fromBuffer.
+ * It takes the "transaction buffer" portion of the psbt buffer and returns a
+ * Transaction (From the bip174 library) interface.
+ */
 const transactionFromBuffer: TransactionFromBuffer = (
   buffer: Buffer,
 ): ITransaction => new PsbtTransaction(buffer);
 
+/**
+ * This class implements the Transaction interface from bip174 library.
+ * It contains a bitcoinjs-lib Transaction object.
+ */
 class PsbtTransaction implements ITransaction {
   tx: Transaction;
   constructor(buffer: Buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
     this.tx = Transaction.fromBuffer(buffer);
-    if (this.tx.ins.some(input => input.script.length !== 0)) {
-      throw new Error('Format Error: Transaction ScriptSigs are not empty');
-    }
+    checkTxEmpty(this.tx);
     Object.defineProperty(this, 'tx', {
       enumerable: false,
       writable: true,
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index e537656..c3a12d0 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -4,9 +4,40 @@ import { KeyValue, PsbtGlobalUpdate, PsbtInputUpdate, PsbtOutputUpdate, Transact
 import { Signer, SignerAsync } from './ecpair';
 import { Network } from './networks';
 import { Transaction } from './transaction';
+/**
+ * Psbt class can parse and generate a PSBT binary based off of the BIP174.
+ * There are 6 roles that this class fulfills. (Explained in BIP174)
+ *
+ * Creator: This can be done with `new Psbt()`
+ * Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`,
+ *   `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to
+ *   add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`,
+ *   `psbt.updateInput(itemObject)`, `psbt.updateOutput(itemObject)`
+ *   addInput requires hash: Buffer | string; and index: number; as attributes
+ *   and can also include any attributes that are used in updateInput method.
+ *   addOutput requires script: Buffer; and value: number; and likewise can include
+ *   data for updateOutput.
+ *   For a list of what attributes should be what types. Check the bip174 library.
+ *   Also, check the integration tests for some examples of usage.
+ * Signer: There are a few methods. sign and signAsync, which will search all input
+ *   information for your pubkey or pubkeyhash, and only sign inputs where it finds
+ *   your info. Or you can explicitly sign a specific input with signInput and
+ *   signInputAsync. For the async methods you can create a SignerAsync object
+ *   and use something like a hardware wallet to sign with. (You must implement this)
+ * Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)`
+ *   the psbt calling combine will always have precedence when a conflict occurs.
+ *   Combine checks if the internal bitcoin transaction is the same, so be sure that
+ *   all sequences, version, locktime, etc. are the same before combining.
+ * Input Finalizer: This role is fairly important. Not only does it need to construct
+ *   the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
+ *   Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()`
+ *   Running any finalize method will delete any data in the input(s) that are no longer
+ *   needed due to the finalized scripts containing the information.
+ * Transaction Extractor: This role will perform some checks before returning a
+ *   Transaction object. Such as fee rate not being larger than maximumFeeRate etc.
+ */
 export declare class Psbt {
     readonly data: PsbtBase;
-    static fromTransaction(txBuf: Buffer, opts?: PsbtOptsOptional): Psbt;
     static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt;
     static fromHex(data: string, opts?: PsbtOptsOptional): Psbt;
     static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt;
@@ -32,9 +63,9 @@ export declare class Psbt {
     validateAllSignatures(): boolean;
     validateSignatures(inputIndex: number, pubkey?: Buffer): boolean;
     sign(keyPair: Signer, sighashTypes?: number[]): this;
-    signAsync(keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
+    signAsync(keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>;
     signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this;
-    signInputAsync(inputIndex: number, keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
+    signInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>;
     toBuffer(): Buffer;
     toHex(): string;
     toBase64(): string;

From def2182eaf862ef1d9e7a7bcc9e2333b390fad16 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Thu, 18 Jul 2019 15:57:00 +0900
Subject: [PATCH 104/111] Fix: integration test comments

---
 test/integration/transactions-psbt.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js
index 96ae073..7a3a85e 100644
--- a/test/integration/transactions-psbt.js
+++ b/test/integration/transactions-psbt.js
@@ -48,7 +48,9 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       //   value: 90000,
       // },
 
-      // Not featured here: redeemScript. A Buffer of the redeemScript
+      // Not featured here:
+      //   redeemScript. A Buffer of the redeemScript for P2SH
+      //   witnessScript. A Buffer of the witnessScript for P2WSH
     });
     psbt.addOutput({
       address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp',
@@ -149,8 +151,6 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
     // This step it new. Since we separate the signing operation and
     // the creation of the scriptSig and witness stack, we are able to
     psbt.finalizeAllInputs();
-    // it returns an array of the success of each input, also a result attribute
-    // which is true if all array items are true.
 
     // build and broadcast our RegTest network
     await regtestUtils.broadcast(psbt.extractTransaction().toHex());

From 1326e0cc4286b3d6f25f50fc67edd3826505d52b Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 19 Jul 2019 10:12:20 +0900
Subject: [PATCH 105/111] Remove the cached buffer getter

---
 src/psbt.js     | 20 --------------------
 ts_src/psbt.ts  | 23 -----------------------
 types/psbt.d.ts |  1 -
 3 files changed, 44 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index a3191b4..dab6dbc 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -73,23 +73,6 @@ class Psbt {
     const c = this.__CACHE;
     c.__TX = this.data.globalMap.unsignedTx.tx;
     if (this.data.inputs.length === 0) this.setVersion(2);
-    // set cache
-    this.unsignedTx = Buffer.from([]);
-    Object.defineProperty(this, 'unsignedTx', {
-      enumerable: true,
-      get() {
-        const buf = c.__TX_BUF_CACHE;
-        if (buf !== undefined) {
-          return buf;
-        } else {
-          c.__TX_BUF_CACHE = c.__TX.toBuffer();
-          return c.__TX_BUF_CACHE;
-        }
-      },
-      set(_data) {
-        c.__TX_BUF_CACHE = _data;
-      },
-    });
     // Make data hidden when enumerating
     const dpew = (obj, attr, enumerable, writable) =>
       Object.defineProperty(obj, attr, {
@@ -137,7 +120,6 @@ class Psbt {
     checkInputsForPartialSig(this.data.inputs, 'setVersion');
     const c = this.__CACHE;
     c.__TX.version = version;
-    c.__TX_BUF_CACHE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
   }
@@ -146,7 +128,6 @@ class Psbt {
     checkInputsForPartialSig(this.data.inputs, 'setLocktime');
     const c = this.__CACHE;
     c.__TX.locktime = locktime;
-    c.__TX_BUF_CACHE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
   }
@@ -158,7 +139,6 @@ class Psbt {
       throw new Error('Input index too high');
     }
     c.__TX.ins[inputIndex].sequence = sequence;
-    c.__TX_BUF_CACHE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
   }
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 97f615f..14dca4b 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -96,7 +96,6 @@ export class Psbt {
     return psbt;
   }
 
-  unsignedTx: Buffer;
   private __CACHE: PsbtCache = {
     __NON_WITNESS_UTXO_TX_CACHE: [],
     __NON_WITNESS_UTXO_BUF_CACHE: [],
@@ -115,24 +114,6 @@ export class Psbt {
     c.__TX = (this.data.globalMap.unsignedTx as PsbtTransaction).tx;
     if (this.data.inputs.length === 0) this.setVersion(2);
 
-    // set cache
-    this.unsignedTx = Buffer.from([]);
-    Object.defineProperty(this, 'unsignedTx', {
-      enumerable: true,
-      get(): Buffer {
-        const buf = c.__TX_BUF_CACHE;
-        if (buf !== undefined) {
-          return buf;
-        } else {
-          c.__TX_BUF_CACHE = c.__TX.toBuffer();
-          return c.__TX_BUF_CACHE;
-        }
-      },
-      set(_data: Buffer): void {
-        c.__TX_BUF_CACHE = _data;
-      },
-    });
-
     // Make data hidden when enumerating
     const dpew = (
       obj: any,
@@ -174,7 +155,6 @@ export class Psbt {
     checkInputsForPartialSig(this.data.inputs, 'setVersion');
     const c = this.__CACHE;
     c.__TX.version = version;
-    c.__TX_BUF_CACHE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
   }
@@ -184,7 +164,6 @@ export class Psbt {
     checkInputsForPartialSig(this.data.inputs, 'setLocktime');
     const c = this.__CACHE;
     c.__TX.locktime = locktime;
-    c.__TX_BUF_CACHE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
   }
@@ -197,7 +176,6 @@ export class Psbt {
       throw new Error('Input index too high');
     }
     c.__TX.ins[inputIndex].sequence = sequence;
-    c.__TX_BUF_CACHE = undefined;
     c.__EXTRACTED_TX = undefined;
     return this;
   }
@@ -533,7 +511,6 @@ interface PsbtCache {
   __NON_WITNESS_UTXO_BUF_CACHE: Buffer[];
   __TX_IN_CACHE: { [index: string]: number };
   __TX: Transaction;
-  __TX_BUF_CACHE?: Buffer;
   __FEE_RATE?: number;
   __EXTRACTED_TX?: Transaction;
 }
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index c3a12d0..2b24e65 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -41,7 +41,6 @@ export declare class Psbt {
     static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt;
     static fromHex(data: string, opts?: PsbtOptsOptional): Psbt;
     static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt;
-    unsignedTx: Buffer;
     private __CACHE;
     private opts;
     constructor(opts?: PsbtOptsOptional, data?: PsbtBase);

From 4366b621d7db918b5fa8e59db285ddfefa4f432a Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 19 Jul 2019 11:42:45 +0900
Subject: [PATCH 106/111] Add HD signer methods

---
 src/psbt.js             | 108 ++++++++++++++++++++++++++
 test/fixtures/psbt.json |  46 +++++++++++
 test/psbt.js            | 125 ++++++++++++++++++++++++++++++
 ts_src/psbt.ts          | 166 ++++++++++++++++++++++++++++++++++++++++
 types/psbt.d.ts         |  33 ++++++++
 5 files changed, 478 insertions(+)

diff --git a/src/psbt.js b/src/psbt.js
index dab6dbc..f183bbc 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -278,6 +278,86 @@ class Psbt {
     }
     return results.every(res => res === true);
   }
+  signHD(hdKeyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) {
+    if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+      throw new Error('Need HDSigner to sign input');
+    }
+    const results = [];
+    for (const i of range(this.data.inputs.length)) {
+      try {
+        this.signInputHD(i, hdKeyPair, sighashTypes);
+        results.push(true);
+      } catch (err) {
+        results.push(false);
+      }
+    }
+    if (results.every(v => v === false)) {
+      throw new Error('No inputs were signed');
+    }
+    return this;
+  }
+  signHDAsync(
+    hdKeyPair,
+    sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
+  ) {
+    return new Promise((resolve, reject) => {
+      if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+        return reject(new Error('Need HDSigner to sign input'));
+      }
+      const results = [];
+      const promises = [];
+      for (const i of range(this.data.inputs.length)) {
+        promises.push(
+          this.signInputHDAsync(i, hdKeyPair, sighashTypes).then(
+            () => {
+              results.push(true);
+            },
+            () => {
+              results.push(false);
+            },
+          ),
+        );
+      }
+      return Promise.all(promises).then(() => {
+        if (results.every(v => v === false)) {
+          return reject(new Error('No inputs were signed'));
+        }
+        resolve();
+      });
+    });
+  }
+  signInputHD(
+    inputIndex,
+    hdKeyPair,
+    sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
+  ) {
+    if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+      throw new Error('Need HDSigner to sign input');
+    }
+    const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
+    signers.forEach(signer => this.signInput(inputIndex, signer, sighashTypes));
+    return this;
+  }
+  signInputHDAsync(
+    inputIndex,
+    hdKeyPair,
+    sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
+  ) {
+    return new Promise((resolve, reject) => {
+      if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+        return reject(new Error('Need HDSigner to sign input'));
+      }
+      const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
+      const promises = signers.map(signer =>
+        this.signInputAsync(inputIndex, signer, sighashTypes),
+      );
+      return Promise.all(promises)
+        .then(() => {
+          resolve();
+        })
+        .catch(reject);
+    });
+  }
   sign(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
@@ -853,6 +933,34 @@ function getScriptFromInput(inputIndex, input, cache) {
   }
   return res;
 }
+function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
+  const input = utils_1.checkForInput(inputs, inputIndex);
+  if (!input.bip32Derivation || input.bip32Derivation.length === 0) {
+    throw new Error('Need bip32Derivation to sign with HD');
+  }
+  const myDerivations = input.bip32Derivation
+    .map(bipDv => {
+      if (bipDv.masterFingerprint.equals(hdKeyPair.fingerprint)) {
+        return bipDv;
+      } else {
+        return;
+      }
+    })
+    .filter(v => !!v);
+  if (myDerivations.length === 0) {
+    throw new Error(
+      'Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint',
+    );
+  }
+  const signers = myDerivations.map(bipDv => {
+    const node = hdKeyPair.derivePath(bipDv.path);
+    if (!bipDv.pubkey.equals(node.publicKey)) {
+      throw new Error('pubkey did not match bip32Derivation');
+    }
+    return node;
+  });
+  return signers;
+}
 function getSortedSigs(script, partialSig) {
   const p2ms = payments.p2ms({ output: script });
   // for each pubkey in order of p2ms script
diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 8d83e9d..528e922 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -448,6 +448,52 @@
       }
     ]
   },
+  "signInputHD": {
+    "checks": [
+      {
+        "description": "checks the bip32Derivation exists",
+        "shouldSign": {
+          "psbt": "cHNidP8BADMBAAAAARtEptsZNydT9Bh9A5ptwIZz87yH8NXwzr1bjJorAZEAAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFJWXNY2Vp7E5pNOey64rnhhgUlohiKwAAAAAIgYDn85vSg5rlR25fd4MOU2ANsFoO+q828zuOI/5b8tj89kYBCppsiwAAIAAAACAAAAAgAAAAAAAAAAAAAA=",
+          "inputToCheck": 0,
+          "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev"
+        },
+        "shouldThrow": {
+          "errorMessage": "Need bip32Derivation to sign with HD",
+          "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=",
+          "inputToCheck": 0,
+          "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev"
+        }
+      },
+      {
+        "description": "checks the bip32Derivation exists",
+        "shouldSign": {
+          "psbt": "cHNidP8BADMBAAAAARtEptsZNydT9Bh9A5ptwIZz87yH8NXwzr1bjJorAZEAAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFJWXNY2Vp7E5pNOey64rnhhgUlohiKwAAAAAIgYDn85vSg5rlR25fd4MOU2ANsFoO+q828zuOI/5b8tj89kYBCppsiwAAIAAAACAAAAAgAAAAAAAAAAAAAA=",
+          "inputToCheck": 0,
+          "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev"
+        },
+        "shouldThrow": {
+          "errorMessage": "Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint",
+          "psbt": "cHNidP8BADMBAAAAARtEptsZNydT9Bh9A5ptwIZz87yH8NXwzr1bjJorAZEAAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFJWXNY2Vp7E5pNOey64rnhhgUlohiKwAAAAAIgYD/85vSg5rlR25fd4MOU2ANsFoO+q828zuOI/5b8tj89kY/////ywAAIAAAACAAAAAgAAAAAAAAAAAAAA=",
+          "inputToCheck": 0,
+          "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev"
+        }
+      },
+      {
+        "description": "checks the bip32Derivation exists",
+        "shouldSign": {
+          "psbt": "cHNidP8BADMBAAAAARtEptsZNydT9Bh9A5ptwIZz87yH8NXwzr1bjJorAZEAAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFJWXNY2Vp7E5pNOey64rnhhgUlohiKwAAAAAIgYDn85vSg5rlR25fd4MOU2ANsFoO+q828zuOI/5b8tj89kYBCppsiwAAIAAAACAAAAAgAAAAAAAAAAAAAA=",
+          "inputToCheck": 0,
+          "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev"
+        },
+        "shouldThrow": {
+          "errorMessage": "pubkey did not match bip32Derivation",
+          "psbt": "cHNidP8BADMBAAAAARtEptsZNydT9Bh9A5ptwIZz87yH8NXwzr1bjJorAZEAAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFJWXNY2Vp7E5pNOey64rnhhgUlohiKwAAAAAIgYD/85vSg5rlR25fd4MOU2ANsFoO+q828zuOI/5b8tj89kYBCppsiwAAIAAAACAAAAAgAAAAAAAAAAAAAA=",
+          "inputToCheck": 0,
+          "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev"
+        }
+      }
+    ]
+  },
   "finalizeAllInputs": [
     {
       "type": "P2PK",
diff --git a/test/psbt.js b/test/psbt.js
index 1837760..18a4c99 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -1,6 +1,7 @@
 const { describe, it } = require('mocha')
 const assert = require('assert')
 
+const bip32 = require('bip32')
 const ECPair = require('../src/ecpair')
 const Psbt = require('..').Psbt
 const NETWORKS = require('../src/networks')
@@ -278,6 +279,130 @@ describe(`Psbt`, () => {
     })
   })
 
+  describe('signInputHDAsync', () => {
+    fixtures.signInputHD.checks.forEach(f => {
+      it(f.description, async () => {
+        if (f.shouldSign) {
+          const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+          assert.doesNotReject(async () => {
+            await psbtThatShouldsign.signInputHDAsync(
+              f.shouldSign.inputToCheck,
+              bip32.fromBase58(f.shouldSign.xprv),
+              f.shouldSign.sighashTypes || undefined,
+            )
+          })
+        }
+
+        if (f.shouldThrow) {
+          const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+          assert.rejects(async () => {
+            await psbtThatShouldThrow.signInputHDAsync(
+              f.shouldThrow.inputToCheck,
+              bip32.fromBase58(f.shouldThrow.xprv),
+              f.shouldThrow.sighashTypes || undefined,
+            )
+          }, new RegExp(f.shouldThrow.errorMessage))
+          assert.rejects(async () => {
+            await psbtThatShouldThrow.signInputHDAsync(
+              f.shouldThrow.inputToCheck,
+            )
+          }, new RegExp('Need HDSigner to sign input'))
+        }
+      })
+    })
+  })
+
+  describe('signInputHD', () => {
+    fixtures.signInputHD.checks.forEach(f => {
+      it(f.description, () => {
+        if (f.shouldSign) {
+          const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+          assert.doesNotThrow(() => {
+            psbtThatShouldsign.signInputHD(
+              f.shouldSign.inputToCheck,
+              bip32.fromBase58(f.shouldSign.xprv),
+              f.shouldSign.sighashTypes || undefined,
+            )
+          })
+        }
+
+        if (f.shouldThrow) {
+          const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+          assert.throws(() => {
+            psbtThatShouldThrow.signInputHD(
+              f.shouldThrow.inputToCheck,
+              bip32.fromBase58(f.shouldThrow.xprv),
+              f.shouldThrow.sighashTypes || undefined,
+            )
+          }, new RegExp(f.shouldThrow.errorMessage))
+          assert.throws(() => {
+            psbtThatShouldThrow.signInputHD(
+              f.shouldThrow.inputToCheck,
+            )
+          }, new RegExp('Need HDSigner to sign input'))
+        }
+      })
+    })
+  })
+
+  describe('signHDAsync', () => {
+    fixtures.signInputHD.checks.forEach(f => {
+      it(f.description, async () => {
+        if (f.shouldSign) {
+          const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+          assert.doesNotReject(async () => {
+            await psbtThatShouldsign.signHDAsync(
+              bip32.fromBase58(f.shouldSign.xprv),
+              f.shouldSign.sighashTypes || undefined,
+            )
+          })
+        }
+
+        if (f.shouldThrow) {
+          const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+          assert.rejects(async () => {
+            await psbtThatShouldThrow.signHDAsync(
+              bip32.fromBase58(f.shouldThrow.xprv),
+              f.shouldThrow.sighashTypes || undefined,
+            )
+          }, new RegExp('No inputs were signed'))
+          assert.rejects(async () => {
+            await psbtThatShouldThrow.signHDAsync()
+          }, new RegExp('Need HDSigner to sign input'))
+        }
+      })
+    })
+  })
+
+  describe('signHD', () => {
+    fixtures.signInputHD.checks.forEach(f => {
+      it(f.description, () => {
+        if (f.shouldSign) {
+          const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
+          assert.doesNotThrow(() => {
+            psbtThatShouldsign.signHD(
+              bip32.fromBase58(f.shouldSign.xprv),
+              f.shouldSign.sighashTypes || undefined,
+            )
+          })
+        }
+
+        if (f.shouldThrow) {
+          const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
+          assert.throws(() => {
+            psbtThatShouldThrow.signHD(
+              bip32.fromBase58(f.shouldThrow.xprv),
+              f.shouldThrow.sighashTypes || undefined,
+            )
+          }, new RegExp('No inputs were signed'))
+          assert.throws(() => {
+            psbtThatShouldThrow.signHD()
+          }, new RegExp('Need HDSigner to sign input'))
+        }
+      })
+    })
+  })
+
   describe('finalizeAllInputs', () => {
     fixtures.finalizeAllInputs.forEach(f => {
       it(`Finalizes inputs of type "${f.type}"`, () => {
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 14dca4b..0778226 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -332,6 +332,107 @@ export class Psbt {
     return results.every(res => res === true);
   }
 
+  signHD(
+    hdKeyPair: HDSigner,
+    sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+  ): this {
+    if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+      throw new Error('Need HDSigner to sign input');
+    }
+
+    const results: boolean[] = [];
+    for (const i of range(this.data.inputs.length)) {
+      try {
+        this.signInputHD(i, hdKeyPair, sighashTypes);
+        results.push(true);
+      } catch (err) {
+        results.push(false);
+      }
+    }
+    if (results.every(v => v === false)) {
+      throw new Error('No inputs were signed');
+    }
+    return this;
+  }
+
+  signHDAsync(
+    hdKeyPair: HDSigner | HDSignerAsync,
+    sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+  ): Promise<void> {
+    return new Promise(
+      (resolve, reject): any => {
+        if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+          return reject(new Error('Need HDSigner to sign input'));
+        }
+
+        const results: boolean[] = [];
+        const promises: Array<Promise<void>> = [];
+        for (const i of range(this.data.inputs.length)) {
+          promises.push(
+            this.signInputHDAsync(i, hdKeyPair, sighashTypes).then(
+              () => {
+                results.push(true);
+              },
+              () => {
+                results.push(false);
+              },
+            ),
+          );
+        }
+        return Promise.all(promises).then(() => {
+          if (results.every(v => v === false)) {
+            return reject(new Error('No inputs were signed'));
+          }
+          resolve();
+        });
+      },
+    );
+  }
+
+  signInputHD(
+    inputIndex: number,
+    hdKeyPair: HDSigner,
+    sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+  ): this {
+    if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+      throw new Error('Need HDSigner to sign input');
+    }
+    const signers = getSignersFromHD(
+      inputIndex,
+      this.data.inputs,
+      hdKeyPair,
+    ) as Signer[];
+    signers.forEach(signer => this.signInput(inputIndex, signer, sighashTypes));
+    return this;
+  }
+
+  signInputHDAsync(
+    inputIndex: number,
+    hdKeyPair: HDSigner | HDSignerAsync,
+    sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+  ): Promise<void> {
+    return new Promise(
+      (resolve, reject): any => {
+        if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+          return reject(new Error('Need HDSigner to sign input'));
+        }
+        const signers = getSignersFromHD(
+          inputIndex,
+          this.data.inputs,
+          hdKeyPair,
+        );
+        const promises = signers.map(signer =>
+          this.signInputAsync(inputIndex, signer, sighashTypes),
+        );
+        return Promise.all(promises)
+          .then(() => {
+            resolve();
+          })
+          .catch(reject);
+      },
+    );
+  }
+
   sign(
     keyPair: Signer,
     sighashTypes: number[] = [Transaction.SIGHASH_ALL],
@@ -525,6 +626,38 @@ interface PsbtOpts {
   maximumFeeRate: number;
 }
 
+interface HDSignerBase {
+  /**
+   * DER format compressed publicKey buffer
+   */
+  publicKey: Buffer;
+  /**
+   * The first 4 bytes of the sha256-ripemd160 of the publicKey
+   */
+  fingerprint: Buffer;
+}
+
+interface HDSigner extends HDSignerBase {
+  /**
+   * The path string must match /^m(\/\d+'?)+$/
+   * ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations
+   */
+  derivePath(path: string): HDSigner;
+  /**
+   * Input hash (the "message digest") for the signature algorithm
+   * Return a 64 byte signature (32 byte r and 32 byte s in that order)
+   */
+  sign(hash: Buffer): Buffer;
+}
+
+/**
+ * Same as above but with async sign method
+ */
+interface HDSignerAsync extends HDSignerBase {
+  derivePath(path: string): HDSignerAsync;
+  sign(hash: Buffer): Promise<Buffer>;
+}
+
 /**
  * This function is needed to pass to the bip174 base class's fromBuffer.
  * It takes the "transaction buffer" portion of the psbt buffer and returns a
@@ -1042,6 +1175,39 @@ function getScriptFromInput(
   return res;
 }
 
+function getSignersFromHD(
+  inputIndex: number,
+  inputs: PsbtInput[],
+  hdKeyPair: HDSigner | HDSignerAsync,
+): Array<Signer | SignerAsync> {
+  const input = checkForInput(inputs, inputIndex);
+  if (!input.bip32Derivation || input.bip32Derivation.length === 0) {
+    throw new Error('Need bip32Derivation to sign with HD');
+  }
+  const myDerivations = input.bip32Derivation
+    .map(bipDv => {
+      if (bipDv.masterFingerprint.equals(hdKeyPair.fingerprint)) {
+        return bipDv;
+      } else {
+        return;
+      }
+    })
+    .filter(v => !!v);
+  if (myDerivations.length === 0) {
+    throw new Error(
+      'Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint',
+    );
+  }
+  const signers: Array<Signer | SignerAsync> = myDerivations.map(bipDv => {
+    const node = hdKeyPair.derivePath(bipDv!.path);
+    if (!bipDv!.pubkey.equals(node.publicKey)) {
+      throw new Error('pubkey did not match bip32Derivation');
+    }
+    return node;
+  });
+  return signers;
+}
+
 function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] {
   const p2ms = payments.p2ms({ output: script });
   // for each pubkey in order of p2ms script
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 2b24e65..9557f79 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -61,6 +61,10 @@ export declare class Psbt {
     finalizeInput(inputIndex: number): this;
     validateAllSignatures(): boolean;
     validateSignatures(inputIndex: number, pubkey?: Buffer): boolean;
+    signHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this;
+    signHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>;
+    signInputHD(inputIndex: number, hdKeyPair: HDSigner, sighashTypes?: number[]): this;
+    signInputHDAsync(inputIndex: number, hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>;
     sign(keyPair: Signer, sighashTypes?: number[]): this;
     signAsync(keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>;
     signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this;
@@ -80,4 +84,33 @@ interface PsbtOptsOptional {
     network?: Network;
     maximumFeeRate?: number;
 }
+interface HDSignerBase {
+    /**
+     * DER format compressed publicKey buffer
+     */
+    publicKey: Buffer;
+    /**
+     * The first 4 bytes of the sha256-ripemd160 of the publicKey
+     */
+    fingerprint: Buffer;
+}
+interface HDSigner extends HDSignerBase {
+    /**
+     * The path string must match /^m(\/\d+'?)+$/
+     * ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations
+     */
+    derivePath(path: string): HDSigner;
+    /**
+     * Input hash (the "message digest") for the signature algorithm
+     * Return a 64 byte signature (32 byte r and 32 byte s in that order)
+     */
+    sign(hash: Buffer): Buffer;
+}
+/**
+ * Same as above but with async sign method
+ */
+interface HDSignerAsync extends HDSignerBase {
+    derivePath(path: string): HDSignerAsync;
+    sign(hash: Buffer): Promise<Buffer>;
+}
 export {};

From 1c5b0025c8aedc63a201a63046e7cb7ac5712850 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 19 Jul 2019 12:03:32 +0900
Subject: [PATCH 107/111] Update integration test with HD example

---
 test/integration/transactions-psbt.js | 73 +++++++++++++++++++++++++--
 1 file changed, 69 insertions(+), 4 deletions(-)

diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js
index 7a3a85e..0e4bfe7 100644
--- a/test/integration/transactions-psbt.js
+++ b/test/integration/transactions-psbt.js
@@ -1,6 +1,8 @@
 const { describe, it } = require('mocha');
 const assert = require('assert');
 const bitcoin = require('../../');
+const bip32 = require('bip32');
+const rng = require('randombytes');
 const regtestUtils = require('./_regtest');
 const regtest = regtestUtils.network;
 
@@ -403,24 +405,87 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       value: 2e4,
     });
   });
+
+  it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input using HD', async () => {
+    const hdRoot = bip32.fromSeed(rng(64));
+    const masterFingerprint = hdRoot.fingerprint;
+    const path = "m/84'/0'/0'/0/0";
+    const childNode = hdRoot.derivePath(path);
+    const pubkey = childNode.publicKey;
+
+    // This information should be added to your input via updateInput
+    // You can add multiple bip32Derivation objects for multisig, but
+    // each must have a unique pubkey.
+    //
+    // This is useful because as long as you store the masterFingerprint on
+    // the PSBT Creator's server, you can have the PSBT Creator do the heavy
+    // lifting with derivation from your m/84'/0'/0' xpub, (deriving only 0/0 )
+    // and your signer just needs to pass in an HDSigner interface (ie. bip32 library)
+    const updateData = {
+      bip32Derivation: [
+        {
+          masterFingerprint,
+          path,
+          pubkey,
+        }
+      ]
+    }
+    const p2wpkh = createPayment('p2wpkh', [childNode]);
+    const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem');
+    {
+      const { hash, index, witnessUtxo } = inputData;
+      assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData);
+    }
+
+    // You can add extra attributes for updateData into the addInput(s) object(s)
+    Object.assign(inputData, updateData)
+
+    const psbt = new bitcoin.Psbt({ network: regtest })
+      .addInput(inputData)
+      // .updateInput(0, updateData) // if you didn't merge the bip32Derivation with inputData
+      .addOutput({
+        address: regtestUtils.RANDOM_ADDRESS,
+        value: 2e4,
+      })
+      .signInputHD(0, hdRoot); // must sign with root!!!
+
+    assert.strictEqual(psbt.validateSignatures(0), true);
+    assert.strictEqual(psbt.validateSignatures(0, childNode.publicKey), true);
+    psbt.finalizeAllInputs();
+
+    const tx = psbt.extractTransaction();
+
+    // build and broadcast to the Bitcoin RegTest network
+    await regtestUtils.broadcast(tx.toHex());
+
+    await regtestUtils.verify({
+      txId: tx.getId(),
+      address: regtestUtils.RANDOM_ADDRESS,
+      vout: 0,
+      value: 2e4,
+    });
+  });
 });
 
-function createPayment(_type, network) {
+function createPayment(_type, myKeys, network) {
   network = network || regtest;
   const splitType = _type.split('-').reverse();
   const isMultisig = splitType[0].slice(0, 4) === 'p2ms';
-  const keys = [];
+  const keys = myKeys || [];
   let m;
   if (isMultisig) {
     const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/);
     m = parseInt(match[1]);
     let n = parseInt(match[2]);
-    while (n > 1) {
+    if (keys.length > 0 && keys.length !== n) {
+      throw new Error('Need n keys for multisig')
+    }
+    while (!myKeys && n > 1) {
       keys.push(bitcoin.ECPair.makeRandom({ network }));
       n--;
     }
   }
-  keys.push(bitcoin.ECPair.makeRandom({ network }));
+  if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network }));
 
   let payment;
   splitType.forEach(type => {

From acf59f167c369cdf771ddbd06f8e8fa20220a447 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 19 Jul 2019 12:41:12 +0900
Subject: [PATCH 108/111] Use bip174@1.0.0

---
 package-lock.json | 5 +++--
 package.json      | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 3a7f8be..8e641b1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -200,8 +200,9 @@
       }
     },
     "bip174": {
-      "version": "git+https://github.com/bitcoinjs/bip174.git#5137e367c7a3a4e281ee01574f88977cdd4be896",
-      "from": "git+https://github.com/bitcoinjs/bip174.git#interface"
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/bip174/-/bip174-1.0.0.tgz",
+      "integrity": "sha512-AaoWrkYtv6A2y8H+qzs6NvRWypzNbADT8PQGpM9rnP+jLzeol+uzhe3Myeuq/dwrHYtmsW8V71HmX2oXhQGagw=="
     },
     "bip32": {
       "version": "2.0.3",
diff --git a/package.json b/package.json
index 0b91e4e..806de55 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
   "dependencies": {
     "@types/node": "10.12.18",
     "bech32": "^1.1.2",
-    "bip174": "git+https://github.com/bitcoinjs/bip174.git#interface",
+    "bip174": "^1.0.0",
     "bip32": "^2.0.3",
     "bip66": "^1.1.0",
     "bitcoin-ops": "^1.4.0",

From e19bc58b303fea38a8f7d58d9cea943495f540f1 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 19 Jul 2019 14:53:54 +0900
Subject: [PATCH 109/111] Rename methods

---
 src/psbt.js                           | 26 +++++++++++-------
 test/fixtures/psbt.json               |  2 +-
 test/integration/transactions-psbt.js | 34 ++++++++++++------------
 test/psbt.js                          | 38 +++++++++++++--------------
 ts_src/psbt.ts                        | 20 +++++++-------
 types/psbt.d.ts                       | 14 +++++-----
 6 files changed, 70 insertions(+), 64 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index f183bbc..31ac164 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -42,7 +42,7 @@ const DEFAULT_OPTS = {
  *   data for updateOutput.
  *   For a list of what attributes should be what types. Check the bip174 library.
  *   Also, check the integration tests for some examples of usage.
- * Signer: There are a few methods. sign and signAsync, which will search all input
+ * Signer: There are a few methods. signAllInputs and signAsync, which will search all input
  *   information for your pubkey or pubkeyhash, and only sign inputs where it finds
  *   your info. Or you can explicitly sign a specific input with signInput and
  *   signInputAsync. For the async methods you can create a SignerAsync object
@@ -53,7 +53,7 @@ const DEFAULT_OPTS = {
  *   all sequences, version, locktime, etc. are the same before combining.
  * Input Finalizer: This role is fairly important. Not only does it need to construct
  *   the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
- *   Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()`
+ *   Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()`
  *   Running any finalize method will delete any data in the input(s) that are no longer
  *   needed due to the finalized scripts containing the information.
  * Transaction Extractor: This role will perform some checks before returning a
@@ -131,9 +131,9 @@ class Psbt {
     c.__EXTRACTED_TX = undefined;
     return this;
   }
-  setSequence(inputIndex, sequence) {
+  setInputSequence(inputIndex, sequence) {
     check32Bit(sequence);
-    checkInputsForPartialSig(this.data.inputs, 'setSequence');
+    checkInputsForPartialSig(this.data.inputs, 'setInputSequence');
     const c = this.__CACHE;
     if (c.__TX.ins.length <= inputIndex) {
       throw new Error('Input index too high');
@@ -239,14 +239,14 @@ class Psbt {
     this.data.clearFinalizedInput(inputIndex);
     return this;
   }
-  validateAllSignatures() {
+  validateSignaturesOfAllInputs() {
     utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one
     const results = range(this.data.inputs.length).map(idx =>
-      this.validateSignatures(idx),
+      this.validateSignaturesOfInput(idx),
     );
     return results.reduce((final, res) => res === true && final, true);
   }
-  validateSignatures(inputIndex, pubkey) {
+  validateSignaturesOfInput(inputIndex, pubkey) {
     const input = this.data.inputs[inputIndex];
     const partialSig = (input || {}).partialSig;
     if (!input || !partialSig || partialSig.length < 1)
@@ -278,7 +278,10 @@ class Psbt {
     }
     return results.every(res => res === true);
   }
-  signHD(hdKeyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) {
+  signAllInputsHD(
+    hdKeyPair,
+    sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
+  ) {
     if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
       throw new Error('Need HDSigner to sign input');
     }
@@ -358,7 +361,10 @@ class Psbt {
         .catch(reject);
     });
   }
-  sign(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) {
+  signAllInputs(
+    keyPair,
+    sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
+  ) {
     if (!keyPair || !keyPair.publicKey)
       throw new Error('Need Signer to sign input');
     // TODO: Add a pubkey/pubkeyhash cache to each input
@@ -635,7 +641,7 @@ function checkInputsForPartialSig(inputs, action) {
         case transaction_1.Transaction.SIGHASH_SINGLE:
         case transaction_1.Transaction.SIGHASH_NONE:
           whitelist.push('addOutput');
-          whitelist.push('setSequence');
+          whitelist.push('setInputSequence');
           break;
       }
       if (whitelist.indexOf(action) === -1) {
diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 528e922..22655da 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -526,7 +526,7 @@
       "result": "cHNidP8BAKYCAAAAAlwKQ3suPWwEJ/zQ9sZsIioOcHKU1KoLMxlMNSXVIkEWAAAAAAD/////YYDJMap+mYgbTrCNAdpWHN+EkKvl+XYao/6co/EQfwMAAAAAAP////8CkF8BAAAAAAAWABRnPBAmVHz2HL+8/1U+QG5L2thjmjhKAAAAAAAAIgAg700yfFRyhWzQnPHIUb/XQqsjlpf4A0uw682pCVWuQ8IAAAAAAAEBKzB1AAAAAAAAIgAgth9oE4cDfC5aV58VgkW5CptHsIxppYzJV8C5kT6aTo8BCG4CSDBFAiEAs7TFGm6o/dpWSb4M/KSu2p7p881KA1uWn0r0wDCa0ckCIAdXlm9k3xWLj2f+j0hIXK+0ew9dc8qoHEL2babYeWliASMhA182CcWcQ9L2zg9j8jlt7OlaIATNDgNFMBKB1J14wzTprAABASuAOAEAAAAAACIAILYfaBOHA3wuWlefFYJFuQqbR7CMaaWMyVfAuZE+mk6PAQhuAkgwRQIhALpc3q2mj2ZiVl6PgFdJKHkPUrpogsF9DhYFZdSshLHrAiBVrEVSzPzLn3EnVXixnWpqsdf2ln4wmYspuXZlJp2KygEjIQNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06awAAAA="
     }
   ],
-  "validateSignatures": {
+  "validateSignaturesOfInput": {
     "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
     "index": 0,
     "pubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f', 'hex')",
diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js
index 0e4bfe7..07035ab 100644
--- a/test/integration/transactions-psbt.js
+++ b/test/integration/transactions-psbt.js
@@ -59,7 +59,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       value: 80000,
     });
     psbt.signInput(0, alice);
-    psbt.validateSignatures(0);
+    psbt.validateSignaturesOfInput(0);
     psbt.finalizeAllInputs();
     assert.strictEqual(
       psbt.extractTransaction().toHex(),
@@ -128,8 +128,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
     // Alice signs each input with the respective private keys
     // signInput and signInputAsync are better
     // (They take the input index explicitly as the first arg)
-    signer1.sign(alice1.keys[0]);
-    signer2.sign(alice2.keys[0]);
+    signer1.signAllInputs(alice1.keys[0]);
+    signer2.signAllInputs(alice2.keys[0]);
 
     // If your signer object's sign method returns a promise, use the following
     // await signer2.signAsync(alice2.keys[0])
@@ -147,8 +147,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
     // Finalizer wants to check all signatures are valid before finalizing.
     // If the finalizer wants to check for specific pubkeys, the second arg
     // can be passed. See the first multisig example below.
-    assert.strictEqual(psbt.validateSignatures(0), true);
-    assert.strictEqual(psbt.validateSignatures(1), true);
+    assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
+    assert.strictEqual(psbt.validateSignaturesOfInput(1), true);
 
     // This step it new. Since we separate the signing operation and
     // the creation of the scriptSig and witness stack, we are able to
@@ -183,7 +183,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       })
       .signInput(0, alice1.keys[0]);
 
-    assert.strictEqual(psbt.validateSignatures(0), true);
+    assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
     psbt.finalizeAllInputs();
 
     // build and broadcast to the RegTest network
@@ -215,13 +215,13 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       .signInput(0, multisig.keys[0])
       .signInput(0, multisig.keys[2]);
 
-    assert.strictEqual(psbt.validateSignatures(0), true);
+    assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
     assert.strictEqual(
-      psbt.validateSignatures(0, multisig.keys[0].publicKey),
+      psbt.validateSignaturesOfInput(0, multisig.keys[0].publicKey),
       true,
     );
     assert.throws(() => {
-      psbt.validateSignatures(0, multisig.keys[3].publicKey);
+      psbt.validateSignaturesOfInput(0, multisig.keys[3].publicKey);
     }, new RegExp('No signatures for this pubkey'));
     psbt.finalizeAllInputs();
 
@@ -267,7 +267,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
     const tx = new bitcoin.Psbt()
       .addInputs([inputData, inputData2])
       .addOutputs([outputData, outputData2])
-      .sign(keyPair)
+      .signAllInputs(keyPair)
       .finalizeAllInputs()
       .extractTransaction();
 
@@ -300,7 +300,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       })
       .signInput(0, p2wpkh.keys[0]);
 
-    assert.strictEqual(psbt.validateSignatures(0), true);
+    assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
     psbt.finalizeAllInputs();
 
     const tx = psbt.extractTransaction();
@@ -340,7 +340,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       })
       .signInput(0, p2wsh.keys[0]);
 
-    assert.strictEqual(psbt.validateSignatures(0), true);
+    assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
     psbt.finalizeAllInputs();
 
     const tx = psbt.extractTransaction();
@@ -383,13 +383,13 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       .signInput(0, p2sh.keys[2])
       .signInput(0, p2sh.keys[3]);
 
-    assert.strictEqual(psbt.validateSignatures(0), true);
+    assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
     assert.strictEqual(
-      psbt.validateSignatures(0, p2sh.keys[3].publicKey),
+      psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey),
       true,
     );
     assert.throws(() => {
-      psbt.validateSignatures(0, p2sh.keys[1].publicKey);
+      psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey);
     }, new RegExp('No signatures for this pubkey'));
     psbt.finalizeAllInputs();
 
@@ -449,8 +449,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
       })
       .signInputHD(0, hdRoot); // must sign with root!!!
 
-    assert.strictEqual(psbt.validateSignatures(0), true);
-    assert.strictEqual(psbt.validateSignatures(0, childNode.publicKey), true);
+    assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
+    assert.strictEqual(psbt.validateSignaturesOfInput(0, childNode.publicKey), true);
     psbt.finalizeAllInputs();
 
     const tx = psbt.extractTransaction();
diff --git a/test/psbt.js b/test/psbt.js
index 18a4c99..2b630d7 100644
--- a/test/psbt.js
+++ b/test/psbt.js
@@ -249,14 +249,14 @@ describe(`Psbt`, () => {
     })
   })
 
-  describe('sign', () => {
+  describe('signAllInputs', () => {
     fixtures.signInput.checks.forEach(f => {
       if (f.description === 'checks the input exists') return
       it(f.description, () => {
         if (f.shouldSign) {
           const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
           assert.doesNotThrow(() => {
-            psbtThatShouldsign.sign(
+            psbtThatShouldsign.signAllInputs(
               ECPair.fromWIF(f.shouldSign.WIF),
               f.shouldSign.sighashTypes || undefined,
             )
@@ -266,13 +266,13 @@ describe(`Psbt`, () => {
         if (f.shouldThrow) {
           const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
           assert.throws(() => {
-            psbtThatShouldThrow.sign(
+            psbtThatShouldThrow.signAllInputs(
               ECPair.fromWIF(f.shouldThrow.WIF),
               f.shouldThrow.sighashTypes || undefined,
             )
           }, new RegExp('No inputs were signed'))
           assert.throws(() => {
-            psbtThatShouldThrow.sign()
+            psbtThatShouldThrow.signAllInputs()
           }, new RegExp('Need Signer to sign input'))
         }
       })
@@ -374,13 +374,13 @@ describe(`Psbt`, () => {
     })
   })
 
-  describe('signHD', () => {
+  describe('signAllInputsHD', () => {
     fixtures.signInputHD.checks.forEach(f => {
       it(f.description, () => {
         if (f.shouldSign) {
           const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt)
           assert.doesNotThrow(() => {
-            psbtThatShouldsign.signHD(
+            psbtThatShouldsign.signAllInputsHD(
               bip32.fromBase58(f.shouldSign.xprv),
               f.shouldSign.sighashTypes || undefined,
             )
@@ -390,13 +390,13 @@ describe(`Psbt`, () => {
         if (f.shouldThrow) {
           const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt)
           assert.throws(() => {
-            psbtThatShouldThrow.signHD(
+            psbtThatShouldThrow.signAllInputsHD(
               bip32.fromBase58(f.shouldThrow.xprv),
               f.shouldThrow.sighashTypes || undefined,
             )
           }, new RegExp('No inputs were signed'))
           assert.throws(() => {
-            psbtThatShouldThrow.signHD()
+            psbtThatShouldThrow.signAllInputsHD()
           }, new RegExp('Need HDSigner to sign input'))
         }
       })
@@ -505,7 +505,7 @@ describe(`Psbt`, () => {
     })
   })
 
-  describe('setSequence', () => {
+  describe('setInputSequence', () => {
     it('Sets the sequence number for a given input', () => {
       const psbt = new Psbt()
       psbt.addInput({
@@ -515,7 +515,7 @@ describe(`Psbt`, () => {
 
       assert.strictEqual(psbt.inputCount, 1)
       assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0xffffffff)
-      psbt.setSequence(0, 0)
+      psbt.setInputSequence(0, 0)
       assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0)
     })
 
@@ -527,7 +527,7 @@ describe(`Psbt`, () => {
       });
 
       assert.throws(() => {
-        psbt.setSequence(1, 0)
+        psbt.setInputSequence(1, 0)
       }, new RegExp('Input index too high'))
     })
   })
@@ -539,7 +539,7 @@ describe(`Psbt`, () => {
       const notAClone = Object.assign(new Psbt(), psbt) // references still active
       const clone = psbt.clone()
 
-      assert.strictEqual(psbt.validateAllSignatures(), true)
+      assert.strictEqual(psbt.validateSignaturesOfAllInputs(), true)
 
       assert.strictEqual(clone.toBase64(), psbt.toBase64())
       assert.strictEqual(clone.toBase64(), notAClone.toBase64())
@@ -561,22 +561,22 @@ describe(`Psbt`, () => {
     })
   })
 
-  describe('validateSignatures', () => {
-    const f = fixtures.validateSignatures
+  describe('validateSignaturesOfInput', () => {
+    const f = fixtures.validateSignaturesOfInput
     it('Correctly validates a signature', () => {
       const psbt = Psbt.fromBase64(f.psbt)
 
-      assert.strictEqual(psbt.validateSignatures(f.index), true)
+      assert.strictEqual(psbt.validateSignaturesOfInput(f.index), true)
       assert.throws(() => {
-        psbt.validateSignatures(f.nonExistantIndex)
+        psbt.validateSignaturesOfInput(f.nonExistantIndex)
       }, new RegExp('No signatures to validate'))
     })
 
     it('Correctly validates a signature against a pubkey', () => {
       const psbt = Psbt.fromBase64(f.psbt)
-      assert.strictEqual(psbt.validateSignatures(f.index, f.pubkey), true)
+      assert.strictEqual(psbt.validateSignaturesOfInput(f.index, f.pubkey), true)
       assert.throws(() => {
-        psbt.validateSignatures(f.index, f.incorrectPubkey)
+        psbt.validateSignaturesOfInput(f.index, f.incorrectPubkey)
       }, new RegExp('No signatures for this pubkey'))
     })
   })
@@ -623,7 +623,7 @@ describe(`Psbt`, () => {
     assert.throws(() => {
       psbt.setVersion(3)
     }, new RegExp('Can not modify transaction, signatures exist.'))
-    psbt.validateSignatures(0)
+    psbt.validateSignaturesOfInput(0)
     psbt.finalizeAllInputs()
     assert.strictEqual(
       psbt.extractTransaction().toHex(),
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 0778226..863b331 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -58,7 +58,7 @@ const DEFAULT_OPTS: PsbtOpts = {
  *   data for updateOutput.
  *   For a list of what attributes should be what types. Check the bip174 library.
  *   Also, check the integration tests for some examples of usage.
- * Signer: There are a few methods. sign and signAsync, which will search all input
+ * Signer: There are a few methods. signAllInputs and signAsync, which will search all input
  *   information for your pubkey or pubkeyhash, and only sign inputs where it finds
  *   your info. Or you can explicitly sign a specific input with signInput and
  *   signInputAsync. For the async methods you can create a SignerAsync object
@@ -69,7 +69,7 @@ const DEFAULT_OPTS: PsbtOpts = {
  *   all sequences, version, locktime, etc. are the same before combining.
  * Input Finalizer: This role is fairly important. Not only does it need to construct
  *   the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
- *   Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()`
+ *   Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()`
  *   Running any finalize method will delete any data in the input(s) that are no longer
  *   needed due to the finalized scripts containing the information.
  * Transaction Extractor: This role will perform some checks before returning a
@@ -168,9 +168,9 @@ export class Psbt {
     return this;
   }
 
-  setSequence(inputIndex: number, sequence: number): this {
+  setInputSequence(inputIndex: number, sequence: number): this {
     check32Bit(sequence);
-    checkInputsForPartialSig(this.data.inputs, 'setSequence');
+    checkInputsForPartialSig(this.data.inputs, 'setInputSequence');
     const c = this.__CACHE;
     if (c.__TX.ins.length <= inputIndex) {
       throw new Error('Input index too high');
@@ -291,15 +291,15 @@ export class Psbt {
     return this;
   }
 
-  validateAllSignatures(): boolean {
+  validateSignaturesOfAllInputs(): boolean {
     checkForInput(this.data.inputs, 0); // making sure we have at least one
     const results = range(this.data.inputs.length).map(idx =>
-      this.validateSignatures(idx),
+      this.validateSignaturesOfInput(idx),
     );
     return results.reduce((final, res) => res === true && final, true);
   }
 
-  validateSignatures(inputIndex: number, pubkey?: Buffer): boolean {
+  validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean {
     const input = this.data.inputs[inputIndex];
     const partialSig = (input || {}).partialSig;
     if (!input || !partialSig || partialSig.length < 1)
@@ -332,7 +332,7 @@ export class Psbt {
     return results.every(res => res === true);
   }
 
-  signHD(
+  signAllInputsHD(
     hdKeyPair: HDSigner,
     sighashTypes: number[] = [Transaction.SIGHASH_ALL],
   ): this {
@@ -433,7 +433,7 @@ export class Psbt {
     );
   }
 
-  sign(
+  signAllInputs(
     keyPair: Signer,
     sighashTypes: number[] = [Transaction.SIGHASH_ALL],
   ): this {
@@ -812,7 +812,7 @@ function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
         case Transaction.SIGHASH_SINGLE:
         case Transaction.SIGHASH_NONE:
           whitelist.push('addOutput');
-          whitelist.push('setSequence');
+          whitelist.push('setInputSequence');
           break;
       }
       if (whitelist.indexOf(action) === -1) {
diff --git a/types/psbt.d.ts b/types/psbt.d.ts
index 9557f79..a133feb 100644
--- a/types/psbt.d.ts
+++ b/types/psbt.d.ts
@@ -19,7 +19,7 @@ import { Transaction } from './transaction';
  *   data for updateOutput.
  *   For a list of what attributes should be what types. Check the bip174 library.
  *   Also, check the integration tests for some examples of usage.
- * Signer: There are a few methods. sign and signAsync, which will search all input
+ * Signer: There are a few methods. signAllInputs and signAsync, which will search all input
  *   information for your pubkey or pubkeyhash, and only sign inputs where it finds
  *   your info. Or you can explicitly sign a specific input with signInput and
  *   signInputAsync. For the async methods you can create a SignerAsync object
@@ -30,7 +30,7 @@ import { Transaction } from './transaction';
  *   all sequences, version, locktime, etc. are the same before combining.
  * Input Finalizer: This role is fairly important. Not only does it need to construct
  *   the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
- *   Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()`
+ *   Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()`
  *   Running any finalize method will delete any data in the input(s) that are no longer
  *   needed due to the finalized scripts containing the information.
  * Transaction Extractor: This role will perform some checks before returning a
@@ -50,7 +50,7 @@ export declare class Psbt {
     setMaximumFeeRate(satoshiPerByte: number): void;
     setVersion(version: number): this;
     setLocktime(locktime: number): this;
-    setSequence(inputIndex: number, sequence: number): this;
+    setInputSequence(inputIndex: number, sequence: number): this;
     addInputs(inputDatas: TransactionInput[]): this;
     addInput(inputData: TransactionInput): this;
     addOutputs(outputDatas: TransactionOutput[]): this;
@@ -59,13 +59,13 @@ export declare class Psbt {
     getFeeRate(): number;
     finalizeAllInputs(): this;
     finalizeInput(inputIndex: number): this;
-    validateAllSignatures(): boolean;
-    validateSignatures(inputIndex: number, pubkey?: Buffer): boolean;
-    signHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this;
+    validateSignaturesOfAllInputs(): boolean;
+    validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean;
+    signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this;
     signHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>;
     signInputHD(inputIndex: number, hdKeyPair: HDSigner, sighashTypes?: number[]): this;
     signInputHDAsync(inputIndex: number, hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>;
-    sign(keyPair: Signer, sighashTypes?: number[]): this;
+    signAllInputs(keyPair: Signer, sighashTypes?: number[]): this;
     signAsync(keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>;
     signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this;
     signInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>;

From d05806fe699b1538aca0fb5bcab241a2f5a3f918 Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 19 Jul 2019 15:10:58 +0900
Subject: [PATCH 110/111] Update README, add deprecation warning

---
 README.md                        | 8 ++++++++
 src/transaction_builder.js       | 7 +++++++
 test/integration/cltv.js         | 1 +
 test/integration/csv.js          | 1 +
 test/integration/payments.js     | 1 +
 test/integration/transactions.js | 1 +
 test/transaction_builder.js      | 2 ++
 ts_src/transaction_builder.ts    | 7 +++++++
 8 files changed, 28 insertions(+)

diff --git a/README.md b/README.md
index 00b5816..6f46004 100644
--- a/README.md
+++ b/README.md
@@ -85,6 +85,14 @@ The below examples are implemented as integration tests, they should be very eas
 Otherwise, pull requests are appreciated.
 Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP).
 
+### Warning: Currently the tests use TransactionBuilder, which will be removed in the future (v6.x.x or higher)
+We will move towards replacing all instances of TransactionBuilder in the tests with the new Psbt.
+
+Currently we have a few examples on how to use the newer Psbt class at the following link:
+- [Psbt examples](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions-psbt.js)
+
+The rest of the examples are below (using TransactionBuilder for Transaction creation)
+
 - [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js)
 - [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js)
 - [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js)
diff --git a/src/transaction_builder.js b/src/transaction_builder.js
index e63bdd0..a13c481 100644
--- a/src/transaction_builder.js
+++ b/src/transaction_builder.js
@@ -57,6 +57,13 @@ class TransactionBuilder {
     this.__TX = new transaction_1.Transaction();
     this.__TX.version = 2;
     this.__USE_LOW_R = false;
+    console.warn(
+      'Deprecation Warning: TransactionBuilder will be removed in the future. ' +
+        '(v6.x.x or later) Please use the Psbt class instead. Examples of usage ' +
+        'are available in the transactions-psbt.js integration test file on our ' +
+        'Github. A high level explanation is available in the psbt.ts and psbt.js ' +
+        'files as well.',
+    );
   }
   static fromTransaction(transaction, network) {
     const txb = new TransactionBuilder(network);
diff --git a/test/integration/cltv.js b/test/integration/cltv.js
index b2fd478..e123652 100644
--- a/test/integration/cltv.js
+++ b/test/integration/cltv.js
@@ -7,6 +7,7 @@ const bip65 = require('bip65')
 
 const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest)
 const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest)
+console.warn = () => {} // Silence the Deprecation Warning
 
 describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
   // force update MTP
diff --git a/test/integration/csv.js b/test/integration/csv.js
index f213c6c..2e133f0 100644
--- a/test/integration/csv.js
+++ b/test/integration/csv.js
@@ -9,6 +9,7 @@ const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGk
 const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest)
 const charles = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf', regtest)
 const dave = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', regtest)
+console.warn = () => {} // Silence the Deprecation Warning
 
 describe('bitcoinjs-lib (transactions w/ CSV)', () => {
   // force update MTP
diff --git a/test/integration/payments.js b/test/integration/payments.js
index 256bd00..905eda8 100644
--- a/test/integration/payments.js
+++ b/test/integration/payments.js
@@ -7,6 +7,7 @@ const keyPairs = [
   bitcoin.ECPair.makeRandom({ network: NETWORK }),
   bitcoin.ECPair.makeRandom({ network: NETWORK })
 ]
+console.warn = () => {} // Silence the Deprecation Warning
 
 async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) {
   const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4)
diff --git a/test/integration/transactions.js b/test/integration/transactions.js
index 464460e..a75b6f2 100644
--- a/test/integration/transactions.js
+++ b/test/integration/transactions.js
@@ -3,6 +3,7 @@ const assert = require('assert')
 const bitcoin = require('../../')
 const regtestUtils = require('./_regtest')
 const regtest = regtestUtils.network
+console.warn = () => {} // Silence the Deprecation Warning
 
 function rng () {
   return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64')
diff --git a/test/transaction_builder.js b/test/transaction_builder.js
index a135cca..6374161 100644
--- a/test/transaction_builder.js
+++ b/test/transaction_builder.js
@@ -9,6 +9,8 @@ const Transaction = require('..').Transaction
 const TransactionBuilder = require('..').TransactionBuilder
 const NETWORKS = require('../src/networks')
 
+console.warn = () => {} // Silence the Deprecation Warning
+
 const fixtures = require('./fixtures/transaction_builder')
 
 function constructSign (f, txb, useOldSignArgs) {
diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts
index c486285..1edf0f2 100644
--- a/ts_src/transaction_builder.ts
+++ b/ts_src/transaction_builder.ts
@@ -146,6 +146,13 @@ export class TransactionBuilder {
     this.__TX = new Transaction();
     this.__TX.version = 2;
     this.__USE_LOW_R = false;
+    console.warn(
+      'Deprecation Warning: TransactionBuilder will be removed in the future. ' +
+        '(v6.x.x or later) Please use the Psbt class instead. Examples of usage ' +
+        'are available in the transactions-psbt.js integration test file on our ' +
+        'Github. A high level explanation is available in the psbt.ts and psbt.js ' +
+        'files as well.',
+    );
   }
 
   setLowR(setting?: boolean): boolean {

From 6e447b1f1b4f68fd38f7b23170f5e4ef3ee053ae Mon Sep 17 00:00:00 2001
From: junderw <junderwood@bitcoinbank.co.jp>
Date: Fri, 19 Jul 2019 15:51:38 +0900
Subject: [PATCH 111/111] Refactor: Create cache in constructor

---
 src/psbt.js    | 12 ++++--------
 ts_src/psbt.ts | 20 ++++++++------------
 2 files changed, 12 insertions(+), 20 deletions(-)

diff --git a/src/psbt.js b/src/psbt.js
index 31ac164..e975faa 100644
--- a/src/psbt.js
+++ b/src/psbt.js
@@ -62,16 +62,14 @@ const DEFAULT_OPTS = {
 class Psbt {
   constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) {
     this.data = data;
+    // set defaults
+    this.opts = Object.assign({}, DEFAULT_OPTS, opts);
     this.__CACHE = {
       __NON_WITNESS_UTXO_TX_CACHE: [],
       __NON_WITNESS_UTXO_BUF_CACHE: [],
       __TX_IN_CACHE: {},
-      __TX: new transaction_1.Transaction(),
+      __TX: this.data.globalMap.unsignedTx.tx,
     };
-    // set defaults
-    this.opts = Object.assign({}, DEFAULT_OPTS, opts);
-    const c = this.__CACHE;
-    c.__TX = this.data.globalMap.unsignedTx.tx;
     if (this.data.inputs.length === 0) this.setVersion(2);
     // Make data hidden when enumerating
     const dpew = (obj, attr, enumerable, writable) =>
@@ -92,10 +90,8 @@ class Psbt {
   }
   static fromBuffer(buffer, opts = {}) {
     const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer);
-    const tx = psbtBase.globalMap.unsignedTx.tx;
     const psbt = new Psbt(opts, psbtBase);
-    psbt.__CACHE.__TX = tx;
-    checkTxForDupeIns(tx, psbt.__CACHE);
+    checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE);
     return psbt;
   }
   get inputCount() {
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index 863b331..6efbc9d 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -88,20 +88,12 @@ export class Psbt {
 
   static fromBuffer(buffer: Buffer, opts: PsbtOptsOptional = {}): Psbt {
     const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer);
-    const tx: Transaction = (psbtBase.globalMap.unsignedTx as PsbtTransaction)
-      .tx;
     const psbt = new Psbt(opts, psbtBase);
-    psbt.__CACHE.__TX = tx;
-    checkTxForDupeIns(tx, psbt.__CACHE);
+    checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE);
     return psbt;
   }
 
-  private __CACHE: PsbtCache = {
-    __NON_WITNESS_UTXO_TX_CACHE: [],
-    __NON_WITNESS_UTXO_BUF_CACHE: [],
-    __TX_IN_CACHE: {},
-    __TX: new Transaction(),
-  };
+  private __CACHE: PsbtCache;
   private opts: PsbtOpts;
 
   constructor(
@@ -110,8 +102,12 @@ export class Psbt {
   ) {
     // set defaults
     this.opts = Object.assign({}, DEFAULT_OPTS, opts);
-    const c = this.__CACHE;
-    c.__TX = (this.data.globalMap.unsignedTx as PsbtTransaction).tx;
+    this.__CACHE = {
+      __NON_WITNESS_UTXO_TX_CACHE: [],
+      __NON_WITNESS_UTXO_BUF_CACHE: [],
+      __TX_IN_CACHE: {},
+      __TX: (this.data.globalMap.unsignedTx as PsbtTransaction).tx,
+    };
     if (this.data.inputs.length === 0) this.setVersion(2);
 
     // Make data hidden when enumerating