commit 328d7758e31a025301be956310cbb47a3ed4b29a
parent 9eabce59a590696c5ca5c2a787c3867256348c4f
Author: Antoine A <>
Date: Fri, 4 Feb 2022 15:54:52 +0100
eth-wire: support lifetime and bounce
Diffstat:
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(¤t.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(), ð_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