depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

commit 579cd18c2560716b4924a73ec097720731745502
parent 10edb1f772f2774b97a05f4388d5ab15cb2a7e65
Author: Antoine A <>
Date:   Mon, 10 Jan 2022 18:26:57 +0100

btc-wire: support and test outgoing transactions conflict

Diffstat:
Mbtc-wire/src/bin/btc-wire-cli.rs | 27+++++++++++++++++++++++----
Mbtc-wire/src/main.rs | 139++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mbtc-wire/src/rpc.rs | 20++++++++++++++++----
Mmakefile | 3++-
Mscript/setup.sh | 50++++++++++++++++++++++++++++++--------------------
Ascript/test_btc_conflict.sh | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscript/test_btc_fail.sh | 8++------
Dscript/test_btc_fork.sh | 140-------------------------------------------------------------------------------
Mscript/test_btc_lifetime.sh | 4+---
Mscript/test_btc_reconnect.sh | 6++----
Ascript/test_btc_reorg.sh | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscript/test_btc_stress.sh | 6++----
Mscript/test_btc_wire.sh | 9++-------
13 files changed, 419 insertions(+), 247 deletions(-)

diff --git a/btc-wire/src/bin/btc-wire-cli.rs b/btc-wire/src/bin/btc-wire-cli.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use bitcoin::{Address, Amount, Network}; use btc_wire::{ config::BitcoinConfig, - rpc::{BtcRpc, Error, ErrorCode}, + rpc::{BtcRpc, Error, ErrorCode, Category}, rpc_utils::default_data_dir, test::rand_key, }; @@ -23,6 +23,7 @@ struct Args { enum Cmd { Transfer(TransferCmd), NextBlock(NextBlockCmd), + Abandon(AbandonCmd), } #[derive(argh::FromArgs)] @@ -55,6 +56,15 @@ struct NextBlockCmd { to: String, } +#[derive(argh::FromArgs)] +#[argh(subcommand, name = "abandon")] +/// Abandon all unconfirmed transaction +struct AbandonCmd { + #[argh(option, short = 't', default = "String::from(\"wire\")")] + /// sender wallet + from: String, +} + struct App { config: BitcoinConfig, client: BtcRpc, @@ -101,6 +111,7 @@ impl App { fn main() { let args: Args = argh::from_env(); + let mut app = App::start(args.datadir); match args.cmd { Cmd::Transfer(TransferCmd { key: _key, @@ -108,16 +119,24 @@ fn main() { to, amount, }) => { - let mut app = App::start(args.datadir); let (mut client, _) = app.auto_wallet(&from); let (_, to) = app.auto_wallet(&to); - client + let tx = client .send_segwit_key(&to, &Amount::from_btc(amount).unwrap(), &rand_key()) .unwrap(); + println!("{}", tx); } Cmd::NextBlock(NextBlockCmd { to }) => { - let mut app = App::start(args.datadir); app.next_block(&to); } + Cmd::Abandon(AbandonCmd { from }) => { + let (mut wire, _) = app.auto_wallet(&from); + let list = wire.list_since_block(None, 1, false).unwrap(); + for tx in list.transactions { + if tx.category == Category::Send && tx.confirmations == 0 { + wire.abandon_tx(&tx.txid).unwrap(); + } + } + } } } diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs @@ -77,18 +77,19 @@ fn worker(mut rpc: AutoReconnectRPC, mut db: AutoReconnectSql, config: &Config) "UPDATE tx_out SET status=$1, txid=$2 WHERE id=$3", &[&(TxStatus::Sent as i16), &tx_id.as_ref(), &id], )?; + tx.commit()?; let amount = btc_to_taler(&amount.to_signed().unwrap()); info!(">> {} {} in {} to {}", amount, base32(wtid), tx_id, addr); } Err(e) => { - info!("sender: RPC - {}", e); tx.execute( "UPDATE tx_out SET status=$1 WHERE id=$2", &[&(TxStatus::Delayed as i16), &id], )?; + tx.commit()?; + info!("sender: RPC - {}", e); } } - tx.commit()?; } Ok(row.is_some()) } @@ -116,27 +117,28 @@ fn worker(mut rpc: AutoReconnectRPC, mut db: AutoReconnectSql, config: &Config) fail_point("Skip send_op_return", 0.2)?; match rpc.bounce(&bounced, fee, &metadata) { Ok(it) => { - info!("|| {} in {}", &bounced, &it); tx.execute( "UPDATE bounce SET txid = $1, status = $2 WHERE id = $3", &[&it.as_ref(), &(BounceStatus::Sent as i16), &id], )?; + tx.commit()?; + info!("|| {} in {}", &bounced, &it); } Err(err) => match err { rpc::Error::RPC { code: ErrorCode::RpcWalletInsufficientFunds | ErrorCode::RpcWalletError, msg, } => { - info!("|| (ignore) {} because {}", &bounced, msg); tx.execute( "UPDATE bounce SET status = $1 WHERE id = $2", &[&(BounceStatus::Ignored as i16), &id], )?; + tx.commit()?; + info!("|| (ignore) {} because {}", &bounced, msg); } _ => Err(err)?, }, } - tx.commit()?; } Ok(row.is_some()) } @@ -195,7 +197,7 @@ fn worker(mut rpc: AutoReconnectRPC, mut db: AutoReconnectSql, config: &Config) Ok(()) })(); if let Err(e) = result { - error!("worker: DB - {}", e); + error!("worker: {}", e); // On failure retry without waiting for notifications skip_notification = true; } else { @@ -258,7 +260,6 @@ fn sync_chain( Category::Send => { match rpc.get_tx_op_return(&id) { Ok((full, bytes)) => { - let mut tx = db.transaction()?; match decode_info(&bytes) { Ok(info) => { match info { @@ -266,77 +267,108 @@ fn sync_chain( let addr = full.details[0].address.as_ref().unwrap(); let amount = btc_to_taler(&full.amount); - let row = tx.query_opt( - "SELECT id, status FROM tx_out WHERE wtid=$1 FOR UPDATE", - &[&wtid.as_ref()], - )?; - if let Some(row) = row { - let _id: i32 = row.get(0); - let status: i16 = row.get(1); - match TxStatus::try_from(status as u8).unwrap() { - TxStatus::Requested | TxStatus::Delayed => { - tx.execute( - "UPDATE tx_out SET status=$1 where id=$2", - &[&(TxStatus::Sent as i16), &_id], - )?; + if confirmations < 0 { + let nb_row = db.execute( + "UPDATE tx_out SET status=$1, txid=NULL where txid=$2", + &[&(TxStatus::Delayed as i16), &id.as_ref()], + )?; + if nb_row > 0 { + warn!( + ">> (conflict) {} in {} to {}", + base32(&wtid), + id, + addr + ); + } + } else { + let mut tx = db.transaction()?; + let row = tx.query_opt( + "SELECT id, status FROM tx_out WHERE wtid=$1 FOR UPDATE", + &[&wtid.as_ref()], + )?; + if let Some(row) = row { + let _id: i32 = row.get(0); + let status: i16 = row.get(1); + match TxStatus::try_from(status as u8).unwrap() { + TxStatus::Requested | TxStatus::Delayed => { + tx.execute( + "UPDATE tx_out SET status=$1 where id=$2", + &[&(TxStatus::Sent as i16), &_id], + )?; + tx.commit()?; + warn!( + ">> (recovered) {} {} in {} to {}", + amount, + base32(&wtid), + id, + addr + ); + } + TxStatus::Sent => {} + } + } else { + let debit_addr = sender_address(rpc, &full)?; + let date = SystemTime::UNIX_EPOCH + + Duration::from_secs(full.time); + // Generate a random request_uid + let mut request_uid = [0; 64]; + OsRng.fill_bytes(&mut request_uid); + let nb = tx.execute( + "INSERT INTO tx_out (_date, amount, wtid, debit_acc, credit_acc, exchange_url, status, txid, request_uid) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (wtid) DO NOTHING", + &[&date, &amount.to_string(), &wtid.as_ref(), &btc_payto_url(&debit_addr).as_ref(), &btc_payto_url(addr).as_ref(), &config.base_url.as_ref(), &(TxStatus::Sent as i16), &id.as_ref(), &request_uid.as_ref()], + )?; + tx.commit()?; + if nb > 0 { warn!( - ">> (recovered) {} {} in {} to {}", + ">> (onchain) {} {} in {} to {}", amount, base32(&wtid), id, addr ); } - TxStatus::Sent => {} - } - } else { - let debit_addr = sender_address(rpc, &full)?; - let date = SystemTime::UNIX_EPOCH - + Duration::from_secs(full.time); - // Generate a random request_uid - let mut request_uid = [0; 64]; - OsRng.fill_bytes(&mut request_uid); - let nb = tx.execute( - "INSERT INTO tx_out (_date, amount, wtid, debit_acc, credit_acc, exchange_url, status, txid, request_uid) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (wtid) DO NOTHING", - &[&date, &amount.to_string(), &wtid.as_ref(), &btc_payto_url(&debit_addr).as_ref(), &btc_payto_url(addr).as_ref(), &config.base_url.as_ref(), &(TxStatus::Sent as i16), &id.as_ref(), &request_uid.as_ref()], - )?; - if nb > 0 { - warn!( - ">> (onchain) {} {} in {} to {}", - amount, - base32(&wtid), - id, - addr - ); } } } Info::Bounce { bounced } => { - let row = tx.query_opt( + if confirmations < 0 { + let nb_row = db.execute( + "UPDATE bounce SET status=$1, txid=NULL where txid=$2", + &[&(BounceStatus::Delayed as i16), &id.as_ref()], + )?; + if nb_row > 0 { + warn!("|| (conflict) {} in {}", &bounced, &id); + } + } else { + let mut tx = db.transaction()?; + let row = tx.query_opt( "SELECT id, status FROM bounce WHERE bounced=$1 FOR UPDATE", &[&bounced.as_ref()], - )?; - if let Some(row) = row { - let _id: i32 = row.get(0); - let status: i16 = row.get(1); - match BounceStatus::try_from(status as u8).unwrap() { + )?; + if let Some(row) = row { + let _id: i32 = row.get(0); + let status: i16 = row.get(1); + match BounceStatus::try_from(status as u8).unwrap() { BounceStatus::Requested | BounceStatus::Delayed => { tx.execute( "UPDATE bounce SET status=$1 where id=$2", &[&(BounceStatus::Sent as i16), &_id], )?; + tx.commit()?; warn!("|| (recovered) {} in {}", &bounced, &id); } BounceStatus::Ignored => error!("watcher: ignored bounce {} found in chain at {}", bounced, id), BounceStatus::Sent => {} } - } else { - let nb = tx.execute( + } else { + let nb = tx.execute( "INSERT INTO bounce (bounced, txid, status) VALUES ($1, $2, $3) ON CONFLICT (txid) DO NOTHING", &[&bounced.as_ref(), &id.as_ref(), &(BounceStatus::Sent as i16)], )?; - if nb > 0 { - warn!("|| (onchain) {} in {}", &bounced, &id); + tx.commit()?; + if nb > 0 { + warn!("|| (onchain) {} in {}", &bounced, &id); + } } } } @@ -344,7 +376,6 @@ fn sync_chain( } Err(err) => warn!("send: decode-info {} - {}", id, err), } - tx.commit()?; } Err(err) => match err { GetOpReturnErr::MissingOpReturn => {} // ignore @@ -429,7 +460,7 @@ fn block_listener(mut rpc: AutoReconnectRPC, mut db: AutoReconnectSql) { Ok(()) })(); if let Err(e) = result { - error!("listener: DB - {}", e); + error!("listener - {}", e); } } } diff --git a/btc-wire/src/rpc.rs b/btc-wire/src/rpc.rs @@ -46,6 +46,8 @@ pub enum Error { RPC { code: ErrorCode, msg: String }, #[error("JSON: {0}")] Json(#[from] serde_json::Error), + #[error("No result or error")] + Null, } pub type Result<T> = std::result::Result<T, Error>; @@ -139,13 +141,16 @@ impl BtcRpc { assert_eq!(self.id, response.id); self.id += 1; + if let Some(ok) = response.result { Ok(ok) } else { - let err = response.error.unwrap(); - Err(Error::RPC { - code: err.code, - msg: err.message, + Err(match response.error { + Some(err) => Error::RPC { + code: err.code, + msg: err.message, + }, + None => Error::Null, }) } } @@ -254,6 +259,13 @@ impl BtcRpc { self.call("listsinceblock", &(hash, confirmation, (), include_remove)) } + pub fn abandon_tx(&mut self, id: &Txid) -> Result<()> { + match self.call("abandontransaction", &[&id]) { + Err(Error::Null) => Ok(()), + i => i, + } + } + pub fn get_tx(&mut self, id: &Txid) -> Result<TransactionFull> { self.call("gettransaction", &(id, (), true)) } diff --git a/makefile b/makefile @@ -10,7 +10,8 @@ test_btc: script/test_btc_lifetime.sh script/test_btc_reconnect.sh script/test_btc_fail.sh - script/test_btc_fork.sh script/test_btc_stress.sh + script/test_btc_conflict.sh + script/test_btc_reorg.sh test: test_gateway test_btc \ No newline at end of file diff --git a/script/setup.sh b/script/setup.sh @@ -17,6 +17,7 @@ function cleanup() { # Install cleanup handler (except for kill -9) trap cleanup EXIT +# Init temporary dirs DIR=$(mktemp -d) BTC_DIR=$DIR/bitcoin BTC_DIR2=$DIR/bitcoin2 @@ -26,6 +27,10 @@ for dir in $BTC_DIR $BTC_DIR2 $DB_DIR log; do mkdir -p $dir done +# Setup command helpers +BTC_CLI="bitcoin-cli -datadir=$BTC_DIR" +BTC_CLI2="bitcoin-cli -datadir=$BTC_DIR2" + # Load test.conf as bash variables function load_config() { cp ${BASH_SOURCE%/*}/conf/${CONFIG:-taler_test.conf} $CONF @@ -70,20 +75,31 @@ function reset_db() { # ----- Bitcoin node ----- # -# Start a bitcoind regtest node +# Start a bitcoind regtest node, generate money, wallet and addresses function init_btc() { cp ${BASH_SOURCE%/*}/conf/bitcoin.conf $BTC_DIR/bitcoin.conf - BTC_CLI="bitcoin-cli -datadir=$BTC_DIR" - bitcoind -datadir=$BTC_DIR &> log/btc.log & + bitcoind -datadir=$BTC_DIR $* &> log/btc.log & BTC_PID="$!" - $BTC_CLI -rpcwait getnetworkinfo > /dev/null + # Wait for RPC server to be online + $BTC_CLI -rpcwait getnetworkinfo > /dev/null + # Load wallets + for wallet in wire client reserve; do + $BTC_CLI createwallet $wallet > /dev/null + done + # Generate addresses + RESERVE=`$BTC_CLI -rpcwallet=reserve getnewaddress` + CLIENT=`$BTC_CLI -rpcwallet=client getnewaddress` + WIRE=`$BTC_CLI -rpcwallet=wire getnewaddress` + # Generate money + mine_btc 101 + $BTC_CLI -rpcwallet=reserve sendtoaddress $CLIENT 10 > /dev/null + mine_btc } # Start a second bitcoind regtest node connected to the first one function init_btc2() { cp ${BASH_SOURCE%/*}/conf/bitcoin2.conf $BTC_DIR2/bitcoin.conf - BTC_CLI2="bitcoin-cli -datadir=$BTC_DIR2" - bitcoind -datadir=$BTC_DIR2 &> log/btc2.log & + bitcoind -datadir=$BTC_DIR2 $* &> log/btc2.log & $BTC_CLI2 -rpcwait getnetworkinfo > /dev/null $BTC_CLI addnode 127.0.0.1:8346 onetry } @@ -102,26 +118,14 @@ function btc2_fork() { # Start a bitcoind regest server in a previously created temporary directory and load wallets function restart_btc() { - bitcoind -datadir=$BTC_DIR &>> log/btc.log & + bitcoind -datadir=$BTC_DIR $* &>> log/btc.log & + BTC_PID="$!" $BTC_CLI -rpcwait getnetworkinfo > /dev/null for wallet in wire client reserve; do $BTC_CLI loadwallet $wallet > /dev/null done } -# Create test wallets, generate money and load addresses in variables -function setup_btc() { - for wallet in wire client reserve; do - $BTC_CLI createwallet $wallet > /dev/null - done - RESERVE=`$BTC_CLI -rpcwallet=reserve getnewaddress` - CLIENT=`$BTC_CLI -rpcwallet=client getnewaddress` - WIRE=`$BTC_CLI -rpcwallet=wire getnewaddress` - mine_btc 101 - $BTC_CLI -rpcwallet=reserve sendtoaddress $CLIENT 10 > /dev/null - mine_btc -} - function stop_btc() { kill $BTC_PID wait $BTC_PID @@ -142,6 +146,12 @@ function next_btc() { mine_btc } +# Remove money from wallet wire +function clear_wallet() { + $BTC_CLI -rpcwallet=wire sendtoaddress $CLIENT `$BTC_CLI -rpcwallet=wire getbalance` "" "" true > /dev/null + mine_btc +} + # Check client and wire balance function check_balance() { local CLIENT_BALANCE=`$BTC_CLI -rpcwallet=client getbalance` diff --git a/script/test_btc_conflict.sh b/script/test_btc_conflict.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +## Test btc_wire ability to handle conflicting outgoing transactions + +set -eu + +source "${BASH_SOURCE%/*}/setup.sh" +SCHEMA=btc.sql + +echo "----- Setup -----" +echo "Load config file" +load_config +echo "Start database" +setup_db +echo "Start bitcoin node" +init_btc +echo "Start second bitcoin node" +init_btc2 +echo "Start btc-wire" +btc_wire +echo "Start gateway" +gateway +echo "" + +echo "----- Conflict send -----" + +echo -n "Making wire transfer to exchange:" +btc-wire-cli -d $BTC_DIR transfer 0.042 > /dev/null +next_btc +check_balance 9.95799209 0.04200000 +echo " OK" + +echo -n "Making wire transfer from exchange:" +taler-exchange-wire-gateway-client \ + -b $BANK_ENDPOINT \ + -C payto://bitcoin/$CLIENT \ + -a BTC:0.004 > /dev/null +sleep 1 +check_balance 9.95799209 0.03799801 +echo " OK" + +echo -n "Abandon pending transaction:" +stop_btc +restart_btc -minrelaytxfee=0.0001 +btc-wire-cli -d $BTC_DIR abandon +check_balance 9.95799209 0.04200000 +echo " OK" + +echo -n "Generate conflict:" +taler-exchange-wire-gateway-client \ + -b $BANK_ENDPOINT \ + -C payto://bitcoin/$CLIENT \ + -a BTC:0.005 > /dev/null +sleep 1 +stop_btc +restart_btc +mine_btc +check_balance 9.96299209 0.03698010 +echo " OK" + +echo -n "Resend conflicting transaction:" +sleep 5 +mine_btc +check_delta "outgoing?delta=-100" "seq 4 5" "0.00" +check_balance 9.96699209 0.03297811 +echo " OK" + +echo "----- Reset -----" +echo "Cleanup" +cleanup +source "${BASH_SOURCE%/*}/setup.sh" +echo "Load config file" +load_config +echo "Start database" +setup_db +echo "Start bitcoin node" +init_btc +echo "Start second bitcoin node" +init_btc2 +echo "Start btc-wire" +btc_wire +echo "Start gateway" +gateway +echo "" + +echo "----- Conflict bounce -----" + +echo -n "Bounce:" +$BTC_CLI -rpcwallet=client sendtoaddress $WIRE 0.04 > /dev/null +mine_btc $CONFIRMATION +sleep 1 +check_balance 9.95999859 0.00001000 +echo " OK" + +echo -n "Abandon pending transaction:" +stop_btc +restart_btc -minrelaytxfee=0.0001 +btc-wire-cli -d $BTC_DIR abandon +check_balance 9.95999859 0.04000000 +echo " OK" + +echo -n "Generate conflict:" +$BTC_CLI -rpcwallet=client sendtoaddress $WIRE 0.05 > /dev/null +mine_btc $CONFIRMATION +sleep 1 +stop_btc +restart_btc +mine_btc +check_balance 9.95994929 0.04001000 +echo " OK" + +echo -n "Resend conflicting transaction:" +sleep 5 +mine_btc +check_balance 9.99993744 0.00002000 +echo " OK" + +echo "All tests passed" +\ No newline at end of file diff --git a/script/test_btc_fail.sh b/script/test_btc_fail.sh @@ -14,8 +14,6 @@ echo "Start database" setup_db echo "Start bitcoin node" init_btc -echo "Setup bitcoin" -setup_btc echo "Start failing btc-wire" fail_btc_wire echo "Start gateway" @@ -28,7 +26,7 @@ echo "----- Handle incoming -----" echo -n "Making wire transfer to exchange:" for n in `$SEQ`; do - btc-wire-cli -d $BTC_DIR transfer 0.000$n + btc-wire-cli -d $BTC_DIR transfer 0.000$n > /dev/null mine_btc # Mine transactions done next_btc # Trigger btc_wire @@ -65,9 +63,7 @@ echo " OK" echo "----- Handle bounce -----" -echo -n "Clear wire wallet:" -$BTC_CLI -rpcwallet=wire sendtoaddress $CLIENT `$BTC_CLI -rpcwallet=wire getbalance` "" "" true > /dev/null -echo " OK" +clear_wallet echo -n "Making incomplete wire transfer to exchange:" for n in `$SEQ`; do diff --git a/script/test_btc_fork.sh b/script/test_btc_fork.sh @@ -1,139 +0,0 @@ -#!/bin/bash - -## Test btc_wire correctness when a blockchain reorganisation occurs - -set -eu - -source "${BASH_SOURCE%/*}/setup.sh" -SCHEMA=btc.sql - -echo "----- Setup -----" -echo "Load config file" -load_config -echo "Start database" -setup_db -echo "Start bitcoin node" -init_btc -echo "Setup bitcoin" -setup_btc -echo "Start second bitcoin node" -init_btc2 -echo "Start btc-wire" -btc_wire -echo "Start gateway" -gateway -echo "" - -# Check btc-wire is running -function up() { - check_up $WIRE_PID btc_wire -} -# Check btc-wire is not running -function down() { - check_down $WIRE_PID btc_wire -} - -SEQ="seq 10 20" - -echo "----- Handle reorg incoming transactions -----" - -echo "Loose second bitcoin node" -btc2_deco - -echo -n "Gen incoming transactions:" -for n in `$SEQ`; do - btc-wire-cli -d $BTC_DIR transfer 0.000$n - mine_btc # Mine transactions -done -next_btc # Trigger btc_wire -check_delta "incoming?delta=-100" "$SEQ" "0.000" -check_balance 9.99826299 0.00165000 -echo " OK" - -echo -n "Perform fork and check btc-wire hard error:" -up -btc2_fork -check_balance 9.99826299 0.00000000 -down -echo " OK" - -echo -n "Check btc-wire hard error on restart:" -btc_wire -sleep 1 -down -echo " OK" - -echo -n "Recover orphaned transactions:" -next_btc -check_balance 9.99826299 0.00165000 -echo " OK" - -echo -n "Check btc-wire heal on restart:" -btc_wire -sleep 1 -up -echo " OK" - -echo "----- Handle reorg outgoing transactions -----" - -echo "Loose second bitcoin node" -btc2_deco - -echo -n "Gen outgoing transactions:" -for n in `$SEQ`; do - taler-exchange-wire-gateway-client \ - -b $BANK_ENDPOINT \ - -C payto://bitcoin/$CLIENT \ - -a BTC:0.0000$n > /dev/null -done -sleep 1 -mine_btc # Mine transactions -check_delta "outgoing?delta=-100" "$SEQ" -check_balance 9.99842799 0.00146311 -echo " OK" - -echo -n "Perform fork and check btc-wire still up:" -up -btc2_fork -check_balance 9.99826299 0.00146311 -up -echo " OK" - -echo -n "Recover orphaned transactions:" -next_btc -check_balance 9.99842799 0.00146311 -echo " OK" - -echo "----- Handle reorg bounce -----" - -echo -n "Clear wire wallet:" -$BTC_CLI -rpcwallet=wire sendtoaddress $CLIENT `$BTC_CLI -rpcwallet=wire getbalance` "" "" true > /dev/null -mine_btc -echo " OK" - -echo "Loose second bitcoin node" -btc2_deco - -echo -n "Generate bounce:" -for n in `$SEQ`; do - $BTC_CLI -rpcwallet=client sendtoaddress $WIRE 0.000$n > /dev/null - mine_btc -done -next_btc -sleep 1 -check_balance "*" 0.00011000 -echo " OK" - -echo -n "Perform fork and check btc-wire still up:" -up -btc2_fork -check_balance "*" 0.00000000 -up -echo " OK" - -echo -n "Recover orphaned transactions:" -next_btc -check_balance "*" 0.00011000 -echo " OK" - -echo "All tests passed" -\ No newline at end of file diff --git a/script/test_btc_lifetime.sh b/script/test_btc_lifetime.sh @@ -16,8 +16,6 @@ echo "Start database" setup_db echo "Start bitcoin node" init_btc -echo "Setup bitcoin" -setup_btc echo "Start btc-wire" btc_wire echo "Start gateway" @@ -35,7 +33,7 @@ echo " OK" echo -n "Do some work:" for n in `$SEQ`; do - btc-wire-cli -d $BTC_DIR transfer 0.000$n + btc-wire-cli -d $BTC_DIR transfer 0.000$n > /dev/null mine_btc # Mine transactions done next_btc # Trigger btc_wire diff --git a/script/test_btc_reconnect.sh b/script/test_btc_reconnect.sh @@ -14,8 +14,6 @@ echo "Start database" setup_db echo "Start bitcoin node" init_btc -echo "Setup bitcoin" -setup_btc echo "Start btc-wire" btc_wire echo "Start gateway" @@ -24,7 +22,7 @@ echo "" echo "----- With DB -----" echo "Making wire transfer to exchange:" -btc-wire-cli -d $BTC_DIR transfer 0.000042 +btc-wire-cli -d $BTC_DIR transfer 0.000042 > /dev/null next_btc check_balance 9.99995009 0.00004200 echo -n "Requesting exchange incoming transaction list:" @@ -37,7 +35,7 @@ pg_ctl stop -D $DB_DIR > /dev/null echo "Making incomplete wire transfer to exchange" $BTC_CLI -rpcwallet=client sendtoaddress $WIRE 0.00042 &> /dev/null echo -n "Making wire transfer to exchange:" -btc-wire-cli -d $BTC_DIR transfer 0.00004 +btc-wire-cli -d $BTC_DIR transfer 0.00004 > /dev/null next_btc check_balance 9.99948077 0.00050200 echo " OK" diff --git a/script/test_btc_reorg.sh b/script/test_btc_reorg.sh @@ -0,0 +1,134 @@ +#!/bin/bash + +## Test btc_wire correctness when a blockchain reorganisation occurs + +set -eu + +source "${BASH_SOURCE%/*}/setup.sh" +SCHEMA=btc.sql + +echo "----- Setup -----" +echo "Load config file" +load_config +echo "Start database" +setup_db +echo "Start bitcoin node" +init_btc +echo "Start second bitcoin node" +init_btc2 +echo "Start btc-wire" +btc_wire +echo "Start gateway" +gateway +echo "" + +# Check btc-wire is running +function up() { + check_up $WIRE_PID btc_wire +} +# Check btc-wire is not running +function down() { + check_down $WIRE_PID btc_wire +} + +SEQ="seq 10 20" + +echo "----- Handle reorg incoming transactions -----" + +echo "Loose second bitcoin node" +btc2_deco + +echo -n "Gen incoming transactions:" +for n in `$SEQ`; do + btc-wire-cli -d $BTC_DIR transfer 0.000$n > /dev/null + mine_btc # Mine transactions +done +next_btc # Trigger btc_wire +check_delta "incoming?delta=-100" "$SEQ" "0.000" +check_balance 9.99826299 0.00165000 +echo " OK" + +echo -n "Perform fork and check btc-wire hard error:" +up +btc2_fork +check_balance 9.99826299 0.00000000 +down +echo " OK" + +echo -n "Check btc-wire hard error on restart:" +btc_wire +sleep 1 +down +echo " OK" + +echo -n "Recover orphaned transactions:" +next_btc +check_balance 9.99826299 0.00165000 +echo " OK" + +echo -n "Check btc-wire heal on restart:" +btc_wire +sleep 1 +up +echo " OK" + +echo "----- Handle reorg outgoing transactions -----" + +echo "Loose second bitcoin node" +btc2_deco + +echo -n "Gen outgoing transactions:" +for n in `$SEQ`; do + taler-exchange-wire-gateway-client \ + -b $BANK_ENDPOINT \ + -C payto://bitcoin/$CLIENT \ + -a BTC:0.0000$n > /dev/null +done +sleep 1 +mine_btc # Mine transactions +check_delta "outgoing?delta=-100" "$SEQ" +check_balance 9.99842799 0.00146311 +echo " OK" + +echo -n "Perform fork and check btc-wire still up:" +up +btc2_fork +check_balance 9.99826299 0.00146311 +up +echo " OK" + +echo -n "Recover orphaned transactions:" +next_btc +check_balance 9.99842799 0.00146311 +echo " OK" + +echo "----- Handle reorg bounce -----" + +clear_wallet + +echo "Loose second bitcoin node" +btc2_deco + +echo -n "Generate bounce:" +for n in `$SEQ`; do + $BTC_CLI -rpcwallet=client sendtoaddress $WIRE 0.000$n > /dev/null + mine_btc +done +next_btc +sleep 1 +check_balance "*" 0.00011000 +echo " OK" + +echo -n "Perform fork and check btc-wire still up:" +up +btc2_fork +check_balance "*" 0.00000000 +up +echo " OK" + +echo -n "Recover orphaned transactions:" +next_btc +check_balance "*" 0.00011000 +echo " OK" + +echo "All tests passed" +\ No newline at end of file diff --git a/script/test_btc_stress.sh b/script/test_btc_stress.sh @@ -14,8 +14,6 @@ echo "Start database" setup_db echo "Start bitcoin node" init_btc -echo "Setup bitcoin" -setup_btc echo "Start btc-wire stressed" stressed_btc_wire echo "Start gateway" @@ -28,7 +26,7 @@ echo "----- Handle incoming -----" echo -n "Making wire transfer to exchange:" for n in `$SEQ`; do - btc-wire-cli -d $BTC_DIR transfer 0.000$n + btc-wire-cli -d $BTC_DIR transfer 0.000$n > /dev/null mine_btc # Mine transactions done sleep 3 # Give time for btc_wire worker to process @@ -113,7 +111,7 @@ echo "----- Recover DB -----" echo "Reset database" reset_db # Clear database tables mine_btc # Trigger worker -sleep 10 +sleep 20 echo -n "Requesting exchange incoming transaction list:" check_delta "incoming?delta=-100" "$SEQ" "0.000" diff --git a/script/test_btc_wire.sh b/script/test_btc_wire.sh @@ -14,8 +14,6 @@ echo "Start database" setup_db echo "Start bitcoin node" init_btc -echo "Setup bitcoin" -setup_btc echo "Start btc-wire" btc_wire echo "Start gateway" @@ -28,7 +26,7 @@ echo "----- Receive -----" echo -n "Making wire transfer to exchange:" for n in `$SEQ`; do - btc-wire-cli -d $BTC_DIR transfer 0.000$n + btc-wire-cli -d $BTC_DIR transfer 0.000$n > /dev/null mine_btc # Mine transactions done next_btc # Trigger btc_wire @@ -59,10 +57,7 @@ echo " OK" echo "----- Bounce -----" -echo -n "Clear wire wallet:" -$BTC_CLI -rpcwallet=wire sendtoaddress $CLIENT `$BTC_CLI -rpcwallet=wire getbalance` "" "" true > /dev/null -mine_btc -echo " OK" +clear_wallet echo -n "Bounce:" for n in `$SEQ`; do