Fix tests Missing Input

This commit is contained in:
junderw 2019-04-07 22:27:16 +09:00
parent 1c24201a46
commit 8ec1911a26
No known key found for this signature in database
GPG key ID: B256185D3A971908
7 changed files with 397 additions and 459 deletions

View file

@ -1,99 +1,109 @@
const assert = require('assert') const assert = require('assert')
const bitcoin = require('../../') const bitcoin = require('../../')
const dhttp = require('dhttp/200') const dhttpCallback = require('dhttp/200')
// use Promises
const dhttp = options => new Promise((resolve, reject) => {
return dhttpCallback(options, (err, data) => {
if (err) return reject(err)
else return resolve(data)
})
})
const APIPASS = process.env.APIPASS || 'satoshi' const APIPASS = process.env.APIPASS || 'satoshi'
const APIURL = 'https://regtest.bitbank.cc/1' const APIURL = 'https://regtest.bitbank.cc/1'
const NETWORK = bitcoin.networks.testnet const NETWORK = bitcoin.networks.testnet
function broadcast (txHex, callback) { function broadcast (txHex) {
dhttp({ return dhttp({
method: 'POST', method: 'POST',
url: APIURL + '/t/push', url: APIURL + '/t/push',
body: txHex body: txHex
}, callback)
}
function mine (count, callback) {
dhttp({
method: 'POST',
url: APIURL + '/r/generate?count=' + count + '&key=' + APIPASS
}, callback)
}
function height (callback) {
dhttp({
method: 'GET',
url: APIURL + '/b/best/height'
}, callback)
}
function faucet (address, value, callback) {
dhttp({
method: 'POST',
url: APIURL + '/r/faucet?address=' + address + '&value=' + value + '&key=' + APIPASS
}, function (err, txId) {
if (err) return callback(err)
unspents(address, function (err, results) {
if (err) return callback(err)
const unspents = results.filter(x => x.txId === txId)
if (unspents.length === 0) return callback(new Error('Missing unspent'))
callback(null, unspents.pop())
})
}) })
} }
function faucetComplex (output, value, callback) { function mine (count) {
return dhttp({
method: 'POST',
url: APIURL + '/r/generate?count=' + count + '&key=' + APIPASS
})
}
function height () {
return dhttp({
method: 'GET',
url: APIURL + '/b/best/height'
})
}
async function faucet (address, value) {
let count = 0
let _unspents = []
const sleep = ms => new Promise(r => setTimeout(r, ms))
do {
if (count > 0) {
if (count >= 5) throw new Error('Missing Inputs')
console.log('Missing Inputs, retry #' + count)
await sleep(200)
}
const txId = await dhttp({
method: 'POST',
url: APIURL + '/r/faucet?address=' + address + '&value=' + value + '&key=' + APIPASS
})
await sleep(100)
const results = await unspents(address)
_unspents = results.filter(x => x.txId === txId)
count++
} while (_unspents.length === 0)
return _unspents.pop()
}
async function faucetComplex (output, value) {
const keyPair = bitcoin.ECPair.makeRandom({ network: NETWORK }) const keyPair = bitcoin.ECPair.makeRandom({ network: NETWORK })
const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: NETWORK }) const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: NETWORK })
faucet(p2pkh.address, value * 2, (err, unspent) => { const unspent = await faucet(p2pkh.address, value * 2)
if (err) return callback(err)
const txvb = new bitcoin.TransactionBuilder(NETWORK) const txvb = new bitcoin.TransactionBuilder(NETWORK)
txvb.addInput(unspent.txId, unspent.vout, null, p2pkh.output) txvb.addInput(unspent.txId, unspent.vout, null, p2pkh.output)
txvb.addOutput(output, value) txvb.addOutput(output, value)
txvb.sign(0, keyPair) txvb.sign(0, keyPair)
const txv = txvb.build() const txv = txvb.build()
broadcast(txv.toHex(), function (err) { await broadcast(txv.toHex())
if (err) return callback(err)
return callback(null, { return {
txId: txv.getId(), txId: txv.getId(),
vout: 0, vout: 0,
value value
}) }
})
})
} }
function fetch (txId, callback) { function fetch (txId) {
dhttp({ return dhttp({
method: 'GET', method: 'GET',
url: APIURL + '/t/' + txId + '/json' url: APIURL + '/t/' + txId + '/json'
}, callback) })
} }
function unspents (address, callback) { function unspents (address) {
dhttp({ return dhttp({
method: 'GET', method: 'GET',
url: APIURL + '/a/' + address + '/unspents' url: APIURL + '/a/' + address + '/unspents'
}, callback) })
} }
function verify (txo, callback) { async function verify (txo) {
fetch(txo.txId, function (err, tx) { const tx = await fetch(txo.txId)
if (err) return callback(err)
const txoActual = tx.outs[txo.vout] const txoActual = tx.outs[txo.vout]
if (txo.address) assert.strictEqual(txoActual.address, txo.address) if (txo.address) assert.strictEqual(txoActual.address, txo.address)
if (txo.value) assert.strictEqual(txoActual.value, txo.value) if (txo.value) assert.strictEqual(txoActual.value, txo.value)
callback()
})
} }
function getAddress (node, network) { function getAddress (node, network) {
@ -108,6 +118,7 @@ function randomAddress () {
module.exports = { module.exports = {
broadcast, broadcast,
dhttp,
faucet, faucet,
faucetComplex, faucetComplex,
fetch, fetch,

View file

@ -1,29 +1,26 @@
const { describe, it } = require('mocha') const { describe, it } = require('mocha')
const assert = require('assert') const assert = require('assert')
const bitcoin = require('../../') const bitcoin = require('../../')
const dhttp = require('dhttp/200') const dhttp = require('./_regtest').dhttp
const TESTNET = bitcoin.networks.testnet const TESTNET = bitcoin.networks.testnet
describe('bitcoinjs-lib (addresses)', function () { describe('bitcoinjs-lib (addresses)', function () {
it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', function (done) { it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', async function () {
const keyPair = bitcoin.ECPair.makeRandom() const keyPair = bitcoin.ECPair.makeRandom()
const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey })
// bitcoin P2PKH addresses start with a '1' // bitcoin P2PKH addresses start with a '1'
assert.strictEqual(address.startsWith('1'), true) assert.strictEqual(address.startsWith('1'), true)
dhttp({ const result = await dhttp({
method: 'GET', method: 'GET',
url: 'https://blockchain.info/rawaddr/' + address url: 'https://blockchain.info/rawaddr/' + address
}, function (err, result) {
if (err) return done(err)
// random private keys [probably!] have no transactions
assert.strictEqual(result.n_tx, 0)
assert.strictEqual(result.total_received, 0)
assert.strictEqual(result.total_sent, 0)
done()
}) })
// random private keys [probably!] have no transactions
assert.strictEqual(result.n_tx, 0)
assert.strictEqual(result.total_received, 0)
assert.strictEqual(result.total_sent, 0)
}) })
it('can import an address via WIF', function () { it('can import an address via WIF', function () {

View file

@ -13,7 +13,7 @@ describe('bitcoinjs-lib (BIP32)', function () {
const xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK' const xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK'
const node = bip32.fromBase58(xpriv, bitcoin.networks.testnet) const node = bip32.fromBase58(xpriv, bitcoin.networks.testnet)
assert.equal(node.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') assert.strictEqual(node.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7')
}) })
it('can export a BIP32 xpriv, then import it', function () { it('can export a BIP32 xpriv, then import it', function () {
@ -23,8 +23,8 @@ describe('bitcoinjs-lib (BIP32)', function () {
const string = node.toBase58() const string = node.toBase58()
const restored = bip32.fromBase58(string) const restored = bip32.fromBase58(string)
assert.equal(getAddress(node), getAddress(restored)) // same public key assert.strictEqual(getAddress(node), getAddress(restored)) // same public key
assert.equal(node.toWIF(), restored.toWIF()) // same private key assert.strictEqual(node.toWIF(), restored.toWIF()) // same private key
}) })
it('can export a BIP32 xpub', function () { it('can export a BIP32 xpub', function () {
@ -33,7 +33,7 @@ describe('bitcoinjs-lib (BIP32)', function () {
const node = bip32.fromSeed(seed) const node = bip32.fromSeed(seed)
const string = node.neutered().toBase58() const string = node.neutered().toBase58()
assert.equal(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n') assert.strictEqual(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n')
}) })
it('can create a BIP32, bitcoin, account 0, external address', function () { it('can create a BIP32, bitcoin, account 0, external address', function () {
@ -47,8 +47,8 @@ describe('bitcoinjs-lib (BIP32)', function () {
.derive(0) .derive(0)
.derive(0) .derive(0)
assert.equal(getAddress(child1), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') assert.strictEqual(getAddress(child1), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7')
assert.equal(getAddress(child1b), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') assert.strictEqual(getAddress(child1b), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7')
}) })
it('can create a BIP44, bitcoin, account 0, external address', function () { it('can create a BIP44, bitcoin, account 0, external address', function () {
@ -63,8 +63,8 @@ describe('bitcoinjs-lib (BIP32)', function () {
.derive(0) .derive(0)
.derive(0) .derive(0)
assert.equal(getAddress(child1), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') assert.strictEqual(getAddress(child1), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au')
assert.equal(getAddress(child1b), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') assert.strictEqual(getAddress(child1b), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au')
}) })
it('can create a BIP49, bitcoin testnet, account 0, external address', function () { it('can create a BIP49, bitcoin testnet, account 0, external address', function () {
@ -79,7 +79,7 @@ describe('bitcoinjs-lib (BIP32)', function () {
redeem: bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network: bitcoin.networks.testnet }), redeem: bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network: bitcoin.networks.testnet }),
network: bitcoin.networks.testnet network: bitcoin.networks.testnet
}) })
assert.equal(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2') assert.strictEqual(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2')
}) })
it('can use BIP39 to generate BIP32 addresses', function () { it('can use BIP39 to generate BIP32 addresses', function () {

View file

@ -10,8 +10,8 @@ const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZs
describe('bitcoinjs-lib (transactions w/ CLTV)', function () { describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
// force update MTP // force update MTP
before(function (done) { before(async function () {
regtestUtils.mine(11, done) await regtestUtils.mine(11)
}) })
const hashType = bitcoin.Transaction.SIGHASH_ALL const hashType = bitcoin.Transaction.SIGHASH_ALL
@ -38,188 +38,160 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
} }
// expiry past, {Alice's signature} OP_TRUE // expiry past, {Alice's signature} OP_TRUE
it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', function (done) { it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', async function () {
// 3 hours ago // 3 hours ago
const lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) const lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) })
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime)
const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest })
// fund the P2SH(CLTV) address // fund the P2SH(CLTV) address
regtestUtils.faucet(address, 1e5, function (err, unspent) { const unspent = await regtestUtils.faucet(address, 1e5)
if (err) return done(err) const txb = new bitcoin.TransactionBuilder(regtest)
txb.setLockTime(lockTime)
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4)
const txb = new bitcoin.TransactionBuilder(regtest) // {Alice's signature} OP_TRUE
txb.setLockTime(lockTime) const tx = txb.buildIncomplete()
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) const redeemScriptSig = bitcoin.payments.p2sh({
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) redeem: {
input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
bitcoin.opcodes.OP_TRUE
]),
output: redeemScript
}
}).input
tx.setInputScript(0, redeemScriptSig)
// {Alice's signature} OP_TRUE await regtestUtils.broadcast(tx.toHex())
const tx = txb.buildIncomplete()
const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
const redeemScriptSig = bitcoin.payments.p2sh({
redeem: {
input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
bitcoin.opcodes.OP_TRUE
]),
output: redeemScript
}
}).input
tx.setInputScript(0, redeemScriptSig)
regtestUtils.broadcast(tx.toHex(), function (err) { await regtestUtils.verify({
if (err) return done(err) txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
regtestUtils.verify({ vout: 0,
txId: tx.getId(), value: 7e4
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 7e4
}, done)
})
}) })
}) })
// expiry will pass, {Alice's signature} OP_TRUE // expiry will pass, {Alice's signature} OP_TRUE
it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', function (done) { it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async function () {
regtestUtils.height(function (err, height) { const height = await regtestUtils.height()
if (err) return done(err) // 5 blocks from now
const lockTime = bip65.encode({ blocks: height + 5 })
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime)
const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest })
// 5 blocks from now // fund the P2SH(CLTV) address
const lockTime = bip65.encode({ blocks: height + 5 }) const unspent = await regtestUtils.faucet(address, 1e5)
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) const txb = new bitcoin.TransactionBuilder(regtest)
const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) txb.setLockTime(lockTime)
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4)
// fund the P2SH(CLTV) address // {Alice's signature} OP_TRUE
regtestUtils.faucet(address, 1e5, function (err, unspent) { const tx = txb.buildIncomplete()
if (err) return done(err) const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
const redeemScriptSig = bitcoin.payments.p2sh({
redeem: {
input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
bitcoin.opcodes.OP_TRUE
]),
output: redeemScript
}
}).input
tx.setInputScript(0, redeemScriptSig)
const txb = new bitcoin.TransactionBuilder(regtest) // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
txb.setLockTime(lockTime) // ...
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. // into the future!
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) await regtestUtils.mine(5)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) await regtestUtils.broadcast(tx.toHex())
await regtestUtils.verify({
// {Alice's signature} OP_TRUE txId: tx.getId(),
const tx = txb.buildIncomplete() address: regtestUtils.RANDOM_ADDRESS,
const signatureHash = tx.hashForSignature(0, redeemScript, hashType) vout: 0,
const redeemScriptSig = bitcoin.payments.p2sh({ value: 7e4
redeem: {
input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
bitcoin.opcodes.OP_TRUE
]),
output: redeemScript
}
}).input
tx.setInputScript(0, redeemScriptSig)
// TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
// ...
// into the future!
regtestUtils.mine(5, function (err) {
if (err) return done(err)
regtestUtils.broadcast(tx.toHex(), function (err) {
if (err) return done(err)
regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 7e4
}, done)
})
})
})
}) })
}) })
// expiry ignored, {Bob's signature} {Alice's signature} OP_FALSE // expiry ignored, {Bob's signature} {Alice's signature} OP_FALSE
it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', function (done) { it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', async function () {
// two hours ago // two hours ago
const lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) }) const lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) })
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime)
const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest })
// fund the P2SH(CLTV) address // fund the P2SH(CLTV) address
regtestUtils.faucet(address, 2e5, function (err, unspent) { const unspent = await regtestUtils.faucet(address, 2e5)
if (err) return done(err) const txb = new bitcoin.TransactionBuilder(regtest)
txb.setLockTime(lockTime)
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4)
const txb = new bitcoin.TransactionBuilder(regtest) // {Alice's signature} {Bob's signature} OP_FALSE
txb.setLockTime(lockTime) const tx = txb.buildIncomplete()
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) const redeemScriptSig = bitcoin.payments.p2sh({
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4) redeem: {
input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
bitcoin.opcodes.OP_FALSE
]),
output: redeemScript
}
}).input
tx.setInputScript(0, redeemScriptSig)
// {Alice's signature} {Bob's signature} OP_FALSE await regtestUtils.broadcast(tx.toHex())
const tx = txb.buildIncomplete() await regtestUtils.verify({
const signatureHash = tx.hashForSignature(0, redeemScript, hashType) txId: tx.getId(),
const redeemScriptSig = bitcoin.payments.p2sh({ address: regtestUtils.RANDOM_ADDRESS,
redeem: { vout: 0,
input: bitcoin.script.compile([ value: 8e4
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
bitcoin.opcodes.OP_FALSE
]),
output: redeemScript
}
}).input
tx.setInputScript(0, redeemScriptSig)
regtestUtils.broadcast(tx.toHex(), function (err) {
if (err) return done(err)
regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 8e4
}, done)
})
}) })
}) })
// expiry in the future, {Alice's signature} OP_TRUE // expiry in the future, {Alice's signature} OP_TRUE
it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', function (done) { it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async function () {
// two hours from now // two hours from now
const lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) }) const lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) })
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime)
const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest })
// fund the P2SH(CLTV) address // fund the P2SH(CLTV) address
regtestUtils.faucet(address, 2e4, function (err, unspent) { const unspent = await regtestUtils.faucet(address, 2e4)
if (err) return done(err) const txb = new bitcoin.TransactionBuilder(regtest)
txb.setLockTime(lockTime)
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4)
const txb = new bitcoin.TransactionBuilder(regtest) // {Alice's signature} OP_TRUE
txb.setLockTime(lockTime) const tx = txb.buildIncomplete()
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) const redeemScriptSig = bitcoin.payments.p2sh({
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) redeem: {
input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
bitcoin.opcodes.OP_TRUE
]),
output: redeemScript
}
}).input
tx.setInputScript(0, redeemScriptSig)
// {Alice's signature} OP_TRUE await regtestUtils.broadcast(tx.toHex()).catch(err => {
const tx = txb.buildIncomplete() assert.throws(function () {
const signatureHash = tx.hashForSignature(0, redeemScript, hashType) if (err) throw err
const redeemScriptSig = bitcoin.payments.p2sh({ }, /Error: non-final \(code 64\)/)
redeem: {
input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
bitcoin.opcodes.OP_TRUE
]),
output: redeemScript
}
}).input
tx.setInputScript(0, redeemScriptSig)
regtestUtils.broadcast(tx.toHex(), function (err) {
assert.throws(function () {
if (err) throw err
}, /Error: non-final \(code 64\)/)
done()
})
}) })
}) })
}) })

View file

@ -10,8 +10,8 @@ const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZs
describe('bitcoinjs-lib (transactions w/ CSV)', function () { describe('bitcoinjs-lib (transactions w/ CSV)', function () {
// force update MTP // force update MTP
before(function (done) { before(async function () {
regtestUtils.mine(11, done) await regtestUtils.mine(11)
}) })
const hashType = bitcoin.Transaction.SIGHASH_ALL const hashType = bitcoin.Transaction.SIGHASH_ALL
@ -35,66 +35,56 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () {
} }
// expiry will pass, {Alice's signature} OP_TRUE // expiry will pass, {Alice's signature} OP_TRUE
it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', function (done) { it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async function () {
regtestUtils.height(function (err, height) { // 5 blocks from now
if (err) return done(err) const sequence = bip68.encode({ blocks: 5 })
const p2sh = bitcoin.payments.p2sh({
redeem: {
output: csvCheckSigOutput(alice, bob, sequence)
},
network: regtest
})
// 5 blocks from now // fund the P2SH(CSV) address
const sequence = bip68.encode({ blocks: 5 }) const unspent = await regtestUtils.faucet(p2sh.address, 1e5)
const p2sh = bitcoin.payments.p2sh({
redeem: {
output: csvCheckSigOutput(alice, bob, sequence)
},
network: regtest
})
// fund the P2SH(CSV) address const txb = new bitcoin.TransactionBuilder(regtest)
regtestUtils.faucet(p2sh.address, 1e5, function (err, unspent) { txb.addInput(unspent.txId, unspent.vout, sequence)
if (err) return done(err) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4)
const txb = new bitcoin.TransactionBuilder(regtest) // {Alice's signature} OP_TRUE
txb.addInput(unspent.txId, unspent.vout, sequence) const tx = txb.buildIncomplete()
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType)
const redeemScriptSig = bitcoin.payments.p2sh({
network: regtest,
redeem: {
network: regtest,
output: p2sh.redeem.output,
input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
bitcoin.opcodes.OP_TRUE
])
}
}).input
tx.setInputScript(0, redeemScriptSig)
// {Alice's signature} OP_TRUE // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
const tx = txb.buildIncomplete() // ...
const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) // into the future!
const redeemScriptSig = bitcoin.payments.p2sh({ await regtestUtils.mine(10)
network: regtest,
redeem: {
network: regtest,
output: p2sh.redeem.output,
input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
bitcoin.opcodes.OP_TRUE
])
}
}).input
tx.setInputScript(0, redeemScriptSig)
// TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently await regtestUtils.broadcast(tx.toHex())
// ...
// into the future!
regtestUtils.mine(10, function (err) {
if (err) return done(err)
regtestUtils.broadcast(tx.toHex(), function (err) { await regtestUtils.verify({
if (err) return done(err) txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
regtestUtils.verify({ vout: 0,
txId: tx.getId(), value: 7e4
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 7e4
}, done)
})
})
})
}) })
}) })
// expiry in the future, {Alice's signature} OP_TRUE // expiry in the future, {Alice's signature} OP_TRUE
it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', function (done) { it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async function () {
// two hours after confirmation // two hours after confirmation
const sequence = bip68.encode({ seconds: 7168 }) const sequence = bip68.encode({ seconds: 7168 })
const p2sh = bitcoin.payments.p2sh({ const p2sh = bitcoin.payments.p2sh({
@ -105,37 +95,33 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () {
}) })
// fund the P2SH(CSV) address // fund the P2SH(CSV) address
regtestUtils.faucet(p2sh.address, 2e4, function (err, unspent) { const unspent = await regtestUtils.faucet(p2sh.address, 2e4)
if (err) return done(err)
const txb = new bitcoin.TransactionBuilder(regtest) const txb = new bitcoin.TransactionBuilder(regtest)
txb.addInput(unspent.txId, unspent.vout, sequence) txb.addInput(unspent.txId, unspent.vout, sequence)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4)
// {Alice's signature} OP_TRUE // {Alice's signature} OP_TRUE
const tx = txb.buildIncomplete() const tx = txb.buildIncomplete()
const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType)
const redeemScriptSig = bitcoin.payments.p2sh({ const redeemScriptSig = bitcoin.payments.p2sh({
network: regtest,
redeem: {
network: regtest, network: regtest,
redeem: { output: p2sh.redeem.output,
network: regtest, input: bitcoin.script.compile([
output: p2sh.redeem.output, bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
input: bitcoin.script.compile([ bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.opcodes.OP_TRUE
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), ])
bitcoin.opcodes.OP_TRUE }
]) }).input
} tx.setInputScript(0, redeemScriptSig)
}).input
tx.setInputScript(0, redeemScriptSig)
regtestUtils.broadcast(tx.toHex(), function (err) { await regtestUtils.broadcast(tx.toHex()).catch(err => {
assert.throws(function () { assert.throws(function () {
if (err) throw err if (err) throw err
}, /Error: non-BIP68-final \(code 64\)/) }, /Error: non-BIP68-final \(code 64\)/)
done()
})
}) })
}) })
}) })

View file

@ -8,24 +8,22 @@ const keyPairs = [
bitcoin.ECPair.makeRandom({ network: NETWORK }) bitcoin.ECPair.makeRandom({ network: NETWORK })
] ]
function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) { async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) {
regtestUtils.faucetComplex(prevOutput, 5e4, (err, unspent) => { const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4)
if (err) return done(err)
const txb = new bitcoin.TransactionBuilder(NETWORK) const txb = new bitcoin.TransactionBuilder(NETWORK)
txb.addInput(unspent.txId, unspent.vout, null, prevOutput) txb.addInput(unspent.txId, unspent.vout, null, prevOutput)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4)
if (depends.signatures) { if (depends.signatures) {
keyPairs.forEach((keyPair) => { keyPairs.forEach((keyPair) => {
txb.sign(0, keyPair, redeemScript, null, unspent.value, witnessScript) txb.sign(0, keyPair, redeemScript, null, unspent.value, witnessScript)
}) })
} else if (depends.signature) { } else if (depends.signature) {
txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, witnessScript) txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, witnessScript)
} }
regtestUtils.broadcast(txb.build().toHex(), done) return regtestUtils.broadcast(txb.build().toHex())
})
} }
;['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach((k) => { ;['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach((k) => {
@ -42,28 +40,28 @@ function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) {
if (!output) throw new TypeError('Missing output') if (!output) throw new TypeError('Missing output')
describe('bitcoinjs-lib (payments - ' + k + ')', function () { describe('bitcoinjs-lib (payments - ' + k + ')', function () {
it('can broadcast as an output, and be spent as an input', (done) => { it('can broadcast as an output, and be spent as an input', async () => {
buildAndSign(depends, output, null, null, done) await buildAndSign(depends, output, null, null)
}) })
it('can (as P2SH(' + k + ')) broadcast as an output, and be spent as an input', (done) => { it('can (as P2SH(' + k + ')) broadcast as an output, and be spent as an input', async () => {
const p2sh = bitcoin.payments.p2sh({ redeem: { output }, network: NETWORK }) const p2sh = bitcoin.payments.p2sh({ redeem: { output }, network: NETWORK })
buildAndSign(depends, p2sh.output, p2sh.redeem.output, null, done) await buildAndSign(depends, p2sh.output, p2sh.redeem.output, null)
}) })
// NOTE: P2WPKH cannot be wrapped in P2WSH, consensus fail // NOTE: P2WPKH cannot be wrapped in P2WSH, consensus fail
if (k === 'p2wpkh') return if (k === 'p2wpkh') return
it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', (done) => { it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', async () => {
const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK })
buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output, done) await buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output)
}) })
it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', (done) => { it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', async () => {
const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK })
const p2sh = bitcoin.payments.p2sh({ redeem: { output: p2wsh.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) await buildAndSign(depends, p2sh.output, p2sh.redeem.output, p2wsh.redeem.output)
}) })
}) })
}) })

View file

@ -43,7 +43,7 @@ describe('bitcoinjs-lib (transactions)', function () {
assert.strictEqual(txb.build().toHex(), '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000') assert.strictEqual(txb.build().toHex(), '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000')
}) })
it('can create (and broadcast via 3PBP) a typical Transaction', function (done) { it('can create (and broadcast via 3PBP) a typical Transaction', async () => {
const alice1 = bitcoin.ECPair.makeRandom({ network: regtest }) const alice1 = bitcoin.ECPair.makeRandom({ network: regtest })
const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }) const alice2 = bitcoin.ECPair.makeRandom({ network: regtest })
const aliceChange = bitcoin.ECPair.makeRandom({ network: regtest, rng: rng }) const aliceChange = bitcoin.ECPair.makeRandom({ network: regtest, rng: rng })
@ -53,51 +53,45 @@ describe('bitcoinjs-lib (transactions)', function () {
const aliceCpkh = bitcoin.payments.p2pkh({ pubkey: aliceChange.publicKey, network: regtest }) const aliceCpkh = bitcoin.payments.p2pkh({ pubkey: aliceChange.publicKey, network: regtest })
// give Alice 2 unspent outputs // give Alice 2 unspent outputs
regtestUtils.faucet(alice1pkh.address, 5e4, function (err, unspent0) { const unspent0 = await regtestUtils.faucet(alice1pkh.address, 5e4)
if (err) return done(err)
regtestUtils.faucet(alice2pkh.address, 7e4, function (err, unspent1) { const unspent1 = await regtestUtils.faucet(alice2pkh.address, 7e4)
if (err) return done(err)
const txb = new bitcoin.TransactionBuilder(regtest) const txb = new bitcoin.TransactionBuilder(regtest)
txb.addInput(unspent0.txId, unspent0.vout) // alice1 unspent txb.addInput(unspent0.txId, unspent0.vout) // alice1 unspent
txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent
txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend"
txb.addOutput(aliceCpkh.address, 1e4) // Alice's change txb.addOutput(aliceCpkh.address, 1e4) // Alice's change
// (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee
// Alice signs each input with the respective private keys // Alice signs each input with the respective private keys
txb.sign(0, alice1) txb.sign(0, alice1)
txb.sign(1, alice2) txb.sign(1, alice2)
// build and broadcast our RegTest network // build and broadcast our RegTest network
regtestUtils.broadcast(txb.build().toHex(), done) await regtestUtils.broadcast(txb.build().toHex())
// to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 // 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', function (done) { it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => {
const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const keyPair = bitcoin.ECPair.makeRandom({ network: regtest })
const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: regtest }) const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: regtest })
regtestUtils.faucet(p2pkh.address, 2e5, function (err, unspent) { const unspent = await regtestUtils.faucet(p2pkh.address, 2e5)
if (err) return done(err)
const txb = new bitcoin.TransactionBuilder(regtest) const txb = new bitcoin.TransactionBuilder(regtest)
const data = Buffer.from('bitcoinjs-lib', 'utf8') const data = Buffer.from('bitcoinjs-lib', 'utf8')
const embed = bitcoin.payments.embed({ data: [data] }) const embed = bitcoin.payments.embed({ data: [data] })
txb.addInput(unspent.txId, unspent.vout) txb.addInput(unspent.txId, unspent.vout)
txb.addOutput(embed.output, 1000) txb.addOutput(embed.output, 1000)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5)
txb.sign(0, keyPair) txb.sign(0, keyPair)
// build and broadcast to the RegTest network // build and broadcast to the RegTest network
regtestUtils.broadcast(txb.build().toHex(), done) await regtestUtils.broadcast(txb.build().toHex())
})
}) })
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', function (done) { it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => {
const keyPairs = [ const keyPairs = [
bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }),
bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }),
@ -108,118 +102,102 @@ describe('bitcoinjs-lib (transactions)', function () {
const p2ms = bitcoin.payments.p2ms({ m: 2, pubkeys: pubkeys, network: regtest }) const p2ms = bitcoin.payments.p2ms({ m: 2, pubkeys: pubkeys, network: regtest })
const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network: regtest }) const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network: regtest })
regtestUtils.faucet(p2sh.address, 2e4, function (err, unspent) { const unspent = await regtestUtils.faucet(p2sh.address, 2e4)
if (err) return done(err)
const txb = new bitcoin.TransactionBuilder(regtest) const txb = new bitcoin.TransactionBuilder(regtest)
txb.addInput(unspent.txId, unspent.vout) txb.addInput(unspent.txId, unspent.vout)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4)
txb.sign(0, keyPairs[0], p2sh.redeem.output) txb.sign(0, keyPairs[0], p2sh.redeem.output)
txb.sign(0, keyPairs[2], p2sh.redeem.output) txb.sign(0, keyPairs[2], p2sh.redeem.output)
const tx = txb.build() const tx = txb.build()
// build and broadcast to the Bitcoin RegTest network // build and broadcast to the Bitcoin RegTest network
regtestUtils.broadcast(tx.toHex(), function (err) { await regtestUtils.broadcast(tx.toHex())
if (err) return done(err)
regtestUtils.verify({ await regtestUtils.verify({
txId: tx.getId(), txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS, address: regtestUtils.RANDOM_ADDRESS,
vout: 0, vout: 0,
value: 1e4 value: 1e4
}, done)
})
}) })
}) })
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', function (done) { it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => {
const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const keyPair = bitcoin.ECPair.makeRandom({ network: regtest })
const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest })
const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }) const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest })
regtestUtils.faucet(p2sh.address, 5e4, function (err, unspent) { const unspent = await regtestUtils.faucet(p2sh.address, 5e4)
if (err) return done(err)
const txb = new bitcoin.TransactionBuilder(regtest) const txb = new bitcoin.TransactionBuilder(regtest)
txb.addInput(unspent.txId, unspent.vout) txb.addInput(unspent.txId, unspent.vout)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4)
txb.sign(0, keyPair, p2sh.redeem.output, null, unspent.value) txb.sign(0, keyPair, p2sh.redeem.output, null, unspent.value)
const tx = txb.build() const tx = txb.build()
// build and broadcast to the Bitcoin RegTest network // build and broadcast to the Bitcoin RegTest network
regtestUtils.broadcast(tx.toHex(), function (err) { await regtestUtils.broadcast(tx.toHex())
if (err) return done(err)
regtestUtils.verify({ await regtestUtils.verify({
txId: tx.getId(), txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS, address: regtestUtils.RANDOM_ADDRESS,
vout: 0, vout: 0,
value: 2e4 value: 2e4
}, done)
})
}) })
}) })
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', function (done) { it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => {
const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const keyPair = bitcoin.ECPair.makeRandom({ network: regtest })
const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest })
regtestUtils.faucetComplex(p2wpkh.address, 5e4, function (err, unspent) { const unspent = await regtestUtils.faucetComplex(p2wpkh.address, 5e4)
if (err) return done(err)
// XXX: build the Transaction w/ a P2WPKH input // XXX: build the Transaction w/ a P2WPKH input
const txb = new bitcoin.TransactionBuilder(regtest) const txb = new bitcoin.TransactionBuilder(regtest)
txb.addInput(unspent.txId, unspent.vout, null, p2wpkh.output) // NOTE: provide the prevOutScript! txb.addInput(unspent.txId, unspent.vout, null, p2wpkh.output) // NOTE: provide the prevOutScript!
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4)
txb.sign(0, keyPair, null, null, unspent.value) // NOTE: no redeem script txb.sign(0, keyPair, null, null, unspent.value) // NOTE: no redeem script
const tx = txb.build() const tx = txb.build()
// build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network
regtestUtils.broadcast(tx.toHex(), function (err) { await regtestUtils.broadcast(tx.toHex())
if (err) return done(err)
regtestUtils.verify({ await regtestUtils.verify({
txId: tx.getId(), txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS, address: regtestUtils.RANDOM_ADDRESS,
vout: 0, vout: 0,
value: 2e4 value: 2e4
}, done)
})
}) })
}) })
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', function (done) { it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => {
const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const keyPair = bitcoin.ECPair.makeRandom({ network: regtest })
const p2pk = bitcoin.payments.p2pk({ pubkey: keyPair.publicKey, network: regtest }) const p2pk = bitcoin.payments.p2pk({ pubkey: keyPair.publicKey, network: regtest })
const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }) const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest })
regtestUtils.faucetComplex(p2wsh.address, 5e4, function (err, unspent) { const unspent = await regtestUtils.faucetComplex(p2wsh.address, 5e4)
if (err) return done(err)
// XXX: build the Transaction w/ a P2WSH input // XXX: build the Transaction w/ a P2WSH input
const txb = new bitcoin.TransactionBuilder(regtest) const txb = new bitcoin.TransactionBuilder(regtest)
txb.addInput(unspent.txId, unspent.vout, null, p2wsh.output) // NOTE: provide the prevOutScript! txb.addInput(unspent.txId, unspent.vout, null, p2wsh.output) // NOTE: provide the prevOutScript!
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4)
txb.sign(0, keyPair, null, null, 5e4, p2wsh.redeem.output) // NOTE: provide a witnessScript! txb.sign(0, keyPair, null, null, 5e4, p2wsh.redeem.output) // NOTE: provide a witnessScript!
const tx = txb.build() const tx = txb.build()
// build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network
regtestUtils.broadcast(tx.toHex(), function (err) { await regtestUtils.broadcast(tx.toHex())
if (err) return done(err)
regtestUtils.verify({ await regtestUtils.verify({
txId: tx.getId(), txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS, address: regtestUtils.RANDOM_ADDRESS,
vout: 0, vout: 0,
value: 2e4 value: 2e4
}, done)
})
}) })
}) })
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', function (done) { it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => {
const keyPairs = [ const keyPairs = [
bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }),
bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }),
@ -232,29 +210,25 @@ describe('bitcoinjs-lib (transactions)', function () {
const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network: regtest }) const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network: regtest })
const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network: regtest }) const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network: regtest })
regtestUtils.faucet(p2sh.address, 6e4, function (err, unspent) { const unspent = await regtestUtils.faucet(p2sh.address, 6e4)
if (err) return done(err)
const txb = new bitcoin.TransactionBuilder(regtest) const txb = new bitcoin.TransactionBuilder(regtest)
txb.addInput(unspent.txId, unspent.vout, null, p2sh.output) txb.addInput(unspent.txId, unspent.vout, null, p2sh.output)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4)
txb.sign(0, keyPairs[0], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) txb.sign(0, keyPairs[0], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output)
txb.sign(0, keyPairs[2], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) txb.sign(0, keyPairs[2], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output)
txb.sign(0, keyPairs[3], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) txb.sign(0, keyPairs[3], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output)
const tx = txb.build() const tx = txb.build()
// build and broadcast to the Bitcoin RegTest network // build and broadcast to the Bitcoin RegTest network
regtestUtils.broadcast(tx.toHex(), function (err) { await regtestUtils.broadcast(tx.toHex())
if (err) return done(err)
regtestUtils.verify({ await regtestUtils.verify({
txId: tx.getId(), txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS, address: regtestUtils.RANDOM_ADDRESS,
vout: 0, vout: 0,
value: 3e4 value: 3e4
}, done)
})
}) })
}) })