depolymerization

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

commit 328d7758e31a025301be956310cbb47a3ed4b29a
parent 9eabce59a590696c5ca5c2a787c3867256348c4f
Author: Antoine A <>
Date:   Fri,  4 Feb 2022 15:54:52 +0100

eth-wire: support lifetime and bounce

Diffstat:
Mbtc-wire/src/loops/analysis.rs | 12+++---------
Mbtc-wire/src/loops/worker.rs | 37+++++++++++++++----------------------
Mbtc-wire/src/main.rs | 13+++++++------
Meth-wire/src/bin/eth-wire-cli.rs | 2+-
Meth-wire/src/bin/eth-wire-utils.rs | 79++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Meth-wire/src/loops/worker.rs | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Meth-wire/src/rpc.rs | 11+++++++----
Meth-wire/src/sql.rs | 11++++++++---
Mmakefile | 7++++---
Atest/conf/taler_eth_lifetime.conf | 13+++++++++++++
Atest/eth/lifetime.sh | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/eth/wire.sh | 29+++++++++++------------------
12 files changed, 307 insertions(+), 130 deletions(-)

diff --git a/btc-wire/src/loops/analysis.rs b/btc-wire/src/loops/analysis.rs @@ -17,7 +17,6 @@ use std::sync::atomic::Ordering; use btc_wire::rpc::ChainTipsStatus; use common::{ - config::BtcConfig, log::log::{error, warn}, postgres::fallible_iterator::FallibleIterator, }; @@ -30,12 +29,7 @@ use crate::{ use super::LoopResult; /// Analyse blockchain behavior and adapt confirmations in real time -pub fn analysis( - mut rpc: AutoReconnectRPC, - mut db: AutoReconnectSql, - config: &BtcConfig, - state: &WireState, -) { +pub fn analysis(mut rpc: AutoReconnectRPC, mut db: AutoReconnectSql, state: &WireState) { // The biggest fork ever seen let mut max_seen = 0; loop { @@ -61,11 +55,11 @@ pub fn analysis( // If new fork is bigger than the current confirmation if max_fork > current_conf { // Max two time the configuration - let new_conf = max_fork.min(config.confirmation * 2); + let new_conf = max_fork.min(state.config.confirmation * 2); state.confirmation.store(new_conf, Ordering::SeqCst); warn!( "analysis: found dangerous fork of {} blocks, adapt confirmation to {} blocks capped at {}, you should update taler.conf", - max_fork, new_conf, config.confirmation * 2 + max_fork, new_conf, state.config.confirmation * 2 ); } } diff --git a/btc-wire/src/loops/worker.rs b/btc-wire/src/loops/worker.rs @@ -28,7 +28,6 @@ use btc_wire::{ }; use common::{ api_common::base32, - config::BtcConfig, log::log::{error, info, warn}, postgres, sql::{sql_array, sql_url}, @@ -48,13 +47,8 @@ use crate::{ use super::{LoopError, LoopResult}; /// Listen for new proposed transactions and announce them on the bitcoin network -pub fn worker( - mut rpc: AutoReconnectRPC, - mut db: AutoReconnectSql, - config: &BtcConfig, - state: &WireState, -) { - let mut lifetime = config.wire_lifetime; +pub fn worker(mut rpc: AutoReconnectRPC, mut db: AutoReconnectSql, state: &WireState) { + let mut lifetime = state.config.wire_lifetime; let mut status = true; let mut skip_notification = false; @@ -104,14 +98,14 @@ pub fn worker( } // Sync chain - sync_chain(rpc, db, config, state, &mut status)?; + sync_chain(rpc, db, state, &mut status)?; // As we are now in sync with the blockchain if a transaction is in requested or delayed state it have not been sent // Send requested withdraws while withdraw(db, rpc)? {} - let bounce_fee = BtcAmount::from_sat(config.bounce_fee); + let bounce_fee = BtcAmount::from_sat(state.config.bounce_fee); // Send requested bounce while bounce(db, rpc, &bounce_fee)? {} @@ -211,7 +205,6 @@ fn last_hash(db: &mut Client) -> Result<Option<BlockHash>, postgres::Error> { fn sync_chain( rpc: &mut Rpc, db: &mut Client, - config: &BtcConfig, state: &WireState, status: &mut bool, ) -> LoopResult<bool> { @@ -257,7 +250,7 @@ fn sync_chain( } for (id, (category, confirmations)) in txs { match category { - Category::Send => sync_chain_outgoing(&id, confirmations, rpc, db, config)?, + Category::Send => sync_chain_outgoing(&id, confirmations, rpc, db, state)?, Category::Receive if confirmations >= min_confirmations as i32 => { sync_chain_incoming_confirmed(&id, rpc, db)? } @@ -369,7 +362,7 @@ fn sync_chain_outgoing( confirmations: i32, rpc: &mut Rpc, db: &mut Client, - config: &BtcConfig, + state: &WireState, ) -> LoopResult<()> { match rpc .get_tx_op_return(id) @@ -377,10 +370,10 @@ fn sync_chain_outgoing( { Ok((full, Ok(info))) => match info { OutMetadata::Withdraw { wtid, .. } => { - sync_chain_outgoing_send(id, &full, &wtid, rpc, db, confirmations, config)? + sync_chain_withdraw(id, &full, &wtid, rpc, db, confirmations, state)? } OutMetadata::Bounce { bounced } => { - sync_chain_outgoing_bounce(id, &bounced, db, confirmations)? + sync_chain_bounce(id, &bounced, db, confirmations)? } }, Ok((_, Err(e))) => warn!("send: decode-info {} - {}", id, e), @@ -392,15 +385,15 @@ fn sync_chain_outgoing( Ok(()) } -/// Sync database with an outgoing send transaction -fn sync_chain_outgoing_send( +/// Sync database with an outgoing withdraw transaction +fn sync_chain_withdraw( id: &Txid, full: &TransactionFull, wtid: &[u8; 32], rpc: &mut Rpc, db: &mut Client, confirmations: i32, - config: &BtcConfig, + state: &WireState, ) -> LoopResult<()> { let credit_addr = full.details[0].address.as_ref().unwrap(); let amount = btc_to_taler(&full.amount); @@ -478,7 +471,7 @@ fn sync_chain_outgoing_send( let date = SystemTime::UNIX_EPOCH + Duration::from_secs(full.time); let nb = db.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(credit_addr).as_ref(), &config.base_url.as_ref(), &(WithdrawStatus::Sent as i16), &id.as_ref(), &None::<&[u8]>], + &[&date, &amount.to_string(), &wtid.as_ref(), &btc_payto_url(&debit_addr).as_ref(), &btc_payto_url(credit_addr).as_ref(), &state.config.base_url.as_ref(), &(WithdrawStatus::Sent as i16), &id.as_ref(), &None::<&[u8]>], )?; if nb > 0 { warn!( @@ -491,7 +484,7 @@ fn sync_chain_outgoing_send( } } - if let Some(delay) = config.bump_delay { + if let Some(delay) = state.config.bump_delay { if confirmations == 0 && full.replaced_by_txid.is_none() { let now = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) @@ -518,7 +511,7 @@ fn sync_chain_outgoing_send( } /// Sync database with an outgoing bounce transaction -fn sync_chain_outgoing_bounce( +fn sync_chain_bounce( id: &Txid, bounced: &Txid, db: &mut Client, @@ -573,7 +566,7 @@ fn sync_chain_outgoing_bounce( Ok(()) } -/// Sync database with na incoming confirmed transaction +/// Sync database with an incoming confirmed transaction fn sync_chain_incoming_confirmed( id: &Txid, rpc: &mut Rpc, diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs @@ -37,6 +37,7 @@ mod taler_util; pub struct WireState { confirmation: AtomicU16, + config: BtcConfig, } fn main() { @@ -49,7 +50,6 @@ fn main() { .as_ref() .cloned() .unwrap_or_else(default_data_dir); - let config: &'static BtcConfig = Box::leak(Box::new(config)); let btc_config = BitcoinConfig::load(&data_dir).unwrap(); #[cfg(feature = "fail")] @@ -69,6 +69,7 @@ fn main() { let state: &'static WireState = Box::leak(Box::new(WireState { confirmation: AtomicU16::new(config.confirmation), + config, })); let mut rpc = Rpc::common(&btc_config).unwrap(); @@ -77,14 +78,14 @@ fn main() { let rpc_analysis = AutoReconnectRPC::new(btc_config.clone(), WIRE_WALLET_NAME); let rpc_worker = AutoReconnectRPC::new(btc_config, WIRE_WALLET_NAME); - let db_watcher = AutoReconnectSql::new(&config.core.db_url); - let db_analysis = AutoReconnectSql::new(&config.core.db_url); - let db_worker = AutoReconnectSql::new(&config.core.db_url); + let db_watcher = AutoReconnectSql::new(&state.config.core.db_url); + let db_analysis = AutoReconnectSql::new(&state.config.core.db_url); + let db_worker = AutoReconnectSql::new(&state.config.core.db_url); named_spawn("watcher", move || watcher(rpc_watcher, db_watcher)); named_spawn("analysis", move || { - analysis(rpc_analysis, db_analysis, config, state) + analysis(rpc_analysis, db_analysis, state) }); - worker(rpc_worker, db_worker, config, state); + worker(rpc_worker, db_worker, state); } pub fn named_spawn<F, T>(name: impl Into<String>, f: F) -> JoinHandle<T> diff --git a/eth-wire/src/bin/eth-wire-cli.rs b/eth-wire/src/bin/eth-wire-cli.rs @@ -49,7 +49,7 @@ fn main() { .expect("Failed to connect to ethereum RPC server"); // Skip previous blocks - let block = rpc.current_block().expect("Failed to get current block"); + let block = rpc.latest_block().expect("Failed to get current block"); let state = BlockState { hash: block.hash.unwrap(), number: block.number.unwrap(), diff --git a/eth-wire/src/bin/eth-wire-utils.rs b/eth-wire/src/bin/eth-wire-utils.rs @@ -1,12 +1,3 @@ -use std::{path::PathBuf, str::FromStr}; - -use eth_wire::{ - rpc::{hex::Hex, Rpc, TransactionRequest}, - taler_util::taler_to_eth, -}; -use ethereum_types::H160; -use common::{api_common::Amount, rand_slice}; - /* This file is part of TALER Copyright (C) 2022 Taler Systems SA @@ -22,6 +13,14 @@ use common::{api_common::Amount, rand_slice}; You should have received a copy of the GNU Affero General Public License along with TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ +use std::{path::PathBuf, str::FromStr}; + +use common::{api_common::Amount, rand_slice}; +use eth_wire::{ + rpc::{hex::Hex, Rpc, TransactionRequest}, + taler_util::taler_to_eth, +}; +use ethereum_types::H160; #[derive(argh::FromArgs)] /// Euthereum wire test client @@ -56,8 +55,12 @@ struct SendCmd { to: String, #[argh(positional)] - /// amount to send in eth - amount: f64, + /// sender wallet + fmt: String, + + #[argh(positional)] + /// amounts to send in eth + amounts: Vec<u32>, } #[derive(argh::FromArgs)] @@ -73,8 +76,12 @@ struct DepositCmd { to: String, #[argh(positional)] - /// amount to send in eth - amount: f64, + /// sender wallet + fmt: String, + + #[argh(positional)] + /// amounts to send in eth + amounts: Vec<u32>, } #[derive(argh::FromArgs)] @@ -110,31 +117,45 @@ struct BalanceCmd { fn main() { let args: Args = argh::from_env(); match args.cmd { - Cmd::Deposit(DepositCmd { from, to, amount }) => { + Cmd::Deposit(DepositCmd { + from, + to, + amounts, + fmt, + }) => { let mut rpc = Rpc::new(args.datadir.unwrap().join("geth.ipc")).unwrap(); let from = H160::from_str(&from).unwrap(); let to = H160::from_str(&to).unwrap(); - let amount = Amount::from_str(&format!("ETH:{}", amount)).unwrap(); - let value = taler_to_eth(&amount).unwrap(); rpc.unlock_account(&from, "password").ok(); - rpc.deposit(from, to, value, rand_slice()).unwrap(); + for amount in amounts { + let amount = Amount::from_str(&format!("ETH:{}{}", fmt, amount)).unwrap(); + let value = taler_to_eth(&amount).unwrap(); + rpc.deposit(from, to, value, rand_slice()).unwrap(); + } } - Cmd::Send(SendCmd { from, to, amount }) => { + Cmd::Send(SendCmd { + from, + to, + fmt, + amounts, + }) => { let mut rpc = Rpc::new(args.datadir.unwrap().join("geth.ipc")).unwrap(); let from = H160::from_str(&from).unwrap(); let to = H160::from_str(&to).unwrap(); - let amount = Amount::from_str(&format!("ETH:{}", amount)).unwrap(); - let value = taler_to_eth(&amount).unwrap(); rpc.unlock_account(&from, "password").ok(); - rpc.send_transaction(&TransactionRequest { - from, - to, - value, - gas: None, - gas_price: None, - data: Hex(vec![]), - }) - .unwrap(); + for amount in amounts { + let amount = Amount::from_str(&format!("ETH:{}{}", fmt, amount)).unwrap(); + let value = taler_to_eth(&amount).unwrap(); + rpc.send_transaction(&TransactionRequest { + from, + to, + value, + gas: None, + gas_price: None, + data: Hex(vec![]), + }) + .unwrap(); + } } Cmd::Mine(MineCmd { to, mut amount }) => { let mut rpc = Rpc::new(args.datadir.unwrap().join("geth.ipc")).unwrap(); diff --git a/eth-wire/src/loops/worker.rs b/eth-wire/src/loops/worker.rs @@ -13,30 +13,42 @@ You should have received a copy of the GNU Affero General Public License along with TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -use std::time::SystemTime; +use std::{sync::atomic::Ordering, time::SystemTime}; use common::{ api_common::base32, log::log::{error, info}, postgres::{fallible_iterator::FallibleIterator, Client}, sql::{sql_array, sql_url}, - status::WithdrawStatus, + status::{BounceStatus, WithdrawStatus}, }; use eth_wire::{ metadata::InMetadata, - rpc::Rpc, + rpc::{self, Rpc, Transaction}, taler_util::{eth_payto_url, eth_to_taler}, BlockState, }; +use ethereum_types::{Address, H256, U256}; use crate::{ - sql::{sql_addr, sql_eth_amount}, + sql::{sql_addr, sql_eth_amount, sql_hash}, LoopResult, WireState, }; pub fn worker(mut rpc: Rpc, mut db: Client, state: &WireState) { + let mut lifetime = state.config.wire_lifetime; let mut skip_notification = false; loop { + // Check lifetime + if let Some(nb) = lifetime.as_mut() { + if *nb == 0 { + info!("Reach end of lifetime"); + return; + } else { + *nb -= 1; + } + } + let result: LoopResult<()> = (|| { // Listen to all channels db.batch_execute("LISTEN new_block; LISTEN new_tx")?; @@ -55,6 +67,9 @@ pub fn worker(mut rpc: Rpc, mut db: Client, state: &WireState) { sync_chain(&mut rpc, &mut db, state)?; while withdraw(&mut db, &mut rpc, state)? {} + + while bounce(&mut db, &mut rpc, U256::from(state.config.bounce_fee))? {} + Ok(()) })(); @@ -67,51 +82,98 @@ pub fn worker(mut rpc: Rpc, mut db: Client, state: &WireState) { } } -fn sync_chain(rpc: &mut Rpc, db: &mut Client, state: &WireState) -> LoopResult<bool> { - let row = db.query_one("SELECT value FROM state WHERE name='last_block'", &[])?; - let slice: &[u8] = row.get(0); - let block = BlockState::from_bytes(slice.try_into().unwrap()); +fn list_since_block_state( + rpc: &mut Rpc, + address: &Address, + state: BlockState, + min_confirmation: u16, +) -> LoopResult<Option<(Vec<(Transaction, u16)>, BlockState)>> { + let match_tx = |txs: Vec<Transaction>, conf: u16| -> Vec<(Transaction, u16)> { + txs.into_iter() + .filter_map(|tx| { + (tx.from == Some(*address) || tx.to == Some(*address)).then(|| (tx, conf)) + }) + .collect() + }; let mut txs = Vec::new(); + // Add pending transaction + txs.extend(match_tx(rpc.pending_transactions()?, 0)); - txs.extend(rpc.pending_transactions()?.into_iter().map(|t| (t, 0))); - - let mut cursor = rpc.current_block()?; + let mut next_state = state; let mut confirmation = 1; - - // TODO check hash to detect reorg - - while cursor.number.expect("Mined block") != block.number { - txs.extend(cursor.transactions.drain(..).map(|t| (t, confirmation))); - cursor = rpc.block(cursor.number.unwrap() - 1u64)?.unwrap(); + let mut current = rpc.latest_block()?; + + // Move backward until we reach the starting block + while current.number.expect("Mined block") != state.number { + if confirmation == min_confirmation { + next_state = BlockState { + hash: current.hash.unwrap(), + number: current.number.unwrap(), + }; + } + txs.extend(match_tx(current.transactions, confirmation)); + if let Some(block) = rpc.block(&current.parent_hash)? { + current = block; + } else { + return Ok(None); + } confirmation += 1; } - for (tx, _confirmation) in txs { - if tx.to == Some(state.address) { - let metadata = InMetadata::decode(&tx.input).unwrap(); - match metadata { - InMetadata::Deposit { reserve_pub } => { - let date = SystemTime::now(); - let amount = eth_to_taler(&tx.value); - let credit_addr = tx.from.expect("Not coinbase"); - let nb = db.execute("INSERT INTO tx_in (_date, amount, reserve_pub, debit_acc, credit_acc) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (reserve_pub) DO NOTHING ", &[ + if current.hash.unwrap() != state.hash { + return Ok(None); + } + + Ok(Some((txs, next_state))) +} + +fn sync_chain(rpc: &mut Rpc, db: &mut Client, state: &WireState) -> LoopResult<bool> { + let row = db.query_one("SELECT value FROM state WHERE name='last_block'", &[])?; + let slice: &[u8] = row.get(0); + let block = BlockState::from_bytes(slice.try_into().unwrap()); + let min_confirmations = state.confirmation.load(Ordering::SeqCst); + + let (txs, next_state) = + list_since_block_state(rpc, &state.address, block, min_confirmations)?.unwrap(); + + for (tx, confirmation) in txs { + if tx.to == Some(state.address) && confirmation >= min_confirmations { + match InMetadata::decode(&tx.input) { + Ok(metadata) => match metadata { + InMetadata::Deposit { reserve_pub } => { + let date = SystemTime::now(); + let amount = eth_to_taler(&tx.value); + let credit_addr = tx.from.expect("Not coinbase"); + let nb = db.execute("INSERT INTO tx_in (_date, amount, reserve_pub, debit_acc, credit_acc) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (reserve_pub) DO NOTHING ", &[ &date, &amount.to_string(), &reserve_pub.as_ref(), &eth_payto_url(&credit_addr).as_ref(), &state.config.payto.as_ref() ])?; - if nb > 0 { - info!( - "<< {} {} in {} from {}", - amount, - base32(&reserve_pub), - hex::encode(tx.hash), - hex::encode(credit_addr), - ); + if nb > 0 { + info!( + "<< {} {} in {} from {}", + amount, + base32(&reserve_pub), + hex::encode(tx.hash), + hex::encode(credit_addr), + ); + } } + }, + Err(_) => { + // If encoding is wrong request a bounce + db.execute( + "INSERT INTO bounce (bounced) VALUES ($1) ON CONFLICT (bounced) DO NOTHING", + &[&tx.hash.as_ref()], + )?; } } } } + db.execute( + "UPDATE state SET value=$1 WHERE name='last_block'", + &[&next_state.to_bytes().as_ref()], + )?; Ok(true) } @@ -134,7 +196,47 @@ fn withdraw(db: &mut Client, rpc: &mut Rpc, state: &WireState) -> LoopResult<boo &[&(WithdrawStatus::Sent as i16), &tx_id.as_ref(), &id], )?; let amount = eth_to_taler(&amount); - info!(">> {} {} in {} to {}", amount, base32(&wtid), tx_id, addr); + info!( + ">> {} {} in {} to {}", + amount, + base32(&wtid), + hex::encode(tx_id), + hex::encode(addr) + ); + } + Ok(row.is_some()) +} + +/// Bounce a transaction on the blockchain, return false if nor more requested transaction are found +fn bounce(db: &mut Client, rpc: &mut Rpc, fee: U256) -> LoopResult<bool> { + // We rely on the advisory lock to ensure we are the only one sending transactions + let row = db.query_opt( + "SELECT id, bounced FROM bounce WHERE status=$1 ORDER BY _date LIMIT 1", + &[&(BounceStatus::Requested as i16)], + )?; + if let Some(row) = &row { + let id: i32 = row.get(0); + let bounced: H256 = sql_hash(row, 1); + + match rpc.bounce(bounced, fee) { + Ok(it) => { + db.execute( + "UPDATE bounce SET txid=$1, status=$2 WHERE id=$3", + &[&it.as_ref(), &(BounceStatus::Sent as i16), &id], + )?; + info!("|| {} in {}", hex::encode(&bounced), hex::encode(&it)); + } + Err(err) => match err { + rpc::Error::RPC { code, msg } => { + db.execute( + "UPDATE bounce SET status=$1 WHERE id=$2", + &[&(BounceStatus::Ignored as i16), &id], + )?; + info!("|| (ignore) {} because {} {}", &bounced, code, msg); + } + e => Err(e)?, + }, + } } Ok(row.is_some()) } diff --git a/eth-wire/src/rpc.rs b/eth-wire/src/rpc.rs @@ -193,10 +193,14 @@ impl Rpc { self.call("eth_sendTransaction", &[params]) } - pub fn block(&mut self, nb: U64) -> Result<Option<Block>> { + pub fn block_at(&mut self, nb: &U64) -> Result<Option<Block>> { self.call("eth_getBlockByNumber", &(nb, &true)) } + pub fn block(&mut self, hash: &H256) -> Result<Option<Block>> { + self.call("eth_getBlockByHash", &(hash, &true)) + } + pub fn pending_transactions(&mut self) -> Result<Vec<Transaction>> { self.call("eth_pendingTransactions", &EMPTY) } @@ -221,9 +225,8 @@ impl Rpc { Ok(RpcStream::new(rpc, id)) } - pub fn current_block(&mut self) -> Result<Block> { - let number: U64 = self.call("eth_blockNumber", &EMPTY)?; - Ok(self.block(number)?.expect("Current block must exist")) + pub fn latest_block(&mut self) -> Result<Block> { + self.call("eth_getBlockByNumber", &("latest", &true)) } pub fn balance(&mut self, addr: &Address) -> Result<U256> { diff --git a/eth-wire/src/sql.rs b/eth-wire/src/sql.rs @@ -13,12 +13,12 @@ You should have received a copy of the GNU Affero General Public License along with TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -use eth_wire::taler_util::{eth_payto_addr, taler_to_eth}; -use ethereum_types::{H160, U256}; use common::{ postgres::Row, - sql::{sql_amount, sql_url}, + sql::{sql_amount, sql_array, sql_url}, }; +use eth_wire::taler_util::{eth_payto_addr, taler_to_eth}; +use ethereum_types::{H160, H256, U256}; pub fn sql_eth_amount(row: &Row, idx: usize) -> U256 { let amount = sql_amount(row, idx); @@ -39,3 +39,8 @@ pub fn sql_addr(row: &Row, idx: usize) -> H160 { ) }) } + +pub fn sql_hash(row: &Row, idx: usize) -> H256 { + let array: [u8; 32] = sql_array(row, idx); + H256::from_slice(&array) +} diff --git a/makefile b/makefile @@ -3,10 +3,10 @@ install: cargo install --path eth-wire --bin eth-wire-cli --bin eth-wire-utils --bin eth-wire cargo install --path wire-gateway -test_gateway: +test_gateway: install test/gateway/api.sh -test_btc: +test_btc: install test/btc/wire.sh test/btc/lifetime.sh test/btc/reconnect.sh @@ -19,7 +19,8 @@ test_btc: test/btc/maxfee.sh test/btc/config.sh -test_eth: +test_eth: install test/eth/wire.sh + test/eth/lifetime.sh test: install test_gateway test_eth test_btc \ No newline at end of file diff --git a/test/conf/taler_eth_lifetime.conf b/test/conf/taler_eth_lifetime.conf @@ -0,0 +1,12 @@ +[taler] +CURRENCY = ETH + +[exchange] +BASE_URL = http://test.com + +[depolymerizer-ethereum] +DB_URL = postgres://localhost:5454/postgres?user=postgres&password=password +PORT = 8060 +CONFIRMATION = 3 +HTTP_LIFETIME = 10 +WIRE_LIFETIME = 10 +\ No newline at end of file diff --git a/test/eth/lifetime.sh b/test/eth/lifetime.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +## Check eth-wire and wire-gateway correctly stop when a lifetime limit is configured + +set -eu + +source "${BASH_SOURCE%/*}/../common.sh" +SCHEMA=eth.sql +CONFIG=taler_eth_lifetime.conf + + +echo "----- Setup -----" +echo "Load config file" +load_config +echo "Start database" +setup_db +echo "Start ethereum node" +init_eth +echo "Start eth-wire" +eth_wire +echo "Start gateway" +gateway +echo "" + +SEQ="seq 10 20" + +echo "---- Check lifetime -----" + +echo -n "Check up:" +check_up $WIRE_PID eth-wire +check_up $GATEWAY_PID wire-gateway +echo " OK" + +echo -n "Do some work:" +eth-wire-utils -d $WIRE_DIR deposit $CLIENT $WIRE 0.000 `$SEQ` > /dev/null +next_eth # Trigger eth-wire +check_balance_eth 999835000 165000 +for n in `$SEQ`; do + taler-exchange-wire-gateway-client \ + -b $BANK_ENDPOINT \ + -C payto://ethereum/$CLIENT \ + -a ETH:0.0000$n &> /dev/null || break; +done +echo " OK" + +echo -n "Check down:" +check_down $WIRE_PID eth-wire +check_down $GATEWAY_PID wire-gateway +echo " OK" + +echo "All tests passed!" diff --git a/test/eth/wire.sh b/test/eth/wire.sh @@ -21,17 +21,15 @@ echo "Start gateway" gateway echo "" -SEQ="seq 10 29" +SEQ="seq 10 99" RUST_BACKTRACE=1 echo "----- Receive -----" echo -n "Making wire transfer to exchange:" -for n in `$SEQ`; do - eth-wire-utils -d $WIRE_DIR deposit $CLIENT $WIRE 0.000$n -done +eth-wire-utils -d $WIRE_DIR deposit $CLIENT $WIRE 0.000 `$SEQ` next_eth # Trigger eth-wire -check_balance_eth 999610000 390000 +check_balance_eth 995095000 4905000 echo " OK" echo -n "Requesting exchange incoming transaction list:" @@ -49,25 +47,20 @@ for n in `$SEQ`; do done sleep 1 mine_eth # Mine transactions -check_balance_eth 999649000 351000 +check_balance_eth 995585500 4414500 echo " OK" echo -n "Requesting exchange's outgoing transaction list:" check_delta "outgoing?delta=-100" "$SEQ" echo " OK" -#echo "----- Bounce -----" - -#clear_wallet +echo "----- Bounce -----" -#echo -n "Bounce:" -#for n in `seq 10 40`; do -# eth-wire-utils -d $WIRE_DIR send $CLIENT $WIRE 0.000$n -# mine_btc -#done -#next_btc -#sleep 3 -#check_balance_eth "*" 0.00031000 -#echo " OK" +echo -n "Bounce:" +eth-wire-utils -d $WIRE_DIR send $CLIENT $WIRE 0.000 `seq 10 40` +sleep 1 +next_eth +check_balance_eth 995585499 4414500 +echo " OK" echo "All tests passed!" \ No newline at end of file