|
|
@ -7,6 +7,65 @@ const regtest = regtestUtils.network |
|
|
|
// See bottom of file for some helper functions used to make the payment objects needed.
|
|
|
|
|
|
|
|
describe('bitcoinjs-lib (transactions with psbt)', () => { |
|
|
|
it('can create a 1-to-1 Transaction', () => { |
|
|
|
const alice = bitcoin.ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr') |
|
|
|
const psbt = new bitcoin.Psbt() |
|
|
|
psbt.setVersion(2) // These are defaults. This line is not needed.
|
|
|
|
psbt.setLocktime(0) // These are defaults. This line is not needed.
|
|
|
|
psbt.addInput({ |
|
|
|
// if hash is string, txid, if hash is Buffer, is reversed compared to txid
|
|
|
|
hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e', |
|
|
|
index: 0, |
|
|
|
sequence: 0xffffffff, // These are defaults. This line is not needed.
|
|
|
|
|
|
|
|
// non-segwit inputs now require passing the whole previous tx as Buffer
|
|
|
|
nonWitnessUtxo: Buffer.from( |
|
|
|
'0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' + |
|
|
|
'452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + |
|
|
|
'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + |
|
|
|
'9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + |
|
|
|
'631e5e1e66009ce3710ceea5b1ad13ffffffff01' + |
|
|
|
// value in satoshis (Int64LE) = 0x015f90 = 90000
|
|
|
|
'905f010000000000' + |
|
|
|
// scriptPubkey length
|
|
|
|
'19' + |
|
|
|
// scriptPubkey
|
|
|
|
'76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' + |
|
|
|
// locktime
|
|
|
|
'00000000', |
|
|
|
'hex', |
|
|
|
), |
|
|
|
|
|
|
|
// // If this input was segwit, instead of nonWitnessUtxo, you would add
|
|
|
|
// // a witnessUtxo as follows. The scriptPubkey and the value only are needed.
|
|
|
|
// witnessUtxo: {
|
|
|
|
// script: Buffer.from(
|
|
|
|
// '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac',
|
|
|
|
// 'hex',
|
|
|
|
// ),
|
|
|
|
// value: 90000,
|
|
|
|
// },
|
|
|
|
|
|
|
|
// Not featured here: redeemScript. A Buffer of the redeemScript
|
|
|
|
}) |
|
|
|
psbt.addOutput({ |
|
|
|
address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', |
|
|
|
value: 80000 |
|
|
|
}) |
|
|
|
psbt.signInput(0, alice) |
|
|
|
psbt.validateSignatures(0) |
|
|
|
psbt.finalizeAllInputs() |
|
|
|
assert.strictEqual( |
|
|
|
psbt.extractTransaction().toHex(), |
|
|
|
'02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' + |
|
|
|
'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + |
|
|
|
'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + |
|
|
|
'9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + |
|
|
|
'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + |
|
|
|
'08a22724efa6f6a07b0ec4c79aa88ac00000000', |
|
|
|
) |
|
|
|
}) |
|
|
|
|
|
|
|
it('can create (and broadcast via 3PBP) a typical Transaction', async () => { |
|
|
|
// these are { payment: Payment; keys: ECPair[] }
|
|
|
|
const alice1 = createPayment('p2pkh') |
|
|
@ -64,6 +123,12 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { |
|
|
|
// final1.combine(final2) would give the exact same result
|
|
|
|
psbt.combine(final1, final2) |
|
|
|
|
|
|
|
// Finalizer wants to check all signatures are valid before finalizing.
|
|
|
|
// If the finalizer wants to check for specific pubkeys, the second arg
|
|
|
|
// can be passed. See the first multisig example below.
|
|
|
|
assert.strictEqual(psbt.validateSignatures(0), true) |
|
|
|
assert.strictEqual(psbt.validateSignatures(1), true) |
|
|
|
|
|
|
|
// This step it new. Since we separate the signing operation and
|
|
|
|
// the creation of the scriptSig and witness stack, we are able to
|
|
|
|
psbt.finalizeAllInputs() |
|
|
@ -94,6 +159,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { |
|
|
|
}) |
|
|
|
.signInput(0, alice1.keys[0]) |
|
|
|
|
|
|
|
assert.strictEqual(psbt.validateSignatures(0), true) |
|
|
|
psbt.finalizeAllInputs() |
|
|
|
|
|
|
|
// build and broadcast to the RegTest network
|
|
|
@ -122,6 +188,11 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { |
|
|
|
.signInput(0, multisig.keys[0]) |
|
|
|
.signInput(0, multisig.keys[2]) |
|
|
|
|
|
|
|
assert.strictEqual(psbt.validateSignatures(0), true) |
|
|
|
assert.strictEqual(psbt.validateSignatures(0, multisig.keys[0].publicKey), true) |
|
|
|
assert.throws(() => { |
|
|
|
psbt.validateSignatures(0, multisig.keys[3].publicKey) |
|
|
|
}, new RegExp('No signatures for this pubkey')) |
|
|
|
psbt.finalizeAllInputs() |
|
|
|
|
|
|
|
const tx = psbt.extractTransaction() |
|
|
@ -158,6 +229,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { |
|
|
|
}) |
|
|
|
.signInput(0, p2sh.keys[0]) |
|
|
|
|
|
|
|
assert.strictEqual(psbt.validateSignatures(0), true) |
|
|
|
psbt.finalizeAllInputs() |
|
|
|
|
|
|
|
const tx = psbt.extractTransaction() |
|
|
@ -196,6 +268,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { |
|
|
|
}) |
|
|
|
.signInput(0, p2wpkh.keys[0]) |
|
|
|
|
|
|
|
assert.strictEqual(psbt.validateSignatures(0), true) |
|
|
|
psbt.finalizeAllInputs() |
|
|
|
|
|
|
|
const tx = psbt.extractTransaction() |
|
|
@ -232,6 +305,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { |
|
|
|
}) |
|
|
|
.signInput(0, p2wsh.keys[0]) |
|
|
|
|
|
|
|
assert.strictEqual(psbt.validateSignatures(0), true) |
|
|
|
psbt.finalizeAllInputs() |
|
|
|
|
|
|
|
const tx = psbt.extractTransaction() |
|
|
@ -271,6 +345,11 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { |
|
|
|
.signInput(0, p2sh.keys[2]) |
|
|
|
.signInput(0, p2sh.keys[3]) |
|
|
|
|
|
|
|
assert.strictEqual(psbt.validateSignatures(0), true) |
|
|
|
assert.strictEqual(psbt.validateSignatures(0, p2sh.keys[3].publicKey), true) |
|
|
|
assert.throws(() => { |
|
|
|
psbt.validateSignatures(0, p2sh.keys[1].publicKey) |
|
|
|
}, new RegExp('No signatures for this pubkey')) |
|
|
|
psbt.finalizeAllInputs() |
|
|
|
|
|
|
|
const tx = psbt.extractTransaction() |
|
|
@ -287,7 +366,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
function createPayment(_type) { |
|
|
|
function createPayment(_type, network) { |
|
|
|
network = network || regtest |
|
|
|
const splitType = _type.split('-').reverse(); |
|
|
|
const isMultisig = splitType[0].slice(0, 4) === 'p2ms'; |
|
|
|
const keys = []; |
|
|
@ -297,11 +377,11 @@ function createPayment(_type) { |
|
|
|
m = parseInt(match[1]) |
|
|
|
let n = parseInt(match[2]) |
|
|
|
while (n > 1) { |
|
|
|
keys.push(bitcoin.ECPair.makeRandom({ network: regtest })); |
|
|
|
keys.push(bitcoin.ECPair.makeRandom({ network })); |
|
|
|
n-- |
|
|
|
} |
|
|
|
} |
|
|
|
keys.push(bitcoin.ECPair.makeRandom({ network: regtest })); |
|
|
|
keys.push(bitcoin.ECPair.makeRandom({ network })); |
|
|
|
|
|
|
|
let payment; |
|
|
|
splitType.forEach(type => { |
|
|
@ -309,17 +389,17 @@ function createPayment(_type) { |
|
|
|
payment = bitcoin.payments.p2ms({ |
|
|
|
m, |
|
|
|
pubkeys: keys.map(key => key.publicKey).sort(), |
|
|
|
network: regtest, |
|
|
|
network, |
|
|
|
}); |
|
|
|
} else if (['p2sh', 'p2wsh'].indexOf(type) > -1) { |
|
|
|
payment = bitcoin.payments[type]({ |
|
|
|
redeem: payment, |
|
|
|
network: regtest, |
|
|
|
network, |
|
|
|
}); |
|
|
|
} else { |
|
|
|
payment = bitcoin.payments[type]({ |
|
|
|
pubkey: keys[0].publicKey, |
|
|
|
network: regtest, |
|
|
|
network, |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|