From 732df833466e1094d9a0c436f1acd6b1fa4c972c Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Wed, 18 Jul 2018 14:00:53 +1000
Subject: [PATCH 1/7] tests/integration: simplify the bare witness examples

---
 test/integration/_regtest.js     | 45 ++++++++++++----
 test/integration/transactions.js | 92 +++++++++++---------------------
 2 files changed, 68 insertions(+), 69 deletions(-)

diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js
index f851d2c..0f39fad 100644
--- a/test/integration/_regtest.js
+++ b/test/integration/_regtest.js
@@ -4,6 +4,7 @@ const dhttp = require('dhttp/200')
 
 const APIPASS = process.env.APIPASS || 'satoshi'
 const APIURL = 'https://api.dcousens.cloud/1'
+const NETWORK = bitcoin.networks.testnet
 
 function broadcast (txHex, callback) {
   dhttp({
@@ -42,6 +43,31 @@ function faucet (address, value, callback) {
   })
 }
 
+function faucetComplex (output, value, callback) {
+  const keyPair = bitcoin.ECPair.makeRandom({ network: NETWORK })
+  const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: NETWORK })
+
+  faucet(p2pkh.address, value * 2, (err, unspent) => {
+    if (err) return callback(err)
+
+    const txvb = new bitcoin.TransactionBuilder(NETWORK)
+    txvb.addInput(unspent.txId, unspent.vout, null, p2pkh.output)
+    txvb.addOutput(output, value)
+    txvb.sign(0, keyPair)
+    const txv = txvb.build()
+
+    broadcast(txv.toHex(), function (err) {
+      if (err) return callback(err)
+
+      return callback(null, {
+        txId: txv.getId(),
+        vout: 0,
+        value
+      })
+    })
+  })
+}
+
 function fetch (txId, callback) {
   dhttp({
     method: 'GET',
@@ -78,14 +104,15 @@ function randomAddress () {
 }
 
 module.exports = {
-  broadcast: broadcast,
-  faucet: faucet,
-  fetch: fetch,
-  height: height,
-  mine: mine,
-  network: bitcoin.networks.testnet,
-  unspents: unspents,
-  verify: verify,
-  randomAddress: randomAddress,
+  broadcast,
+  faucet,
+  faucetComplex,
+  fetch,
+  height,
+  mine,
+  network: NETWORK,
+  unspents,
+  verify,
+  randomAddress,
   RANDOM_ADDRESS: randomAddress()
 }
diff --git a/test/integration/transactions.js b/test/integration/transactions.js
index 73b83c5..ef95d53 100644
--- a/test/integration/transactions.js
+++ b/test/integration/transactions.js
@@ -171,91 +171,63 @@ describe('bitcoinjs-lib (transactions)', function () {
     })
   })
 
-  it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input (via a P2SH(P2WPKH) transaction)', function (done) {
+  it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', function (done) {
     this.timeout(30000)
 
     const keyPair = bitcoin.ECPair.makeRandom({ network: regtest })
     const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest })
 
-    // prepare a P2SH(P2WPKH) faucet transaction, as Bitcoin-core doesn't support bare P2WPKH outputs (...yet)
-    const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest })
-
-    regtestUtils.faucet(p2sh.address, 10e4, function (err, unspent) {
+    regtestUtils.faucetComplex(p2wpkh.address, 5e4, function (err, unspent) {
       if (err) return done(err)
 
-      const txvb = new bitcoin.TransactionBuilder(regtest)
-      txvb.addInput(unspent.txId, unspent.vout)
-      txvb.addOutput(p2wpkh.address, 6e4) // funds a P2WPKH address
-      txvb.sign(0, keyPair, p2sh.redeem.output, null, unspent.value)
-      const txv = txvb.build()
+      // XXX: build the Transaction w/ a P2WPKH input
+      const txb = new bitcoin.TransactionBuilder(regtest)
+      txb.addInput(unspent.txId, unspent.vout, null, p2wpkh.output) // NOTE: provide the prevOutScript!
+      txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4)
+      txb.sign(0, keyPair, null, null, unspent.value) // NOTE: no redeem script
+      const tx = txb.build()
 
-      // build and broadcast (the via transaction) to the Bitcoin RegTest network
-      regtestUtils.broadcast(txv.toHex(), function (err) {
+      // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network
+      regtestUtils.broadcast(tx.toHex(), function (err) {
         if (err) return done(err)
 
-        // XXX: build the Transaction w/ a P2WPKH input
-        const txb = new bitcoin.TransactionBuilder(regtest)
-        txb.addInput(txv.getId(), 0, null, p2wpkh.output) // NOTE: provide the prevOutScript!
-        txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4)
-        txb.sign(0, keyPair, null, null, 6e4) // NOTE: no redeem script
-        const tx = txb.build()
-
-        // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network
-        regtestUtils.broadcast(tx.toHex(), function (err) {
-          if (err) return done(err)
-
-          regtestUtils.verify({
-            txId: tx.getId(),
-            address: regtestUtils.RANDOM_ADDRESS,
-            vout: 0,
-            value: 2e4
-          }, done)
-        })
+        regtestUtils.verify({
+          txId: tx.getId(),
+          address: regtestUtils.RANDOM_ADDRESS,
+          vout: 0,
+          value: 2e4
+        }, done)
       })
     })
   })
 
-  it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input (via a P2SH(P2PK) transaction)', function (done) {
+  it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', function (done) {
     this.timeout(30000)
 
     const keyPair = bitcoin.ECPair.makeRandom({ network: regtest })
     const p2pk = bitcoin.payments.p2pk({ pubkey: keyPair.publicKey, network: regtest })
     const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest })
 
-    // prepare a P2SH(P2PK) faucet transaction, as Bitcoin-core doesn't support bare P2WSH outputs (...yet)
-    const p2sh = bitcoin.payments.p2sh({ redeem: p2pk, network: regtest })
-
-    regtestUtils.faucet(p2sh.address, 10e4, function (err, unspent) {
+    regtestUtils.faucetComplex(p2wsh.address, 5e4, function (err, unspent) {
       if (err) return done(err)
 
-      const txvb = new bitcoin.TransactionBuilder(regtest)
-      txvb.addInput(unspent.txId, unspent.vout)
-      txvb.addOutput(p2wsh.address, 6e4) // funds a P2WPKH address
-      txvb.sign(0, keyPair, p2sh.redeem.output)
-      const txv = txvb.build()
+      // XXX: build the Transaction w/ a P2WSH input
+      const txb = new bitcoin.TransactionBuilder(regtest)
+      txb.addInput(unspent.txId, unspent.vout, null, p2wsh.output) // NOTE: provide the prevOutScript!
+      txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4)
+      txb.sign(0, keyPair, null, null, 5e4, p2wsh.redeem.output) // NOTE: provide a witnessScript!
+      const tx = txb.build()
 
-      // build and broadcast (the via transaction) to the Bitcoin RegTest network
-      regtestUtils.broadcast(txv.toHex(), function (err) {
+      // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network
+      regtestUtils.broadcast(tx.toHex(), function (err) {
         if (err) return done(err)
 
-        // XXX: build the Transaction w/ a P2WSH input
-        const txb = new bitcoin.TransactionBuilder(regtest)
-        txb.addInput(txv.getId(), 0, null, p2wsh.output) // NOTE: provide the prevOutScript!
-        txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4)
-        txb.sign(0, keyPair, null, null, 6e4, p2wsh.redeem.output) // NOTE: provide a witnessScript!
-        const tx = txb.build()
-
-        // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network
-        regtestUtils.broadcast(tx.toHex(), function (err) {
-          if (err) return done(err)
-
-          regtestUtils.verify({
-            txId: tx.getId(),
-            address: regtestUtils.RANDOM_ADDRESS,
-            vout: 0,
-            value: 2e4
-          }, done)
-        })
+        regtestUtils.verify({
+          txId: tx.getId(),
+          address: regtestUtils.RANDOM_ADDRESS,
+          vout: 0,
+          value: 2e4
+        }, done)
       })
     })
   })

From b2d3a2cf3024364eb8e5645188658320e9914a62 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Fri, 20 Jul 2018 16:28:14 +1000
Subject: [PATCH 2/7] testing: add payments tests for each standard payment
 type

---
 test/integration/payments.js | 71 ++++++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 test/integration/payments.js

diff --git a/test/integration/payments.js b/test/integration/payments.js
new file mode 100644
index 0000000..0a164f9
--- /dev/null
+++ b/test/integration/payments.js
@@ -0,0 +1,71 @@
+/* global describe, it */
+
+const bitcoin = require('../../')
+
+const regtestUtils = require('./_regtest')
+const NETWORK = regtestUtils.network
+const keyPairs = [
+  bitcoin.ECPair.makeRandom({ network: NETWORK }),
+  bitcoin.ECPair.makeRandom({ network: NETWORK })
+]
+
+function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) {
+  regtestUtils.faucetComplex(prevOutput, 5e4, (err, unspent) => {
+    if (err) return done(err)
+
+    const txb = new bitcoin.TransactionBuilder(NETWORK)
+    txb.addInput(unspent.txId, unspent.vout, null, prevOutput)
+    txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4)
+
+    if (depends.signatures) {
+      keyPairs.forEach((keyPair) => {
+        txb.sign(0, keyPair, redeemScript, null, unspent.value, witnessScript)
+      })
+    } else if (depends.signature) {
+      txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, witnessScript)
+    }
+
+    regtestUtils.broadcast(txb.build().toHex(), done)
+  })
+}
+
+;['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach((k) => {
+  const fixtures = require('../fixtures/' + k)
+  const { depends } = fixtures.dynamic
+  const fn = bitcoin.payments[k]
+
+  const base = {}
+  if (depends.pubkey) base.pubkey = keyPairs[0].publicKey
+  if (depends.pubkeys) base.pubkeys = keyPairs.map(x => x.publicKey)
+  if (depends.m) base.m = base.pubkeys.length
+
+  const { output } = fn(base)
+  if (!output) throw new TypeError('Missing output')
+
+  describe('bitcoinjs-lib (payments - ' + k + ')', () => {
+    it('can broadcast as an output, and be spent as an input', (done) => {
+      buildAndSign(depends, output, null, null, done)
+    })
+
+    it('can (as P2SH(' + k + ')) broadcast as an output, and be spent as an input', (done) => {
+      const p2sh = bitcoin.payments.p2sh({ redeem: { output }, network: NETWORK })
+      buildAndSign(depends, p2sh.output, p2sh.redeem.output, null, done)
+    })
+
+    it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', (done) => {
+      if (k === 'p2wpkh') return done() // skip P2WSH(P2WPKH)
+
+      const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK })
+      buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output, done)
+    })
+
+    it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', (done) => {
+      if (k === 'p2wpkh') return done() // skip P2SH(P2WSH(P2WPKH))
+
+      const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK })
+      const p2sh = bitcoin.payments.p2sh({ redeem: { output: p2wsh.output }, network: NETWORK })
+
+      buildAndSign(depends, p2sh.output, p2sh.redeem.output, p2wsh.redeem.output, done)
+    })
+  })
+})

From faf3645361bb468f4544725911278aee46655937 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Fri, 20 Jul 2018 17:02:27 +1000
Subject: [PATCH 3/7] tests/integration: allow more time

---
 test/integration/payments.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/test/integration/payments.js b/test/integration/payments.js
index 0a164f9..d657f3c 100644
--- a/test/integration/payments.js
+++ b/test/integration/payments.js
@@ -42,7 +42,9 @@ function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) {
   const { output } = fn(base)
   if (!output) throw new TypeError('Missing output')
 
-  describe('bitcoinjs-lib (payments - ' + k + ')', () => {
+  describe('bitcoinjs-lib (payments - ' + k + ')', function () {
+    this.timeout(30000)
+
     it('can broadcast as an output, and be spent as an input', (done) => {
       buildAndSign(depends, output, null, null, done)
     })

From de0259a820cb759c0147da85dc6be79eafaa4830 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Fri, 20 Jul 2018 17:30:18 +1000
Subject: [PATCH 4/7] tests/integration/payments: enable failing
 P2SH(P2WSH(P2WPKH)) tests

---
 src/transaction_builder.js   | 14 ++++++++++++--
 test/integration/payments.js |  4 ----
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/src/transaction_builder.js b/src/transaction_builder.js
index c4724d0..4545107 100644
--- a/src/transaction_builder.js
+++ b/src/transaction_builder.js
@@ -232,6 +232,11 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri
       expanded.signatures = input.signatures
     }
 
+    let signScript = witnessScript
+    if (expanded.type === SCRIPT_TYPES.P2WPKH) {
+      signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output
+    }
+
     return {
       redeemScript,
       redeemScriptType: SCRIPT_TYPES.P2WSH,
@@ -243,7 +248,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri
       prevOutScript: p2sh.output,
 
       hasWitness: true,
-      signScript: witnessScript,
+      signScript,
       signType: expanded.type,
 
       pubkeys: expanded.pubkeys,
@@ -303,6 +308,11 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri
       expanded.signatures = input.signatures
     }
 
+    let signScript = witnessScript
+    if (expanded.type === SCRIPT_TYPES.P2WPKH) {
+      signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output
+    }
+
     return {
       witnessScript,
       witnessScriptType: expanded.type,
@@ -311,7 +321,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri
       prevOutScript: p2wsh.output,
 
       hasWitness: true,
-      signScript: witnessScript,
+      signScript,
       signType: expanded.type,
 
       pubkeys: expanded.pubkeys,
diff --git a/test/integration/payments.js b/test/integration/payments.js
index d657f3c..c996f55 100644
--- a/test/integration/payments.js
+++ b/test/integration/payments.js
@@ -55,15 +55,11 @@ function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) {
     })
 
     it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', (done) => {
-      if (k === 'p2wpkh') return done() // skip P2WSH(P2WPKH)
-
       const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK })
       buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output, done)
     })
 
     it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', (done) => {
-      if (k === 'p2wpkh') return done() // skip P2SH(P2WSH(P2WPKH))
-
       const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK })
       const p2sh = bitcoin.payments.p2sh({ redeem: { output: p2wsh.output }, network: NETWORK })
 

From 31ab9bfc03b26fe73454d8c3b2b58cd23caee629 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Fri, 20 Jul 2018 17:35:34 +1000
Subject: [PATCH 5/7] tests/integration: throw verbose error if unspent is
 missing

---
 test/integration/_regtest.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js
index 0f39fad..059cec2 100644
--- a/test/integration/_regtest.js
+++ b/test/integration/_regtest.js
@@ -38,7 +38,10 @@ function faucet (address, value, callback) {
     unspents(address, function (err, results) {
       if (err) return callback(err)
 
-      callback(null, results.filter(x => x.txId === txId).pop())
+      const unspents = results.filter(x => x.txId === txId)
+      if (unspents.length === 0) return callback(new Error('Missing unspent'))
+
+      callback(null, unspents.pop())
     })
   })
 }

From ca4c9ca64cf878fb703a0248e226c0076a06c654 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Mon, 23 Jul 2018 10:37:31 +1000
Subject: [PATCH 6/7] add note about P2WPKH in P2WSH as a consensus fail

---
 test/integration/payments.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/test/integration/payments.js b/test/integration/payments.js
index c996f55..d319f60 100644
--- a/test/integration/payments.js
+++ b/test/integration/payments.js
@@ -54,6 +54,9 @@ function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) {
       buildAndSign(depends, p2sh.output, p2sh.redeem.output, null, done)
     })
 
+    // NOTE: P2WPKH cannot be wrapped in P2WSH, consensus fail
+    if (k === 'p2wpkh') return
+
     it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', (done) => {
       const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK })
       buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output, done)

From 079d83d887ef56ca4902f151a4c90ed01902623c Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Mon, 23 Jul 2018 10:41:01 +1000
Subject: [PATCH 7/7] txbuilder: note consensus issue

---
 src/transaction_builder.js | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/transaction_builder.js b/src/transaction_builder.js
index 4545107..8706841 100644
--- a/src/transaction_builder.js
+++ b/src/transaction_builder.js
@@ -233,9 +233,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri
     }
 
     let signScript = witnessScript
-    if (expanded.type === SCRIPT_TYPES.P2WPKH) {
-      signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output
-    }
+    if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure')
 
     return {
       redeemScript,
@@ -309,9 +307,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri
     }
 
     let signScript = witnessScript
-    if (expanded.type === SCRIPT_TYPES.P2WPKH) {
-      signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output
-    }
+    if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2WSH(P2WPKH) is a consensus failure')
 
     return {
       witnessScript,