diff --git a/README.md b/README.md index 5213db6..ad4df36 100644 --- a/README.md +++ b/README.md @@ -97,11 +97,11 @@ Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). - [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js#L12) - [Generate an address from a SHA256 hash](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js#L19) - [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js#L29) -- [Generate a 2-of-3 multisig P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js#L36) +- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js#L36) - [Generate a SegWit address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js:61) -- [Generate a SegWit address (via P2SH)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js:50) +- [Generate a SegWit P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js:50) - [Generate a SegWit 3-of-4 multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js:85) -- [Generate a SegWit 2-of-2 multisig address (via P2SH)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js:71) +- [Generate a SegWit 2-of-2 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js:71) - [Support the retrieval of transactions for an address (3rd party blockchain)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js#L52) - [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js#L73) - [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/addresses.js#L83) @@ -109,7 +109,10 @@ Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). - [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/transactions.js#L28) - [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/transactions.js#L46) - [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/transactions.js#L88) -- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 multisig P2SH input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/transactions.js#L115) +- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/transactions +.js#L115) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/transactions.js#L155) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/transactions.js#L182) - [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/bip32.js#L8) - [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/bip32.js#L15) - [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/ae8422d/test/integration/bip32.js#L26) diff --git a/test/integration/_testnet.js b/test/integration/_testnet.js index 8a81e59..0de6ac2 100644 --- a/test/integration/_testnet.js +++ b/test/integration/_testnet.js @@ -2,6 +2,7 @@ var async = require('async') var bitcoin = require('../../') var Blockchain = require('cb-http-client') var coinSelect = require('coinselect') +var dhttp = require('dhttp/200') var typeforce = require('typeforce') var types = require('../../src/types') @@ -26,6 +27,7 @@ function fundAddress (unspents, outputs, callback) { }) result.outputs.forEach(function (x) { + if (x.address) console.warn('funding ' + x.address + ' w/ ' + x.value) txb.addOutput(x.address || kpAddress, x.value) }) @@ -34,17 +36,14 @@ function fundAddress (unspents, outputs, callback) { }) var tx = txb.build() - var txId = tx.getId() blockchain.transactions.propagate(tx.toHex(), function (err) { if (err) return callback(err) - // FIXME: @blocktrail can be very slow, give it time - setTimeout(function () { - callback(null, outputs.map(function (_, i) { - return { txId: txId, vout: i } - })) - }, 3000) + var txId = tx.getId() + callback(null, outputs.map(function (x, i) { + return { txId: txId, vout: i, value: x.value } + })) }) } @@ -73,17 +72,26 @@ blockchain.verify = function (address, txId, value, done) { async.retry(5, function (callback) { setTimeout(function () { // check that the above transaction included the intended address - blockchain.addresses.unspents(blockchain.RETURN_ADDRESS, function (err, unspents) { + dhttp({ + method: 'POST', + url: 'https://api.ei8ht.com.au:9443/3/txs', + body: [txId] + }, function (err, result) { if (err) return callback(err) - if (!unspents.some(function (x) { - return x.txId === txId && x.value === value - })) return callback(new Error('Could not find unspent')) - + if (!result[txId]) return callback(new Error('Could not find ' + txId)) callback() }) - }, 600) + }, 400) }, done) } +blockchain.transactions.propagate = function (txHex, callback) { + dhttp({ + method: 'POST', + url: 'https://api.ei8ht.com.au:9443/3/pushtx', + body: txHex + }, callback) +} + blockchain.RETURN_ADDRESS = kpAddress module.exports = blockchain diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 8832c7f..78f2333 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -21,7 +21,7 @@ describe('bitcoinjs-lib (transactions)', function () { tx.sign(0, alice) - // build, prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below + // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below assert.strictEqual(tx.build().toHex(), '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000') }) @@ -39,7 +39,7 @@ describe('bitcoinjs-lib (transactions)', function () { tx.sign(1, bob) // Bob signs his input, which was the second input (1th) tx.sign(0, alice) // Alice signs her input, which was the first input (0th) - // build, prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below + // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below assert.strictEqual(tx.build().toHex(), '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000') }) @@ -54,11 +54,11 @@ describe('bitcoinjs-lib (transactions)', function () { testnetUtils.faucetMany([ { address: alice1.getAddress(), - value: 4e4 + value: 5e4 }, { address: alice2.getAddress(), - value: 2e4 + value: 7e4 } ], function (err, unspents) { if (err) return done(err) @@ -66,8 +66,8 @@ describe('bitcoinjs-lib (transactions)', function () { var tx = new bitcoin.TransactionBuilder(testnet) tx.addInput(unspents[0].txId, unspents[0].vout) // alice1 unspent tx.addInput(unspents[1].txId, unspents[1].vout) // alice2 unspent - tx.addOutput('mvGVHWi6gbkBZZPaqBVRcxvKVPYd9r3fp7', 1e4) // the actual "spend" - tx.addOutput(aliceChange.getAddress(), 3e4) // Alice's change + tx.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" + tx.addOutput(aliceChange.getAddress(), 1e4) // Alice's change // (in)(4e4 + 2e4) - (out)(1e4 + 3e4) = (fee)2e4 = 20000, this is the miner fee // Alice signs each input with the respective private keys @@ -112,7 +112,7 @@ describe('bitcoinjs-lib (transactions)', function () { }) }) - it('can create (and broadcast via 3PBP) a Transaction with a 2-of-4 multisig P2SH input', function (done) { + it('can create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input', function (done) { this.timeout(30000) var keyPairs = [ @@ -123,11 +123,10 @@ describe('bitcoinjs-lib (transactions)', function () { ].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, testnet) }) var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() }) - var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) // 2 of 4 + var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) var address = bitcoin.address.fromOutputScript(scriptPubKey, testnet) - // attempt to send funds to the source address testnetUtils.faucet(address, 2e4, function (err, unspent) { if (err) return done(err) @@ -135,22 +134,85 @@ describe('bitcoinjs-lib (transactions)', function () { txb.addInput(unspent.txId, unspent.vout) txb.addOutput(testnetUtils.RETURN_ADDRESS, 1e4) - // sign with 1st and 3rd key txb.sign(0, keyPairs[0], redeemScript) txb.sign(0, keyPairs[2], redeemScript) - // broadcast our transaction var tx = txb.build() - var txId = tx.getId() - dhttp({ - method: 'POST', - url: 'https://api.ei8ht.com.au:9443/3/pushtx', - body: tx.toHex() - }, function (err) { + // build and broadcast to the Bitcoin Testnet network + testnetUtils.transactions.propagate(tx.toHex(), function (err) { + if (err) return done(err) + + testnetUtils.verify(address, tx.getId(), 1e4, done) + }) + }) + }) + + it('can create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input', function (done) { + this.timeout(30000) + + var keyPair = bitcoin.ECPair.fromWIF('cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA', testnet) + var pubKey = keyPair.getPublicKeyBuffer() + var pubKeyHash = bitcoin.crypto.hash160(pubKey) + + var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash) + var redeemScriptHash = bitcoin.crypto.hash160(redeemScript) + + var scriptPubKey = bitcoin.script.scriptHash.output.encode(redeemScriptHash) + var address = bitcoin.address.fromOutputScript(scriptPubKey, testnet) + + testnetUtils.faucet(address, 5e4, function (err, unspent) { + if (err) return done(err) + + var txb = new bitcoin.TransactionBuilder(testnet) + txb.addInput(unspent.txId, unspent.vout) + txb.addOutput(testnetUtils.RETURN_ADDRESS, 4e4) + txb.sign(0, keyPair, redeemScript, null, unspent.value) + + var tx = txb.build() + + // build and broadcast to the Bitcoin Testnet network + testnetUtils.transactions.propagate(tx.toHex(), function (err) { + if (err) return done(err) + + testnetUtils.verify(address, tx.getId(), 1e4, done) + }) + }) + }) + + it('can create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input', function (done) { + this.timeout(50000) + + var keyPairs = [ + 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA', + 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87K7XCyj5v', + 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87KcLPVfXz', + 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87L7FgDCKE' + ].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, testnet) }) + var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() }) + + var witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) + var redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) + var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + var address = bitcoin.address.fromOutputScript(scriptPubKey, testnet) + + testnetUtils.faucet(address, 6e4, function (err, unspent) { + if (err) return done(err) + + var txb = new bitcoin.TransactionBuilder(testnet) + txb.addInput(unspent.txId, unspent.vout) + txb.addOutput(testnetUtils.RETURN_ADDRESS, 4e4) + txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, witnessScript) + txb.sign(0, keyPairs[2], redeemScript, null, unspent.value, witnessScript) + txb.sign(0, keyPairs[3], redeemScript, null, unspent.value, witnessScript) + + var tx = txb.build() + + // build and broadcast to the Bitcoin Testnet network + testnetUtils.transactions.propagate(tx.toHex(), function (err) { if (err) return done(err) - testnetUtils.verify(address, txId, 1e4, done) + testnetUtils.verify(address, tx.getId(), 4e4, done) }) }) })