2018-05-30 02:23:55 +02:00
/* global describe, it, before */
2017-12-01 03:13:55 +01:00
2018-06-25 08:24:37 +02:00
const assert = require ( 'assert' )
const bitcoin = require ( '../../' )
const regtestUtils = require ( './_regtest' )
const regtest = regtestUtils . network
const bip68 = require ( 'bip68' )
2017-12-01 03:13:55 +01:00
2018-06-25 08:24:37 +02:00
const alice = bitcoin . ECPair . fromWIF ( 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe' , regtest )
const bob = bitcoin . ECPair . fromWIF ( 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x' , regtest )
2018-08-22 05:23:56 +02:00
const charles = bitcoin . ECPair . fromWIF ( 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf' , regtest )
const dave = bitcoin . ECPair . fromWIF ( 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx' , regtest )
2017-12-01 03:13:55 +01:00
describe ( 'bitcoinjs-lib (transactions w/ CSV)' , function ( ) {
2018-05-30 02:23:55 +02:00
// force update MTP
before ( function ( done ) {
regtestUtils . mine ( 11 , done )
} )
2018-06-25 08:37:45 +02:00
const hashType = bitcoin . Transaction . SIGHASH _ALL
2017-12-01 03:13:55 +01:00
// IF MTP (from when confirmed) > seconds, aQ can redeem
function csvCheckSigOutput ( aQ , bQ , sequence ) {
return bitcoin . script . compile ( [
2018-09-03 08:53:09 +02:00
/* eslint-disable indent */
2017-12-01 03:13:55 +01:00
bitcoin . opcodes . OP _IF ,
2018-09-03 08:53:09 +02:00
bitcoin . script . number . encode ( sequence ) ,
bitcoin . opcodes . OP _CHECKSEQUENCEVERIFY ,
bitcoin . opcodes . OP _DROP ,
2017-12-01 03:13:55 +01:00
bitcoin . opcodes . OP _ELSE ,
2018-09-03 08:53:09 +02:00
bQ . publicKey ,
bitcoin . opcodes . OP _CHECKSIGVERIFY ,
2017-12-01 03:13:55 +01:00
bitcoin . opcodes . OP _ENDIF ,
2018-05-30 03:19:46 +02:00
aQ . publicKey ,
2017-12-01 03:13:55 +01:00
bitcoin . opcodes . OP _CHECKSIG
2018-09-03 08:53:09 +02:00
/* eslint-enable indent */
2017-12-01 03:13:55 +01:00
] )
}
2018-09-03 08:53:09 +02:00
// 2 of 3 multisig of bQ, cQ, dQ,
// but after sequence1 time, aQ can allow the multisig to become 1 of 3.
// but after sequence2 time, aQ can sign for the output all by themself.
// Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example
// Note: bitcoinjs-lib will not offer specific support for problems with
// advanced script usages such as below. Use at your own risk.
2018-08-22 05:23:56 +02:00
function complexCsvOutput ( aQ , bQ , cQ , dQ , sequence1 , sequence2 ) {
return bitcoin . script . compile ( [
2018-09-03 08:53:09 +02:00
/* eslint-disable indent */
2018-08-22 07:41:41 +02:00
bitcoin . opcodes . OP _IF ,
2018-09-03 08:53:09 +02:00
bitcoin . opcodes . OP _IF ,
bitcoin . opcodes . OP _2 ,
bitcoin . opcodes . OP _ELSE ,
bitcoin . script . number . encode ( sequence1 ) ,
bitcoin . opcodes . OP _CHECKSEQUENCEVERIFY ,
bitcoin . opcodes . OP _DROP ,
aQ . publicKey ,
bitcoin . opcodes . OP _CHECKSIGVERIFY ,
bitcoin . opcodes . OP _1 ,
bitcoin . opcodes . OP _ENDIF ,
bQ . publicKey ,
cQ . publicKey ,
dQ . publicKey ,
bitcoin . opcodes . OP _3 ,
bitcoin . opcodes . OP _CHECKMULTISIG ,
2018-08-22 07:41:41 +02:00
bitcoin . opcodes . OP _ELSE ,
2018-09-03 08:53:09 +02:00
bitcoin . script . number . encode ( sequence2 ) ,
bitcoin . opcodes . OP _CHECKSEQUENCEVERIFY ,
bitcoin . opcodes . OP _DROP ,
aQ . publicKey ,
bitcoin . opcodes . OP _CHECKSIG ,
2018-08-22 07:41:41 +02:00
bitcoin . opcodes . OP _ENDIF
2018-09-03 08:53:09 +02:00
/* eslint-enable indent */
2018-08-22 05:23:56 +02:00
] )
}
2017-12-01 03:13:55 +01:00
// expiry will pass, {Alice's signature} OP_TRUE
2018-08-22 07:33:58 +02:00
it ( 'can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)' , function ( done ) {
2017-12-01 03:13:55 +01:00
regtestUtils . height ( function ( err , height ) {
if ( err ) return done ( err )
// 5 blocks from now
2018-06-25 08:37:45 +02:00
const sequence = bip68 . encode ( { blocks : 5 } )
2018-07-03 14:54:24 +02:00
const p2sh = bitcoin . payments . p2sh ( {
redeem : {
output : csvCheckSigOutput ( alice , bob , sequence )
} ,
network : regtest
} )
2017-12-01 03:13:55 +01:00
// fund the P2SH(CSV) address
2018-07-03 14:54:24 +02:00
regtestUtils . faucet ( p2sh . address , 1e5 , function ( err , unspent ) {
2017-12-01 03:13:55 +01:00
if ( err ) return done ( err )
2018-06-25 08:37:45 +02:00
const txb = new bitcoin . TransactionBuilder ( regtest )
2017-12-01 03:13:55 +01:00
txb . addInput ( unspent . txId , unspent . vout , sequence )
txb . addOutput ( regtestUtils . RANDOM _ADDRESS , 7e4 )
// {Alice's signature} OP_TRUE
2018-06-25 08:37:45 +02:00
const tx = txb . buildIncomplete ( )
2018-07-03 14:54:24 +02:00
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
2017-12-01 03:13:55 +01:00
tx . setInputScript ( 0 , redeemScriptSig )
// TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
// ...
// into the future!
regtestUtils . mine ( 10 , 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 in the future, {Alice's signature} OP_TRUE
2018-08-22 07:33:58 +02:00
it ( 'can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)' , function ( done ) {
2017-12-01 03:13:55 +01:00
// two hours after confirmation
2018-06-25 08:37:45 +02:00
const sequence = bip68 . encode ( { seconds : 7168 } )
2018-07-03 14:54:24 +02:00
const p2sh = bitcoin . payments . p2sh ( {
network : regtest ,
redeem : {
output : csvCheckSigOutput ( alice , bob , sequence )
}
} )
2017-12-01 03:13:55 +01:00
// fund the P2SH(CSV) address
2018-07-03 14:54:24 +02:00
regtestUtils . faucet ( p2sh . address , 2e4 , function ( err , unspent ) {
2017-12-01 03:13:55 +01:00
if ( err ) return done ( err )
2018-06-25 08:37:45 +02:00
const txb = new bitcoin . TransactionBuilder ( regtest )
2017-12-01 03:13:55 +01:00
txb . addInput ( unspent . txId , unspent . vout , sequence )
txb . addOutput ( regtestUtils . RANDOM _ADDRESS , 1e4 )
// {Alice's signature} OP_TRUE
2018-06-25 08:37:45 +02:00
const tx = txb . buildIncomplete ( )
2018-07-03 14:54:24 +02:00
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 . script . signature . encode ( bob . sign ( signatureHash ) , hashType ) ,
bitcoin . opcodes . OP _TRUE
] )
}
} ) . input
2017-12-01 03:13:55 +01:00
tx . setInputScript ( 0 , redeemScriptSig )
regtestUtils . broadcast ( tx . toHex ( ) , function ( err ) {
assert . throws ( function ( ) {
if ( err ) throw err
} , /Error: 64: non-BIP68-final/ )
done ( )
} )
} )
} )
2018-08-22 05:23:56 +02:00
// Check first combination of complex CSV, 2 of 3
2018-08-22 07:33:58 +02:00
it ( 'can create (and broadcast via 3PBP) a Transaction where Bob and Charles can send (complex CHECKSEQUENCEVERIFY)' , function ( done ) {
2018-08-22 05:23:56 +02:00
regtestUtils . height ( function ( err , height ) {
if ( err ) return done ( err )
2018-08-22 07:18:21 +02:00
// 2 blocks from now
const sequence1 = bip68 . encode ( { blocks : 2 } )
2018-08-22 05:23:56 +02:00
// 5 blocks from now
2018-08-22 07:18:21 +02:00
const sequence2 = bip68 . encode ( { blocks : 5 } )
2018-08-22 05:23:56 +02:00
const p2sh = bitcoin . payments . p2sh ( {
redeem : {
output : complexCsvOutput ( alice , bob , charles , dave , sequence1 , sequence2 )
} ,
network : regtest
} )
// fund the P2SH(CCSV) address
regtestUtils . faucet ( p2sh . address , 1e5 , function ( err , unspent ) {
if ( err ) return done ( err )
const txb = new bitcoin . TransactionBuilder ( regtest )
txb . addInput ( unspent . txId , unspent . vout )
txb . addOutput ( regtestUtils . RANDOM _ADDRESS , 7e4 )
// OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE
const tx = txb . buildIncomplete ( )
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 . opcodes . OP _0 ,
bitcoin . script . signature . encode ( bob . sign ( signatureHash ) , hashType ) ,
bitcoin . script . signature . encode ( charles . sign ( signatureHash ) , hashType ) ,
bitcoin . opcodes . OP _TRUE ,
bitcoin . opcodes . OP _TRUE
] )
}
} ) . 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 : 7e4
} , done )
} )
} )
} )
} )
2018-09-03 08:53:09 +02:00
// Check first combination of complex CSV, mediator + 1 of 3 after 2 blocks
it ( 'can create (and broadcast via 3PBP) a Transaction where Alice (mediator) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)' , function ( done ) {
2018-08-22 05:23:56 +02:00
regtestUtils . height ( function ( err , height ) {
if ( err ) return done ( err )
2018-08-22 07:18:21 +02:00
// 2 blocks from now
const sequence1 = bip68 . encode ( { blocks : 2 } )
2018-08-22 05:23:56 +02:00
// 5 blocks from now
2018-08-22 07:18:21 +02:00
const sequence2 = bip68 . encode ( { blocks : 5 } )
2018-08-22 05:23:56 +02:00
const p2sh = bitcoin . payments . p2sh ( {
redeem : {
output : complexCsvOutput ( alice , bob , charles , dave , sequence1 , sequence2 )
} ,
network : regtest
} )
// fund the P2SH(CCSV) address
regtestUtils . faucet ( p2sh . address , 1e5 , function ( err , unspent ) {
if ( err ) return done ( err )
const txb = new bitcoin . TransactionBuilder ( regtest )
txb . addInput ( unspent . txId , unspent . vout , sequence1 ) // Set sequence1 for input
txb . addOutput ( regtestUtils . RANDOM _ADDRESS , 7e4 )
2018-09-03 08:53:09 +02:00
// OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE
2018-08-22 05:23:56 +02:00
const tx = txb . buildIncomplete ( )
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 . opcodes . OP _0 ,
bitcoin . script . signature . encode ( bob . sign ( signatureHash ) , hashType ) ,
bitcoin . script . signature . encode ( alice . sign ( signatureHash ) , hashType ) ,
bitcoin . opcodes . OP _0 ,
bitcoin . opcodes . OP _TRUE
] )
}
} ) . input
tx . setInputScript ( 0 , redeemScriptSig )
2018-08-22 07:18:21 +02:00
// Wait 2 blocks
regtestUtils . mine ( 2 , function ( err ) {
2018-08-22 05:23:56 +02:00
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 )
} )
} )
} )
} )
} )
2018-09-03 08:53:09 +02:00
// Check first combination of complex CSV, mediator after 5 blocks
it ( 'can create (and broadcast via 3PBP) a Transaction where Alice (mediator) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)' , function ( done ) {
2018-08-22 05:23:56 +02:00
regtestUtils . height ( function ( err , height ) {
if ( err ) return done ( err )
2018-08-22 07:18:21 +02:00
// 2 blocks from now
const sequence1 = bip68 . encode ( { blocks : 2 } )
2018-08-22 05:23:56 +02:00
// 5 blocks from now
2018-08-22 07:18:21 +02:00
const sequence2 = bip68 . encode ( { blocks : 5 } )
2018-08-22 05:23:56 +02:00
const p2sh = bitcoin . payments . p2sh ( {
redeem : {
output : complexCsvOutput ( alice , bob , charles , dave , sequence1 , sequence2 )
} ,
network : regtest
} )
// fund the P2SH(CCSV) address
regtestUtils . faucet ( p2sh . address , 1e5 , function ( err , unspent ) {
if ( err ) return done ( err )
const txb = new bitcoin . TransactionBuilder ( regtest )
txb . addInput ( unspent . txId , unspent . vout , sequence2 ) // Set sequence2 for input
txb . addOutput ( regtestUtils . RANDOM _ADDRESS , 7e4 )
2018-09-03 08:53:09 +02:00
// {Alice mediator sig} OP_FALSE
2018-08-22 05:23:56 +02:00
const tx = txb . buildIncomplete ( )
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 _0
] )
}
} ) . input
tx . setInputScript ( 0 , redeemScriptSig )
2018-08-22 07:18:21 +02:00
// Wait 5 blocks
regtestUtils . mine ( 5 , function ( err ) {
2018-08-22 05:23:56 +02:00
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 )
} )
} )
} )
} )
} )
2017-12-01 03:13:55 +01:00
} )