Fix tests Missing Input
This commit is contained in:
parent
1c24201a46
commit
8ec1911a26
7 changed files with 397 additions and 459 deletions
|
@ -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,
|
||||||
|
|
|
@ -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 () {
|
||||||
|
|
|
@ -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 () {
|
||||||
|
|
|
@ -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()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue