gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-depolymerization] branch master updated (f1aaee3 -> cc676a9)


From: gnunet
Subject: [taler-depolymerization] branch master updated (f1aaee3 -> cc676a9)
Date: Wed, 09 Feb 2022 14:30:32 +0100

This is an automated email from the git hooks/post-receive script.

antoine pushed a change to branch master
in repository depolymerization.

    from f1aaee3  Add eth-wire reorg handling and fix btc-wire bounce reorg 
handling
     new 0a63ad8  eth-wire: handle RPC and database reconnection
     new d38811c  eth-wire: handle concurrency and injected failures
     new cc676a9  eth-wire: zero-cost bounce and handle attacks

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Cargo.lock                               |  24 +--
 btc-wire/README.md                       |   2 +-
 btc-wire/src/loops.rs                    |   2 +-
 btc-wire/src/loops/analysis.rs           |  10 +-
 btc-wire/src/loops/watcher.rs            |   7 +-
 btc-wire/src/loops/worker.rs             | 241 +++++++++++++++--------------
 btc-wire/src/main.rs                     |  21 +--
 btc-wire/src/reconnect.rs                | 106 -------------
 btc-wire/src/rpc.rs                      |  15 ++
 common/src/lib.rs                        |   4 +-
 common/src/reconnect.rs                  |  71 +++++++++
 eth-wire/Cargo.toml                      |   4 +
 eth-wire/src/bin/eth-wire-utils.rs       |  71 ++++++++-
 {btc-wire => eth-wire}/src/fail_point.rs |   0
 eth-wire/src/lib.rs                      |  21 ++-
 eth-wire/src/loops/watcher.rs            |  25 ++-
 eth-wire/src/loops/worker.rs             | 253 ++++++++++++++++++++++++-------
 eth-wire/src/main.rs                     |  46 ++----
 eth-wire/src/rpc.rs                      |  49 +++++-
 makefile                                 |   2 +
 test/btc/analysis.sh                     |  16 +-
 test/btc/bumpfee.sh                      |  12 +-
 test/btc/config.sh                       |   2 +-
 test/btc/conflict.sh                     |   8 +-
 test/btc/hell.sh                         |  28 ++--
 test/btc/lifetime.sh                     |   4 +-
 test/btc/maxfee.sh                       |   6 +-
 test/btc/reconnect.sh                    |  38 ++---
 test/btc/reorg.sh                        |  18 +--
 test/btc/stress.sh                       |  15 +-
 test/btc/wire.sh                         |   6 +-
 test/common.sh                           |  90 +++++++----
 test/eth/hell.sh                         | 106 +++++++++++++
 test/eth/lifetime.sh                     |   2 +-
 test/eth/reconnect.sh                    |  93 ++++++++++++
 test/eth/reorg.sh                        |  16 +-
 test/eth/stress.sh                       | 112 ++++++++++++++
 test/eth/wire.sh                         |   4 +-
 test/gateway/api.sh                      |   2 +-
 wire-gateway/README.md                   |   2 +-
 40 files changed, 1067 insertions(+), 487 deletions(-)
 delete mode 100644 btc-wire/src/reconnect.rs
 create mode 100644 common/src/reconnect.rs
 copy {btc-wire => eth-wire}/src/fail_point.rs (100%)
 create mode 100644 test/eth/hell.sh
 create mode 100644 test/eth/reconnect.sh
 create mode 100644 test/eth/stress.sh

diff --git a/Cargo.lock b/Cargo.lock
index 20e1471..678145b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -76,9 +76,9 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "base32"
@@ -127,9 +127,9 @@ checksum = 
"bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "block-buffer"
-version = "0.10.1"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b"
+checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
 dependencies = [
  "generic-array",
 ]
@@ -492,9 +492,9 @@ dependencies = [
 
 [[package]]
 name = "ethbloom"
-version = "0.12.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c927489503a5d331fdb25a3ac8d80e433ee6aa96951c99346d66e7be2b0400a0"
+checksum = "11da94e443c60508eb62cf256243a64da87304c2802ac2528847f79d750007ef"
 dependencies = [
  "crunchy",
  "fixed-hash",
@@ -504,9 +504,9 @@ dependencies = [
 
 [[package]]
 name = "ethereum-types"
-version = "0.13.0"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6e6307a3fa100563ce323e6b654c5935c8d919aeee56b0ccef1c65c5ecd2ecf6"
+checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6"
 dependencies = [
  "ethbloom",
  "fixed-hash",
@@ -1196,9 +1196,9 @@ checksum = 
"eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
 
 [[package]]
 name = "primitive-types"
-version = "0.11.0"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "db5a3482110d085cee2a284278b42a4361bc611d008eddfbf04fd84735ae521f"
+checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a"
 dependencies = [
  "fixed-hash",
  "impl-serde",
@@ -1485,9 +1485,9 @@ dependencies = [
 
 [[package]]
 name = "serde_with"
-version = "1.11.0"
+version = "1.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3"
+checksum = "ec1e6ec4d8950e5b1e894eac0d360742f3b1407a6078a604a731c4b3f49cefbc"
 dependencies = [
  "rustversion",
  "serde",
diff --git a/btc-wire/README.md b/btc-wire/README.md
index fa567c0..74a2738 100644
--- a/btc-wire/README.md
+++ b/btc-wire/README.md
@@ -17,7 +17,7 @@ Bitcoind version >= 22.0 is required
 The configuration is based on 
[taler.conf](https://docs.taler.net/manpages/taler.conf.5.html)
 
 ``` ini
-# taler.conf - btc_wire config
+# taler.conf - btc-wire config
 [depolymerizer-bitcoin]
 DATA_DIR     =
 CONFIRMATION = 6
diff --git a/btc-wire/src/loops.rs b/btc-wire/src/loops.rs
index 468fb39..c9831ea 100644
--- a/btc-wire/src/loops.rs
+++ b/btc-wire/src/loops.rs
@@ -29,7 +29,7 @@ pub enum LoopError {
     Rpc(#[from] rpc::Error),
     #[error(transparent)]
     DB(#[from] postgres::Error),
-    #[error("Another btc_wire process is running concurrently")]
+    #[error("Another btc-wire process is running concurrently")]
     Concurrency,
     #[error(transparent)]
     Injected(#[from] Injected),
diff --git a/btc-wire/src/loops/analysis.rs b/btc-wire/src/loops/analysis.rs
index 2924ebc..e956c2c 100644
--- a/btc-wire/src/loops/analysis.rs
+++ b/btc-wire/src/loops/analysis.rs
@@ -15,21 +15,19 @@
 */
 use std::sync::atomic::Ordering;
 
-use btc_wire::rpc::ChainTipsStatus;
+use btc_wire::rpc::{AutoReconnectRPC, ChainTipsStatus};
 use common::{
     log::log::{error, warn},
     postgres::fallible_iterator::FallibleIterator,
+    reconnect::AutoReconnectDb,
 };
 
-use crate::{
-    reconnect::{AutoReconnectRPC, AutoReconnectSql},
-    WireState,
-};
+use crate::WireState;
 
 use super::LoopResult;
 
 /// Analyse blockchain behavior and adapt confirmations in real time
-pub fn analysis(mut rpc: AutoReconnectRPC, mut db: AutoReconnectSql, state: 
&WireState) {
+pub fn analysis(mut rpc: AutoReconnectRPC, mut db: AutoReconnectDb, state: 
&WireState) {
     // The biggest fork ever seen
     let mut max_seen = 0;
     loop {
diff --git a/btc-wire/src/loops/watcher.rs b/btc-wire/src/loops/watcher.rs
index dee688f..e3d7458 100644
--- a/btc-wire/src/loops/watcher.rs
+++ b/btc-wire/src/loops/watcher.rs
@@ -1,3 +1,4 @@
+use btc_wire::rpc::AutoReconnectRPC;
 /*
   This file is part of TALER
   Copyright (C) 2022 Taler Systems SA
@@ -13,14 +14,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 common::log::log::error;
-
-use crate::reconnect::{AutoReconnectRPC, AutoReconnectSql};
+use common::{log::log::error, reconnect::AutoReconnectDb};
 
 use super::LoopResult;
 
 /// Wait for new block and notify arrival with postgreSQL notifications
-pub fn watcher(mut rpc: AutoReconnectRPC, mut db: AutoReconnectSql) {
+pub fn watcher(mut rpc: AutoReconnectRPC, mut db: AutoReconnectDb) {
     loop {
         let rpc = rpc.client();
         let db = db.client();
diff --git a/btc-wire/src/loops/worker.rs b/btc-wire/src/loops/worker.rs
index e8a355c..85dd8ee 100644
--- a/btc-wire/src/loops/worker.rs
+++ b/btc-wire/src/loops/worker.rs
@@ -22,7 +22,7 @@ use std::{
 
 use bitcoin::{hashes::Hash, Amount as BtcAmount, BlockHash, Txid};
 use btc_wire::{
-    rpc::{self, Category, ErrorCode, Rpc, TransactionFull},
+    rpc::{self, AutoReconnectRPC, Category, ErrorCode, Rpc, TransactionFull},
     rpc_utils::sender_address,
     GetOpReturnErr, GetSegwitErr,
 };
@@ -30,6 +30,7 @@ use common::{
     api_common::base32,
     log::log::{error, info, warn},
     postgres,
+    reconnect::AutoReconnectDb,
     sql::{sql_array, sql_url},
     status::{BounceStatus, WithdrawStatus},
 };
@@ -38,7 +39,6 @@ use postgres::{fallible_iterator::FallibleIterator, Client};
 use crate::{
     fail_point::fail_point,
     metadata::OutMetadata,
-    reconnect::{AutoReconnectRPC, AutoReconnectSql},
     sql::{sql_addr, sql_btc_amount, sql_txid},
     taler_util::{btc_payto_url, btc_to_taler},
     WireState,
@@ -46,8 +46,7 @@ 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, state: 
&WireState) {
+pub fn worker(mut rpc: AutoReconnectRPC, mut db: AutoReconnectDb, state: 
&WireState) {
     let mut lifetime = state.config.wire_lifetime;
     let mut status = true;
     let mut skip_notification = false;
@@ -84,8 +83,8 @@ pub fn worker(mut rpc: AutoReconnectRPC, mut db: 
AutoReconnectSql, state: &WireS
 
             // It is not possible to atomically update the blockchain and the 
database.
             // When we failed to sync the database and the blockchain state we 
rely on
-            // sync_chain to recover the lost update.
-            // When this function is running in parallel it not possible to 
known another
+            // sync_chain to recover the lost updates.
+            // When this function is running concurrently, it not possible to 
known another
             // execution has failed, and this can lead to a transaction being 
sent multiple time.
             // To ensure only a single version of this function is running at 
a given time we rely
             // on postgres advisory lock
@@ -100,7 +99,7 @@ pub fn worker(mut rpc: AutoReconnectRPC, mut db: 
AutoReconnectSql, state: &WireS
             // Sync chain
             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
+            // As we are now in sync with the blockchain if a transaction has 
Requested status it have not been sent
 
             // Send requested withdraws
             while withdraw(db, rpc)? {}
@@ -128,72 +127,6 @@ pub fn worker(mut rpc: AutoReconnectRPC, mut db: 
AutoReconnectSql, state: &WireS
     }
 }
 
-/// Send a withdraw transaction on the blockchain, return false if no more 
requested transaction are found
-fn withdraw(db: &mut Client, rpc: &mut Rpc) -> LoopResult<bool> {
-    // We rely on the advisory lock to ensure we are the only one sending 
transactions
-    let row = db.query_opt(
-        "SELECT id, amount, wtid, credit_acc, exchange_url FROM tx_out WHERE 
status=$1 ORDER BY _date LIMIT 1",
-        &[&(WithdrawStatus::Requested as i16)],
-    )?;
-    if let Some(row) = &row {
-        let id: i32 = row.get(0);
-        let amount = sql_btc_amount(row, 1);
-        let wtid: [u8; 32] = sql_array(row, 2);
-        let addr = sql_addr(row, 3);
-        let url = sql_url(row, 4);
-        let metadata = OutMetadata::Withdraw { wtid, url };
-
-        let tx_id = rpc.send_op_return(&addr, &amount, &metadata.encode(), 
false, true)?;
-        fail_point("(injected) fail send", 0.3)?;
-        db.execute(
-            "UPDATE tx_out SET status=$1, txid=$2 WHERE id=$3",
-            &[&(WithdrawStatus::Sent as i16), &tx_id.as_ref(), &id],
-        )?;
-        let amount = btc_to_taler(&amount.to_signed().unwrap());
-        info!(">> {} {} in {} to {}", amount, base32(&wtid), tx_id, addr);
-    }
-    Ok(row.is_some())
-}
-
-/// Bounce a transaction on the blockchain, return false if no more requested 
transaction are found
-fn bounce(db: &mut Client, rpc: &mut Rpc, fee: &BtcAmount) -> 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: Txid = sql_txid(row, 1);
-        let metadata = OutMetadata::Bounce { bounced };
-
-        match rpc.bounce(&bounced, fee, &metadata.encode()) {
-            Ok(it) => {
-                fail_point("(injected) fail bounce", 0.3)?;
-                db.execute(
-                    "UPDATE bounce SET txid=$1, status=$2 WHERE id=$3",
-                    &[&it.as_ref(), &(BounceStatus::Sent as i16), &id],
-                )?;
-                info!("|| {} in {}", &bounced, &it);
-            }
-            Err(err) => match err {
-                rpc::Error::RPC {
-                    code: ErrorCode::RpcWalletInsufficientFunds | 
ErrorCode::RpcWalletError,
-                    msg,
-                } => {
-                    db.execute(
-                        "UPDATE bounce SET status=$1 WHERE id=$2",
-                        &[&(BounceStatus::Ignored as i16), &id],
-                    )?;
-                    info!("|| (ignore) {} because {}", &bounced, msg);
-                }
-                e => Err(e)?,
-            },
-        }
-    }
-    Ok(row.is_some())
-}
-
 /// Retrieve last stored hash
 fn last_hash(db: &mut Client) -> Result<BlockHash, postgres::Error> {
     let row = db.query_one("SELECT value FROM state WHERE name='last_hash'", 
&[])?;
@@ -352,28 +285,41 @@ fn sync_chain_removed(
     }
 }
 
-/// Sync database with an outgoing transaction
-fn sync_chain_outgoing(
+/// Sync database with an incoming confirmed transaction
+fn sync_chain_incoming_confirmed(
     id: &Txid,
-    confirmations: i32,
     rpc: &mut Rpc,
     db: &mut Client,
-    state: &WireState,
-) -> LoopResult<()> {
-    match rpc
-        .get_tx_op_return(id)
-        .map(|(full, bytes)| (full, OutMetadata::decode(&bytes)))
-    {
-        Ok((full, Ok(info))) => match info {
-            OutMetadata::Withdraw { wtid, .. } => {
-                sync_chain_withdraw(id, &full, &wtid, rpc, db, confirmations, 
state)?
+) -> Result<(), LoopError> {
+    match rpc.get_tx_segwit_key(id) {
+        Ok((full, reserve_pub)) => {
+            // Store transactions in database
+            let debit_addr = sender_address(rpc, &full)?;
+            let credit_addr = full.details[0].address.as_ref().unwrap();
+            let date = SystemTime::UNIX_EPOCH + Duration::from_secs(full.time);
+            let amount = btc_to_taler(&full.amount);
+            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(), 
&btc_payto_url(&debit_addr).as_ref(), &btc_payto_url(credit_addr).as_ref()
+            ])?;
+            if nb > 0 {
+                info!(
+                    "<< {} {} in {} from {}",
+                    amount,
+                    base32(&reserve_pub),
+                    id,
+                    debit_addr
+                );
             }
-            OutMetadata::Bounce { bounced } => sync_chain_bounce(id, &bounced, 
db, confirmations)?,
-        },
-        Ok((_, Err(e))) => warn!("send: decode-info {} - {}", id, e),
-        Err(e) => match e {
-            GetOpReturnErr::MissingOpReturn => { /* Ignore */ }
-            GetOpReturnErr::RPC(e) => return Err(e)?,
+        }
+        Err(err) => match err {
+            GetSegwitErr::Decode(_) => {
+                // If encoding is wrong request a bounce
+                db.execute(
+                    "INSERT INTO bounce (bounced) VALUES ($1) ON CONFLICT 
(bounced) DO NOTHING",
+                    &[&id.as_ref()],
+                )?;
+            }
+            GetSegwitErr::RPC(e) => return Err(e.into()),
         },
     }
     Ok(())
@@ -561,42 +507,95 @@ fn sync_chain_bounce(
     Ok(())
 }
 
-/// Sync database with an incoming confirmed transaction
-fn sync_chain_incoming_confirmed(
+/// Sync database with an outgoing transaction
+fn sync_chain_outgoing(
     id: &Txid,
+    confirmations: i32,
     rpc: &mut Rpc,
     db: &mut Client,
-) -> Result<(), LoopError> {
-    match rpc.get_tx_segwit_key(id) {
-        Ok((full, reserve_pub)) => {
-            // Store transactions in database
-            let debit_addr = sender_address(rpc, &full)?;
-            let credit_addr = full.details[0].address.as_ref().unwrap();
-            let date = SystemTime::UNIX_EPOCH + Duration::from_secs(full.time);
-            let amount = btc_to_taler(&full.amount);
-            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(), 
&btc_payto_url(&debit_addr).as_ref(), &btc_payto_url(credit_addr).as_ref()
-            ])?;
-            if nb > 0 {
-                info!(
-                    "<< {} {} in {} from {}",
-                    amount,
-                    base32(&reserve_pub),
-                    id,
-                    debit_addr
-                );
+    state: &WireState,
+) -> LoopResult<()> {
+    match rpc
+        .get_tx_op_return(id)
+        .map(|(full, bytes)| (full, OutMetadata::decode(&bytes)))
+    {
+        Ok((full, Ok(info))) => match info {
+            OutMetadata::Withdraw { wtid, .. } => {
+                sync_chain_withdraw(id, &full, &wtid, rpc, db, confirmations, 
state)?
             }
-        }
-        Err(err) => match err {
-            GetSegwitErr::Decode(_) => {
-                // If encoding is wrong request a bounce
+            OutMetadata::Bounce { bounced } => sync_chain_bounce(id, &bounced, 
db, confirmations)?,
+        },
+        Ok((_, Err(e))) => warn!("send: decode-info {} - {}", id, e),
+        Err(e) => match e {
+            GetOpReturnErr::MissingOpReturn => { /* Ignore */ }
+            GetOpReturnErr::RPC(e) => return Err(e)?,
+        },
+    }
+    Ok(())
+}
+
+/// Send a withdraw transaction on the blockchain, return false if no more 
requested transaction are found
+fn withdraw(db: &mut Client, rpc: &mut Rpc) -> LoopResult<bool> {
+    // We rely on the advisory lock to ensure we are the only one sending 
transactions
+    let row = db.query_opt(
+        "SELECT id, amount, wtid, credit_acc, exchange_url FROM tx_out WHERE 
status=$1 ORDER BY _date LIMIT 1",
+        &[&(WithdrawStatus::Requested as i16)],
+    )?;
+    if let Some(row) = &row {
+        let id: i32 = row.get(0);
+        let amount = sql_btc_amount(row, 1);
+        let wtid: [u8; 32] = sql_array(row, 2);
+        let addr = sql_addr(row, 3);
+        let url = sql_url(row, 4);
+        let metadata = OutMetadata::Withdraw { wtid, url };
+
+        let tx_id = rpc.send_op_return(&addr, &amount, &metadata.encode(), 
false, true)?;
+        fail_point("(injected) fail withdraw", 0.3)?;
+        db.execute(
+            "UPDATE tx_out SET status=$1, txid=$2 WHERE id=$3",
+            &[&(WithdrawStatus::Sent as i16), &tx_id.as_ref(), &id],
+        )?;
+        let amount = btc_to_taler(&amount.to_signed().unwrap());
+        info!(">> {} {} in {} to {}", amount, base32(&wtid), tx_id, addr);
+    }
+    Ok(row.is_some())
+}
+
+/// Bounce a transaction on the blockchain, return false if no more requested 
transaction are found
+fn bounce(db: &mut Client, rpc: &mut Rpc, fee: &BtcAmount) -> 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: Txid = sql_txid(row, 1);
+        let metadata = OutMetadata::Bounce { bounced };
+
+        match rpc.bounce(&bounced, fee, &metadata.encode()) {
+            Ok(it) => {
+                fail_point("(injected) fail bounce", 0.3)?;
                 db.execute(
-                    "INSERT INTO bounce (bounced) VALUES ($1) ON CONFLICT 
(bounced) DO NOTHING",
-                    &[&id.as_ref()],
+                    "UPDATE bounce SET txid=$1, status=$2 WHERE id=$3",
+                    &[&it.as_ref(), &(BounceStatus::Sent as i16), &id],
                 )?;
+                info!("|| {} in {}", &bounced, &it);
             }
-            GetSegwitErr::RPC(e) => return Err(e.into()),
-        },
+            Err(err) => match err {
+                rpc::Error::RPC {
+                    code: ErrorCode::RpcWalletInsufficientFunds | 
ErrorCode::RpcWalletError,
+                    msg,
+                } => {
+                    db.execute(
+                        "UPDATE bounce SET status=$1 WHERE id=$2",
+                        &[&(BounceStatus::Ignored as i16), &id],
+                    )?;
+                    info!("|| (ignore) {} because {}", &bounced, msg);
+                }
+                e => Err(e)?,
+            },
+        }
     }
-    Ok(())
+    Ok(row.is_some())
 }
diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs
index 7d22b0e..16fc0e2 100644
--- a/btc-wire/src/main.rs
+++ b/btc-wire/src/main.rs
@@ -16,14 +16,14 @@
 use bitcoin::Network;
 use btc_wire::{
     config::{BitcoinConfig, WIRE_WALLET_NAME},
-    rpc::Rpc,
+    rpc::{auto_reconnect_rpc, Rpc},
     rpc_utils::default_data_dir,
 };
 use common::{
     config::{load_btc_config, BtcConfig},
     log::log::info,
+    reconnect::auto_reconnect_db,
 };
-use reconnect::{AutoReconnectRPC, AutoReconnectSql};
 use std::{sync::atomic::AtomicU16, thread::JoinHandle};
 
 use crate::loops::{analysis::analysis, watcher::watcher, worker::worker};
@@ -31,7 +31,6 @@ use crate::loops::{analysis::analysis, watcher::watcher, 
worker::worker};
 mod fail_point;
 mod loops;
 mod metadata;
-mod reconnect;
 mod sql;
 mod taler_util;
 
@@ -43,7 +42,9 @@ pub struct WireState {
 fn main() {
     common::log::init();
 
-    let config = load_btc_config(Some(&std::env::args().nth(1).expect("Missing 
conf path arg")));
+    let config = load_btc_config(Some(
+        &std::env::args().nth(1).expect("Missing conf path arg"),
+    ));
     let data_dir = config
         .core
         .data_dir
@@ -74,13 +75,13 @@ fn main() {
 
     let mut rpc = Rpc::common(&btc_config).unwrap();
     rpc.load_wallet(WIRE_WALLET_NAME).ok();
-    let rpc_watcher = AutoReconnectRPC::new(btc_config.clone(), 
WIRE_WALLET_NAME);
-    let rpc_analysis = AutoReconnectRPC::new(btc_config.clone(), 
WIRE_WALLET_NAME);
-    let rpc_worker = AutoReconnectRPC::new(btc_config, WIRE_WALLET_NAME);
+    let rpc_watcher = auto_reconnect_rpc(btc_config.clone(), WIRE_WALLET_NAME);
+    let rpc_analysis = auto_reconnect_rpc(btc_config.clone(), 
WIRE_WALLET_NAME);
+    let rpc_worker = auto_reconnect_rpc(btc_config, WIRE_WALLET_NAME);
 
-    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);
+    let db_watcher = auto_reconnect_db(state.config.core.db_url.clone());
+    let db_analysis = auto_reconnect_db(state.config.core.db_url.clone());
+    let db_worker = auto_reconnect_db(state.config.core.db_url.clone());
     named_spawn("watcher", move || watcher(rpc_watcher, db_watcher));
     named_spawn("analysis", move || {
         analysis(rpc_analysis, db_analysis, state)
diff --git a/btc-wire/src/reconnect.rs b/btc-wire/src/reconnect.rs
deleted file mode 100644
index 1869f7e..0000000
--- a/btc-wire/src/reconnect.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2022 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Affero General Public License as published by the Free 
Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
-
-  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/>
-*/
-//! Wrapper for RPC or PostgreSQL connection which automatically reconnect on 
error
-
-use std::time::Duration;
-
-use btc_wire::{config::BitcoinConfig, rpc::Rpc};
-use common::log::log::error;
-use common::postgres::{Client, NoTls};
-
-const RECONNECT_DELAY: Duration = Duration::from_secs(5);
-
-/// auto-reconnect RPC
-pub struct AutoReconnectRPC {
-    config: BitcoinConfig,
-    wallet: String,
-    client: Rpc,
-}
-
-impl AutoReconnectRPC {
-    pub fn new(config: BitcoinConfig, wallet: impl Into<String>) -> Self {
-        let wallet: String = wallet.into();
-        Self {
-            client: Self::connect(&config, &wallet),
-            wallet,
-            config,
-        }
-    }
-
-    /// Connect a new client, loop on error
-    fn connect(config: &BitcoinConfig, wallet: &str) -> Rpc {
-        loop {
-            match Rpc::wallet(config, wallet) {
-                Ok(mut new) => match new.net_info() {
-                    Ok(_) => return new,
-                    Err(err) => {
-                        error!("connect RPC: {}", err);
-                        std::thread::sleep(RECONNECT_DELAY);
-                    }
-                },
-                Err(err) => {
-                    error!("connect RPC: {}", err);
-                    std::thread::sleep(RECONNECT_DELAY);
-                }
-            }
-        }
-    }
-
-    /// Get a mutable connection, block until a connection can be established
-    pub fn client(&mut self) -> &mut Rpc {
-        if self.client.net_info().is_err() {
-            self.client = Self::connect(&self.config, &self.wallet);
-        }
-        &mut self.client
-    }
-}
-
-/// auto-reconnect SQL
-pub struct AutoReconnectSql {
-    config: String,
-    client: Client,
-}
-
-impl AutoReconnectSql {
-    pub fn new(config: impl Into<String>) -> Self {
-        let config: String = config.into();
-        Self {
-            client: Self::connect(&config),
-            config,
-        }
-    }
-
-    /// Connect a new client, loop on error
-    fn connect(config: &str) -> Client {
-        loop {
-            match Client::connect(config, NoTls) {
-                Ok(new) => return new,
-                Err(err) => {
-                    error!("connect DB: {}", err);
-                    std::thread::sleep(RECONNECT_DELAY);
-                }
-            }
-        }
-    }
-
-    /// Get a mutable connection, block until a connection can be established
-    pub fn client(&mut self) -> &mut Client {
-        if self.client.is_valid(RECONNECT_DELAY).is_err() {
-            self.client = Self::connect(&self.config);
-        }
-        &mut self.client
-    }
-}
diff --git a/btc-wire/src/rpc.rs b/btc-wire/src/rpc.rs
index 8003304..ccd5ac3 100644
--- a/btc-wire/src/rpc.rs
+++ b/btc-wire/src/rpc.rs
@@ -26,6 +26,7 @@
 //! bitcoincore RPC documentation: <https://bitcoincore.org/en/doc/22.0.0/>
 
 use bitcoin::{hashes::hex::ToHex, Address, Amount, BlockHash, SignedAmount, 
Txid};
+use common::{log::log::error, reconnect::AutoReconnect};
 use serde_json::{json, Value};
 use std::{
     fmt::Debug,
@@ -36,6 +37,20 @@ use std::{
 
 use crate::config::{BitcoinConfig, BtcAuth};
 
+pub type AutoReconnectRPC = AutoReconnect<(BitcoinConfig, &'static str), Rpc>;
+
+pub fn auto_reconnect_rpc(config: BitcoinConfig, wallet: &'static str) -> 
AutoReconnectRPC {
+    AutoReconnect::new(
+        (config, wallet),
+        |(config, wallet)| {
+            Rpc::wallet(config, wallet)
+                .map_err(|err| error!("connect RPC: {}", err))
+                .ok()
+        },
+        |client| client.net_info().is_err(),
+    )
+}
+
 #[derive(Debug, serde::Serialize)]
 struct RpcRequest<'a, T: serde::Serialize> {
     method: &'a str,
diff --git a/common/src/lib.rs b/common/src/lib.rs
index 0ed82e1..ade1e60 100644
--- a/common/src/lib.rs
+++ b/common/src/lib.rs
@@ -1,4 +1,3 @@
-use rand::{rngs::OsRng, RngCore};
 /*
   This file is part of TALER
   Copyright (C) 2022 Taler Systems SA
@@ -14,6 +13,8 @@ use rand::{rngs::OsRng, RngCore};
   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 rand::{rngs::OsRng, RngCore};
+
 pub use postgres;
 pub use rand;
 pub use url;
@@ -23,6 +24,7 @@ pub mod api_wire;
 pub mod config;
 pub mod error_codes;
 pub mod log;
+pub mod reconnect;
 pub mod sql;
 pub mod status;
 
diff --git a/common/src/reconnect.rs b/common/src/reconnect.rs
new file mode 100644
index 0000000..bc702e1
--- /dev/null
+++ b/common/src/reconnect.rs
@@ -0,0 +1,71 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  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::Duration;
+
+use log::error;
+use postgres::{Client, NoTls};
+
+const RECONNECT_DELAY: Duration = Duration::from_secs(5);
+
+pub struct AutoReconnect<S, C> {
+    config: S,
+    client: C,
+    connect: fn(&S) -> Option<C>,
+    check: fn(&mut C) -> bool,
+}
+
+impl<S, C> AutoReconnect<S, C> {
+    pub fn new(config: S, connect: fn(&S) -> Option<C>, check: fn(&mut C) -> 
bool) -> Self {
+        Self {
+            client: Self::connect(&config, connect),
+            connect,
+            check,
+            config,
+        }
+    }
+
+    /// Create a new client, loop on error
+    fn connect(config: &S, connect: fn(&S) -> Option<C>) -> C {
+        loop {
+            match connect(config) {
+                Some(new) => return new,
+                None => std::thread::sleep(RECONNECT_DELAY),
+            }
+        }
+    }
+
+    /// Get a mutable connection, block until a connection can be established
+    pub fn client(&mut self) -> &mut C {
+        if (self.check)(&mut self.client) {
+            self.client = Self::connect(&self.config, self.connect);
+        }
+        &mut self.client
+    }
+}
+
+pub type AutoReconnectDb = AutoReconnect<String, Client>;
+
+pub fn auto_reconnect_db(config: String) -> AutoReconnectDb {
+    AutoReconnect::new(
+        config,
+        |config| {
+            Client::connect(config, NoTls)
+                .map_err(|err| error!("connect DB: {}", err))
+                .ok()
+        },
+        |client| client.is_valid(RECONNECT_DELAY).is_err(),
+    )
+}
diff --git a/eth-wire/Cargo.toml b/eth-wire/Cargo.toml
index 9b507f2..644c246 100644
--- a/eth-wire/Cargo.toml
+++ b/eth-wire/Cargo.toml
@@ -5,6 +5,10 @@ edition = "2021"
 license = "AGPL-3.0-or-later"
 rust-version = "1.56.1"
 
+[features]
+# Enable random failures
+fail = []
+
 [dependencies]
 # Cli args 
 argh = "0.1.7"
diff --git a/eth-wire/src/bin/eth-wire-utils.rs 
b/eth-wire/src/bin/eth-wire-utils.rs
index 26f5ddd..86d03f3 100644
--- a/eth-wire/src/bin/eth-wire-utils.rs
+++ b/eth-wire/src/bin/eth-wire-utils.rs
@@ -15,12 +15,19 @@
 */
 use std::{path::PathBuf, str::FromStr};
 
-use common::{api_common::Amount, log::init, rand_slice};
+use common::{
+    api_common::Amount,
+    config::{Config, CoreConfig},
+    log::init,
+    postgres::{Client, NoTls},
+    rand_slice,
+};
 use eth_wire::{
     rpc::{hex::Hex, Rpc, TransactionRequest},
     taler_util::taler_to_eth,
+    SyncState,
 };
-use ethereum_types::H160;
+use ethereum_types::{H160, U256};
 
 #[derive(argh::FromArgs)]
 /// Euthereum wire test client
@@ -38,10 +45,11 @@ enum Cmd {
     Send(SendCmd),
     Deposit(DepositCmd),
     Mine(MineCmd),
-    ClearDB(ClearCmd),
+    ResetDb(ResetCmd),
     Balance(BalanceCmd),
     Connect(ConnectCmd),
     Disconnect(DisconnectCmd),
+    Abandon(AbandonCmd),
 }
 
 #[derive(argh::FromArgs)]
@@ -99,12 +107,12 @@ struct MineCmd {
 }
 
 #[derive(argh::FromArgs)]
-#[argh(subcommand, name = "cleardb")]
+#[argh(subcommand, name = "resetdb")]
 /// Clear database
-struct ClearCmd {
+struct ResetCmd {
     #[argh(positional)]
     /// taler config
-    config: PathBuf,
+    config: String,
 }
 
 #[derive(argh::FromArgs)]
@@ -134,6 +142,15 @@ struct DisconnectCmd {
     datadir: PathBuf,
 }
 
+#[derive(argh::FromArgs)]
+#[argh(subcommand, name = "abandon")]
+/// Abandon all unconfirmed transaction
+struct AbandonCmd {
+    #[argh(positional)]
+    /// sender address
+    from: String,
+}
+
 fn main() {
     init();
     let args: Args = argh::from_env();
@@ -170,6 +187,7 @@ fn main() {
                     from,
                     to,
                     value,
+                    nonce: None,
                     gas: None,
                     gas_price: None,
                     data: Hex(vec![]),
@@ -197,7 +215,28 @@ fn main() {
             let balance = rpc.balance(&addr).unwrap();
             println!("{}", (balance / 10_000_000_000u64).as_u64());
         }
-        Cmd::ClearDB(_) => todo!(),
+        Cmd::ResetDb(ResetCmd { config }) => {
+            let config = CoreConfig::load_taler_config(Some(&config));
+            let block = rpc.earliest_block().unwrap();
+            let mut db = Client::connect(&config.db_url, NoTls).unwrap();
+            let mut tx = db.transaction().unwrap();
+            // Clear transaction tables and reset state
+            tx.execute("DELETE FROM tx_in", &[]).unwrap();
+            tx.execute("DELETE FROM tx_out", &[]).unwrap();
+            tx.execute("DELETE FROM bounce", &[]).unwrap();
+            tx.execute(
+                "UPDATE state SET value=$1 WHERE name='sync'",
+                &[&SyncState {
+                    tip_hash: block.hash.unwrap(),
+                    tip_height: block.number.unwrap(),
+                    conf_height: block.number.unwrap(),
+                }
+                .to_bytes()
+                .as_ref()],
+            )
+            .unwrap();
+            tx.commit().unwrap();
+        }
         Cmd::Connect(ConnectCmd { datadir }) => {
             let mut peer = Rpc::new(datadir.join("geth.ipc")).unwrap();
             let mut enode = peer.node_info().unwrap().enode;
@@ -212,5 +251,23 @@ fn main() {
             enode.set_host(Some("127.0.0.1")).unwrap();
             assert!(rpc.remove_peer(&enode).unwrap());
         }
+        Cmd::Abandon(AbandonCmd { from }) => {
+            let from = H160::from_str(&from).unwrap();
+            rpc.unlock_account(&from, "password").ok();
+            let pending = rpc.pending_transactions().unwrap();
+            for tx in pending.into_iter().filter(|t| t.from == Some(from)) {
+                // Replace transaction value with 0
+                rpc.send_transaction(&TransactionRequest {
+                    from,
+                    to: tx.to.unwrap(),
+                    value: U256::zero(),
+                    gas: None,
+                    gas_price: Some(U256::from(1)), // Bigger gas price to 
replace fee
+                    data: Hex(vec![]),
+                    nonce: Some(tx.nonce),
+                })
+                .unwrap();
+            }
+        }
     }
 }
diff --git a/btc-wire/src/fail_point.rs b/eth-wire/src/fail_point.rs
similarity index 100%
copy from btc-wire/src/fail_point.rs
copy to eth-wire/src/fail_point.rs
diff --git a/eth-wire/src/lib.rs b/eth-wire/src/lib.rs
index c689a63..b386006 100644
--- a/eth-wire/src/lib.rs
+++ b/eth-wire/src/lib.rs
@@ -23,6 +23,8 @@ pub mod metadata;
 pub mod rpc;
 pub mod taler_util;
 
+const GWEI: u64 = 1_000_000_000;
+
 impl rpc::Rpc {
     pub fn deposit(
         &mut self,
@@ -36,6 +38,7 @@ impl rpc::Rpc {
             from,
             to,
             value,
+            nonce: None,
             gas: None,
             gas_price: None,
             data: Hex(metadata.encode()),
@@ -55,26 +58,38 @@ impl rpc::Rpc {
             from,
             to,
             value,
+            nonce: None,
             gas: None,
             gas_price: None,
             data: Hex(metadata.encode()),
         })
     }
 
-    pub fn bounce(&mut self, hash: H256, bounce_fee: U256) -> 
rpc::Result<H256> {
+    pub fn bounce(&mut self, hash: H256, bounce_fee: U256) -> 
rpc::Result<Option<H256>> {
         let tx = self
             .get_transaction(&hash)?
             .expect("Cannot bounce a non existent transaction");
         let bounce_value = tx.value.saturating_sub(bounce_fee);
         let metadata = OutMetadata::Bounce { bounced: hash };
-        // TODO do not bounce empty amount
-        self.send_transaction(&rpc::TransactionRequest {
+        let mut request = rpc::TransactionRequest {
             from: tx.to.expect("Cannot bounce contract transaction"),
             to: tx.from.expect("Cannot bounce coinbase transaction"),
             value: bounce_value,
+            nonce: None,
             gas: None,
             gas_price: None,
             data: Hex(metadata.encode()),
+        };
+        // Estimate fee price using node
+        let fill = self.fill_transaction(&request)?;
+        // Deduce fee price from bounced value
+        request.value = request
+            .value
+            .saturating_sub(fill.tx.gas * GWEI * fill.tx.gas_price);
+        Ok(if request.value.is_zero() {
+            None
+        } else {
+            Some(self.send_transaction(&request)?)
         })
     }
 }
diff --git a/eth-wire/src/loops/watcher.rs b/eth-wire/src/loops/watcher.rs
index 42b3ecb..05396ab 100644
--- a/eth-wire/src/loops/watcher.rs
+++ b/eth-wire/src/loops/watcher.rs
@@ -13,13 +13,26 @@
   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::rpc::Rpc;
-use common::postgres::Client;
+use common::{log::log::error, reconnect::AutoReconnectDb};
+use eth_wire::rpc::AutoReconnectRPC;
 
-pub fn watcher(mut rpc: Rpc, mut db: Client) {
-    let mut notifier = rpc.subscribe_new_head().unwrap();
+use crate::LoopResult;
+
+/// Wait for new block and notify arrival with postgreSQL notifications
+pub fn watcher(mut rpc: AutoReconnectRPC, mut db: AutoReconnectDb) {
     loop {
-        db.execute("NOTIFY new_block", &[]).unwrap();
-        notifier.next().unwrap();
+        let rpc = rpc.client();
+        let db = db.client();
+
+        let result: LoopResult<()> = (|| {
+            let mut notifier = rpc.subscribe_new_head()?;
+            loop {
+                db.execute("NOTIFY new_block", &[])?;
+                notifier.next()?;
+            }
+        })();
+        if let Err(e) = result {
+            error!("watcher: {}", e);
+        }
     }
 }
diff --git a/eth-wire/src/loops/worker.rs b/eth-wire/src/loops/worker.rs
index b812c10..0a245c6 100644
--- a/eth-wire/src/loops/worker.rs
+++ b/eth-wire/src/loops/worker.rs
@@ -17,25 +17,27 @@ use std::{fmt::Write, sync::atomic::Ordering, 
time::SystemTime};
 
 use common::{
     api_common::base32,
-    log::log::{error, info},
+    log::log::{error, info, warn},
     postgres::{fallible_iterator::FallibleIterator, Client},
+    reconnect::AutoReconnectDb,
     sql::{sql_array, sql_url},
     status::{BounceStatus, WithdrawStatus},
 };
 use eth_wire::{
-    metadata::InMetadata,
-    rpc::{self, Rpc, Transaction},
+    metadata::{InMetadata, OutMetadata},
+    rpc::{self, AutoReconnectRPC, Rpc, Transaction},
     taler_util::{eth_payto_url, eth_to_taler},
     SyncState,
 };
 use ethereum_types::{Address, H256, U256, U64};
 
 use crate::{
+    fail_point::fail_point,
     sql::{sql_addr, sql_eth_amount, sql_hash},
-    LoopResult, WireState,
+    LoopError, LoopResult, WireState,
 };
 
-pub fn worker(mut rpc: Rpc, mut db: Client, state: &WireState) {
+pub fn worker(mut rpc: AutoReconnectRPC, mut db: AutoReconnectDb, state: 
&WireState) {
     let mut lifetime = state.config.wire_lifetime;
     let mut status = true;
     let mut skip_notification = false;
@@ -51,6 +53,10 @@ pub fn worker(mut rpc: Rpc, mut db: Client, state: 
&WireState) {
             }
         }
 
+        // Connect
+        let rpc = rpc.client();
+        let db = db.client();
+
         let result: LoopResult<()> = (|| {
             // Listen to all channels
             db.batch_execute("LISTEN new_block; LISTEN new_tx")?;
@@ -66,24 +72,48 @@ pub fn worker(mut rpc: Rpc, mut db: Client, state: 
&WireState) {
                 while iter.next()?.is_some() {}
             }
 
-            sync_chain(&mut rpc, &mut db, state, &mut status)?;
+            // It is not possible to atomically update the blockchain and the 
database.
+            // When we failed to sync the database and the blockchain state we 
rely on
+            // sync_chain to recover the lost updates.
+            // When this function is running concurrently, it not possible to 
known another
+            // execution has failed, and this can lead to a transaction being 
sent multiple time.
+            // To ensure only a single version of this function is running at 
a given time we rely
+            // on postgres advisory lock
+
+            // Take the lock
+            let row = db.query_one("SELECT pg_try_advisory_lock(42)", &[])?;
+            let locked: bool = row.get(0);
+            if !locked {
+                return Err(LoopError::Concurrency);
+            }
+
+            // Sync chain
+            sync_chain(rpc, db, state, &mut status)?;
 
-            while withdraw(&mut db, &mut rpc, state)? {}
+            // As we are now in sync with the blockchain if a transaction has 
Requested status it have not been sent
 
-            while bounce(&mut db, &mut rpc, 
U256::from(state.config.bounce_fee))? {}
+            // Send requested withdraws
+            while withdraw(db, rpc, state)? {}
+
+            // Send requested bounce
+            while bounce(db, rpc, U256::from(state.config.bounce_fee))? {}
 
             Ok(())
         })();
 
         if let Err(e) = result {
             error!("worker: {}", e);
-            skip_notification = false;
+            skip_notification = !matches!(
+                e,
+                LoopError::RPC(rpc::Error::RPC { .. }) | LoopError::Concurrency
+            );
         } else {
             skip_notification = false;
         }
     }
 }
 
+/// List new and removed transaction since the last sync state, returning a 
new sync state
 fn list_since_block_state(
     rpc: &mut Rpc,
     address: &Address,
@@ -150,6 +180,7 @@ fn list_since_block_state(
     ))
 }
 
+/// Parse new transactions, return true if the database is up to date with the 
latest mined block
 fn sync_chain(
     rpc: &mut Rpc,
     db: &mut Client,
@@ -187,34 +218,9 @@ fn sync_chain(
 
     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),
-                            );
-                        }
-                    }
-                },
-                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()],
-                    )?;
-                }
-            }
+            sync_chain_incoming_confirmed(&tx, db, state)?;
+        } else if tx.from == Some(state.address) {
+            sync_chain_outgoing(&tx, db, state)?;
         }
     }
 
@@ -238,7 +244,7 @@ fn sync_chain_removed(
     // - An incoming invalid transactions already bounced
     // Those two cases can compromise ethereum backing
     // Removed outgoing transactions will be retried automatically by the node
-    
+
     let mut blocking_deposit = Vec::new();
     let mut blocking_bounce = Vec::new();
 
@@ -308,6 +314,149 @@ fn sync_chain_removed(
     }
 }
 
+/// Sync database with an incoming confirmed transaction
+fn sync_chain_incoming_confirmed(
+    tx: &Transaction,
+    db: &mut Client,
+    state: &WireState,
+) -> Result<(), LoopError> {
+    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),
+                    );
+                }
+            }
+        },
+        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()],
+            )?;
+        }
+    }
+    Ok(())
+}
+
+/// Sync database with an outgoing transaction
+fn sync_chain_outgoing(tx: &Transaction, db: &mut Client, state: &WireState) 
-> LoopResult<()> {
+    match OutMetadata::decode(&tx.input) {
+        Ok(metadata) => match metadata {
+            OutMetadata::Withdraw { wtid, .. } => {
+                let amount = eth_to_taler(&tx.value);
+                let credit_addr = tx.to.unwrap();
+                // Get previous out tx
+                let row = db.query_opt(
+                    "SELECT id, status, txid FROM tx_out WHERE wtid=$1 FOR 
UPDATE",
+                    &[&wtid.as_ref()],
+                )?;
+                if let Some(row) = row {
+                    // If already in database, sync status
+                    let row_id: i32 = row.get(0);
+                    let status: i16 = row.get(1);
+                    match WithdrawStatus::try_from(status as u8).unwrap() {
+                        WithdrawStatus::Requested => {
+                            let nb_row = db.execute(
+                                "UPDATE tx_out SET status=$1, txid=$2 WHERE 
id=$3 AND status=$4",
+                                &[
+                                    &(WithdrawStatus::Sent as i16),
+                                    &tx.hash.as_ref(),
+                                    &row_id,
+                                    &status,
+                                ],
+                            )?;
+                            if nb_row > 0 {
+                                warn!(
+                                    ">> (recovered) {} {} in {} to {}",
+                                    amount,
+                                    base32(&wtid),
+                                    hex::encode(tx.hash),
+                                    hex::encode(credit_addr)
+                                );
+                            }
+                        }
+                        WithdrawStatus::Sent => { /* Status is correct */ }
+                    }
+                } else {
+                    // Else add to database
+                    let date = SystemTime::now();
+                    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(), 
&eth_payto_url(&state.address).as_ref(), &eth_payto_url(&credit_addr).as_ref(), 
&state.config.base_url.as_ref(), &(WithdrawStatus::Sent as i16), 
&tx.hash.as_ref(), &None::<&[u8]>],
+                        )?;
+                    if nb > 0 {
+                        warn!(
+                            ">> (onchain) {} {} in {} to {}",
+                            amount,
+                            base32(&wtid),
+                            hex::encode(tx.hash),
+                            hex::encode(credit_addr)
+                        );
+                    }
+                }
+            }
+            OutMetadata::Bounce { bounced } => {
+                // Get previous bounce
+                let row = db.query_opt(
+                    "SELECT id, status FROM bounce WHERE bounced=$1",
+                    &[&bounced.as_ref()],
+                )?;
+                if let Some(row) = row {
+                    // If already in database, sync status
+                    let row_id: i32 = row.get(0);
+                    let status: i16 = row.get(1);
+                    match BounceStatus::try_from(status as u8).unwrap() {
+                        BounceStatus::Requested => {
+                            let nb_row = db.execute(
+                                "UPDATE bounce SET status=$1, txid=$2 WHERE 
id=$3 AND status=$4",
+                                &[
+                                    &(BounceStatus::Sent as i16),
+                                    &tx.hash.as_ref(),
+                                    &row_id,
+                                    &status,
+                                ],
+                            )?;
+                            if nb_row > 0 {
+                                warn!("|| (recovered) {} in {}", &bounced, 
hex::encode(tx.hash));
+                            }
+                        }
+                        BounceStatus::Ignored => error!(
+                            "watcher: ignored bounce {} found in chain at {}",
+                            bounced,
+                            hex::encode(tx.hash)
+                        ),
+                        BounceStatus::Sent => { /* Status is correct */ }
+                    }
+                } else {
+                    // Else add to database
+                    let nb = db.execute(
+        "INSERT INTO bounce (bounced, txid, status) VALUES ($1, $2, $3) ON 
CONFLICT (txid) DO NOTHING",
+        &[&bounced.as_ref(), &tx.hash.as_ref(), &(BounceStatus::Sent as i16)],
+            )?;
+                    if nb > 0 {
+                        warn!("|| (onchain) {} in {}", &bounced, 
hex::encode(tx.hash));
+                    }
+                }
+            }
+        },
+        Err(_) => { /* Ignore */ }
+    }
+    Ok(())
+}
+
 /// Send a withdraw transaction on the blockchain, return false if no more 
requested transaction are found
 fn withdraw(db: &mut Client, rpc: &mut Rpc, state: &WireState) -> 
LoopResult<bool> {
     // We rely on the advisory lock to ensure we are the only one sending 
transactions
@@ -322,6 +471,7 @@ fn withdraw(db: &mut Client, rpc: &mut Rpc, state: 
&WireState) -> LoopResult<boo
         let addr = sql_addr(row, 3);
         let url = sql_url(row, 4);
         let tx_id = rpc.withdraw(state.address, addr, amount, wtid, url)?;
+        fail_point("(injected) fail withdraw", 0.3)?;
         db.execute(
             "UPDATE tx_out SET status=$1, txid=$2 WHERE id=$3",
             &[&(WithdrawStatus::Sent as i16), &tx_id.as_ref(), &id],
@@ -349,24 +499,23 @@ fn bounce(db: &mut Client, rpc: &mut Rpc, fee: U256) -> 
LoopResult<bool> {
         let id: i32 = row.get(0);
         let bounced: H256 = sql_hash(row, 1);
 
-        match rpc.bounce(bounced, fee) {
-            Ok(it) => {
+        let bounce = rpc.bounce(bounced, fee)?;
+        match bounce {
+            Some(hash) => {
+                fail_point("(injected) fail bounce", 0.3)?;
                 db.execute(
                     "UPDATE bounce SET txid=$1, status=$2 WHERE id=$3",
-                    &[&it.as_ref(), &(BounceStatus::Sent as i16), &id],
+                    &[&hash.as_ref(), &(BounceStatus::Sent as i16), &id],
                 )?;
-                info!("|| {} in {}", hex::encode(&bounced), hex::encode(&it));
+                info!("|| {} in {}", hex::encode(&bounced), 
hex::encode(&hash));
+            }
+            None => {
+                db.execute(
+                    "UPDATE bounce SET status=$1 WHERE id=$2",
+                    &[&(BounceStatus::Ignored as i16), &id],
+                )?;
+                info!("|| (ignore) {} ", &bounced);
             }
-            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/main.rs b/eth-wire/src/main.rs
index 8165c4a..fdcd618 100644
--- a/eth-wire/src/main.rs
+++ b/eth-wire/src/main.rs
@@ -18,15 +18,18 @@ use std::sync::atomic::AtomicU16;
 
 use common::{
     config::{load_eth_config, EthConfig},
-    postgres::{self, Client, NoTls},
+    postgres,
+    reconnect::auto_reconnect_db,
 };
 use eth_wire::{
-    rpc::{self, Rpc},
+    rpc::{self, auto_reconnect_rpc},
     taler_util::eth_payto_addr,
 };
 use ethereum_types::H160;
+use fail_point::Injected;
 use loops::{watcher::watcher, worker::worker};
 
+mod fail_point;
 mod loops;
 mod sql;
 
@@ -42,10 +45,10 @@ pub enum LoopError {
     RPC(#[from] rpc::Error),
     #[error(transparent)]
     DB(#[from] postgres::Error),
-    #[error("Another btc_wire process is running concurrently")]
+    #[error("Another eth-wire process is running concurrently")]
     Concurrency,
-    // #[error(transparent)]
-    // Injected(#[from] Injected),
+    #[error(transparent)]
+    Injected(#[from] Injected),
 }
 
 pub type LoopResult<T> = Result<T, LoopError>;
@@ -62,33 +65,12 @@ fn main() {
         config,
     }));
 
-    let mut rpc_worker = Rpc::new(
-        state
-            .config
-            .core
-            .data_dir
-            .as_ref()
-            .unwrap()
-            .join("geth.ipc"),
-    )
-    .unwrap();
-
-    rpc_worker
-        .unlock_account(&state.address, "password")
-        .unwrap();
-
-    let rpc_watcher = Rpc::new(
-        state
-            .config
-            .core
-            .data_dir
-            .as_ref()
-            .unwrap()
-            .join("geth.ipc"),
-    )
-    .unwrap();
-    let db_watcher = Client::connect(&state.config.core.db_url, 
NoTls).unwrap();
-    let db_worker = Client::connect(&state.config.core.db_url, NoTls).unwrap();
+    let rpc_worker = 
auto_reconnect_rpc(state.config.core.data_dir.clone().unwrap(), state.address);
+    let rpc_watcher =
+        auto_reconnect_rpc(state.config.core.data_dir.clone().unwrap(), 
state.address);
+
+    let db_watcher = auto_reconnect_db(state.config.core.db_url.clone());
+    let db_worker = auto_reconnect_db(state.config.core.db_url.clone());
 
     std::thread::spawn(move || watcher(rpc_watcher, db_watcher));
 
diff --git a/eth-wire/src/rpc.rs b/eth-wire/src/rpc.rs
index e626cd6..8c96898 100644
--- a/eth-wire/src/rpc.rs
+++ b/eth-wire/src/rpc.rs
@@ -19,10 +19,10 @@
 //! We only parse the thing we actually use, this reduce memory usage and
 //! make our code more compatible with future deprecation
 
-use common::url::Url;
+use common::{log::log::error, reconnect::AutoReconnect, url::Url};
 use ethereum_types::{Address, H256, U256, U64};
 use serde::de::DeserializeOwned;
-use serde_json::error::Category;
+use serde_json::{error::Category, Value};
 use std::{
     fmt::Debug,
     io::{self, BufWriter, ErrorKind, Read, Write},
@@ -33,6 +33,22 @@ use std::{
 
 use self::hex::Hex;
 
+pub type AutoReconnectRPC = AutoReconnect<(PathBuf, Address), Rpc>;
+
+pub fn auto_reconnect_rpc(data_dir: PathBuf, address: Address) -> 
AutoReconnectRPC {
+    AutoReconnect::new(
+        (data_dir.join("geth.ipc"), address),
+        |(path, address)| {
+            let mut rpc = Rpc::new(path)
+                .map_err(|err| error!("connect RPC: {}", err))
+                .ok()?;
+            rpc.unlock_account(address, "password").ok()?;
+            Some(rpc)
+        },
+        |client| client.node_info().is_err(),
+    )
+}
+
 #[derive(Debug, serde::Serialize)]
 struct RpcRequest<'a, T: serde::Serialize> {
     method: &'a str,
@@ -193,6 +209,10 @@ impl Rpc {
         }
     }
 
+    pub fn fill_transaction(&mut self, params: &TransactionRequest) -> 
Result<Filled> {
+        self.call("eth_fillTransaction", &[params])
+    }
+
     pub fn send_transaction(&mut self, params: &TransactionRequest) -> 
Result<H256> {
         self.call("eth_sendTransaction", &[params])
     }
@@ -232,6 +252,10 @@ impl Rpc {
         self.call("eth_getBlockByNumber", &("latest", &true))
     }
 
+    pub fn earliest_block(&mut self) -> Result<Block> {
+        self.call("eth_getBlockByNumber", &("earliest", &true))
+    }
+
     pub fn balance(&mut self, addr: &Address) -> Result<U256> {
         self.call("eth_getBalance", &(addr, "latest"))
     }
@@ -313,6 +337,8 @@ pub struct BlockHead {}
 pub struct Transaction {
     /// Hash
     pub hash: H256,
+    /// None
+    pub nonce: U256,
     /// Sender address (None when coinbase)
     pub from: Option<Address>,
     /// Recipient address (None when contract creation)
@@ -323,6 +349,22 @@ pub struct Transaction {
     pub input: Hex,
 }
 
+/// Fill result
+#[derive(Debug, serde::Deserialize)]
+pub struct Filled {
+    pub tx: FilledGas,
+}
+
+/// Filles gas
+#[derive(Debug, serde::Deserialize)]
+pub struct FilledGas {
+    /// Supplied gas
+    pub gas: U256,
+    /// Gas price
+    #[serde(rename = "gasPrice")]
+    pub gas_price: U256,
+}
+
 /// Send Transaction Parameters
 #[derive(Debug, serde::Serialize)]
 pub struct TransactionRequest {
@@ -341,6 +383,9 @@ pub struct TransactionRequest {
     pub gas_price: Option<U256>,
     /// Transaction data
     pub data: Hex,
+    /// Transaction nonce (None for next available nonce)
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub nonce: Option<U256>,
 }
 
 #[derive(Debug, serde::Deserialize)]
diff --git a/makefile b/makefile
index cea4d3a..0544e84 100644
--- a/makefile
+++ b/makefile
@@ -22,6 +22,8 @@ test_btc: install
 test_eth: install
        test/eth/wire.sh
        test/eth/lifetime.sh
+       test/eth/reconnect.sh
+       test/eth/stress.sh
        test/eth/reorg.sh
 
 test: install test_gateway test_eth test_btc
\ No newline at end of file
diff --git a/test/btc/analysis.sh b/test/btc/analysis.sh
index c667c84..cc08753 100644
--- a/test/btc/analysis.sh
+++ b/test/btc/analysis.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Test btc_wire ability to learn and protect itself from blockchain behavior
+## Test btc-wire ability to learn and protect itself from blockchain behavior
 
 set -eu
 
@@ -26,17 +26,17 @@ echo ""
 echo "----- Learn from reorg -----"
 
 echo "Loose second bitcoin node"
-btc2_deco
+btc_deco
 
 echo -n "Making wire transfer to exchange:"
-btc-wire-utils -d $WIRE_DIR transfer 0.042 > /dev/null
-next_btc # Trigger btc_wire
+$WIRE_UTILS transfer 0.042 > /dev/null
+next_btc # Trigger btc-wire
 check_balance 9.95799209 0.04200000
 echo " OK"
 
 echo -n "Perform fork and check btc-wire hard error:"
 gateway_up
-btc2_fork 5
+btc_fork 5
 check_balance 9.95799209 0.00000000
 gateway_down
 echo " OK"
@@ -48,17 +48,17 @@ gateway_up
 echo " OK"
 
 echo "Loose second bitcoin node"
-btc2_deco
+btc_deco
 
 echo -n "Making wire transfer to exchange:"
-btc-wire-utils -d $WIRE_DIR transfer 0.064 > /dev/null
+$WIRE_UTILS transfer 0.064 > /dev/null
 next_btc 5 # More block needed to confirm
 check_balance 9.89398418 0.10600000
 echo " OK"
 
 echo -n "Perform fork and check btc-wire learned from previous attack:"
 gateway_up
-btc2_fork 5
+btc_fork 5
 check_balance 9.89398418 0.10600000
 gateway_up
 echo " OK"
diff --git a/test/btc/bumpfee.sh b/test/btc/bumpfee.sh
index 58d7818..028e135 100644
--- a/test/btc/bumpfee.sh
+++ b/test/btc/bumpfee.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Test btc_wire ability to handle stuck transaction correctly
+## Test btc-wire ability to handle stuck transaction correctly
 
 set -eu
 
@@ -27,10 +27,10 @@ SEQ="seq 10 30"
 
 echo -n "Making wire transfer to exchange:"
 for n in `$SEQ`; do
-    btc-wire-utils -d $WIRE_DIR transfer 0.$n > /dev/null
+    $WIRE_UTILS transfer 0.$n > /dev/null
     mine_btc # Mine transactions
 done
-next_btc # Trigger btc_wire
+next_btc # Trigger btc-wire
 check_balance 5.79983389 4.20000000
 echo " OK"
 
@@ -59,7 +59,7 @@ echo " OK"
 echo "----- Bump fee reorg -----"
 
 echo "Loose second bitcoin node"
-btc2_deco
+btc_deco
 
 echo -n "Making wire transfer from exchange:"
 taler-exchange-wire-gateway-client \
@@ -71,7 +71,7 @@ check_balance 5.80383389 4.19196020
 echo " OK"
 
 echo -n "Perform fork and bump relay fee:"
-btc2_fork 6
+btc_fork 6
 restart_btc -minrelaytxfee=0.0002
 mine_btc
 echo " OK"
@@ -85,7 +85,7 @@ echo " OK"
 
 echo "----- Bump fee stress -----"
 
-echo -n "Replace btc_wire with stressed btc_wire"
+echo -n "Replace btc-wire with stressed btc-wire"
 kill $WIRE_PID
 stress_btc_wire
 echo " OK"
diff --git a/test/btc/config.sh b/test/btc/config.sh
index 9e5767c..d1ca7e7 100644
--- a/test/btc/config.sh
+++ b/test/btc/config.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Test btc_wire ability to configure itself from bitcoin configuration
+## Test btc-wire ability to configure itself from bitcoin configuration
 
 set -eu
 
diff --git a/test/btc/conflict.sh b/test/btc/conflict.sh
index 832dfea..8ee7604 100644
--- a/test/btc/conflict.sh
+++ b/test/btc/conflict.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Test btc_wire ability to handle conflicting outgoing transactions
+## Test btc-wire ability to handle conflicting outgoing transactions
 
 set -eu
 
@@ -26,7 +26,7 @@ echo ""
 echo "----- Conflict send -----"
 
 echo -n "Making wire transfer to exchange:"
-btc-wire-utils -d $WIRE_DIR transfer 0.042 > /dev/null
+$WIRE_UTILS transfer 0.042 > /dev/null
 next_btc
 check_balance 9.95799209 0.04200000
 echo " OK"
@@ -42,7 +42,7 @@ echo " OK"
 
 echo -n "Abandon pending transaction:"
 restart_btc -minrelaytxfee=0.0001
-btc-wire-utils -d $WIRE_DIR abandon
+$WIRE_UTILS abandon
 check_balance 9.95799209 0.04200000
 echo " OK"
 
@@ -93,7 +93,7 @@ echo " OK"
 
 echo -n "Abandon pending transaction:"
 restart_btc -minrelaytxfee=0.0001
-btc-wire-utils -d $WIRE_DIR abandon
+$WIRE_UTILS abandon
 check_balance 9.95999859 0.04000000
 echo " OK"
 
diff --git a/test/btc/hell.sh b/test/btc/hell.sh
index 13ada76..67559fa 100644
--- a/test/btc/hell.sh
+++ b/test/btc/hell.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Test btc_wire correctness when a blockchain reorganisation occurs leading 
to past incoming transaction conflict
+## Test btc-wire correctness when a blockchain reorganisation occurs leading 
to past incoming transaction conflict
 
 set -eu
 
@@ -23,37 +23,36 @@ echo "Start gateway"
 gateway
 echo ""
 
-echo  "----- Handle reorg conflicting incoming receive -----"
+echo  "----- Handle reorg conflicting incoming deposit -----"
 
 echo "Loose second bitcoin node"
-btc2_deco
+btc_deco
 
 echo -n "Gen incoming transactions:"
-btc-wire-utils -d $WIRE_DIR transfer 0.0042 > /dev/null
-next_btc # Trigger btc_wire
+$WIRE_UTILS transfer 0.0042 > /dev/null
+next_btc # Trigger btc-wire
 check_balance 9.99579209 0.00420000
 echo " OK"
 
 echo -n "Perform fork and check btc-wire hard error:"
 gateway_up
-btc2_fork 5
+btc_fork 5
 check_balance 9.99579209 0.00000000
 gateway_down
 echo " OK"
 
 echo -n "Generate conflict:"
 restart_btc -minrelaytxfee=0.0001
-btc-wire-utils -d $WIRE_DIR abandon client
-btc-wire-utils -d $WIRE_DIR transfer 0.0054 > /dev/null
+$WIRE_UTILS abandon client
+$WIRE_UTILS transfer 0.0054 > /dev/null
 next_btc
 check_balance 9.99457382 0.00540000
 echo " OK"
 
 echo -n "Check btc-wire have not read the conflicting transaction:"
-sleep 5 # Wait for reconnection # Wait for reconnection
+sleep 5 # Wait for reconnection
 gateway_down
 check_balance 9.99457382 0.00540000
-check_delta "incoming" ""
 echo " OK"
 
 # Recover by paying for the customer ?
@@ -79,7 +78,7 @@ echo ""
 echo  "----- Handle reorg conflicting incoming bounce -----"
 
 echo "Loose second bitcoin node"
-btc2_deco
+btc_deco
 
 echo -n "Generate bounce:"
 $BTC_CLI -rpcwallet=client sendtoaddress $WIRE 0.042 > /dev/null
@@ -89,15 +88,15 @@ echo " OK"
 
 echo -n "Perform fork and check btc-wire hard error:"
 gateway_up
-btc2_fork 5
+btc_fork 5
 check_balance 9.95799859 0.00000000
 gateway_down
 echo " OK"
 
 echo -n "Generate conflict:"
 restart_btc -minrelaytxfee=0.0001
-btc-wire-utils -d $WIRE_DIR abandon client
-btc-wire-utils -d $WIRE_DIR transfer 0.054 > /dev/null
+$WIRE_UTILS abandon client
+$WIRE_UTILS transfer 0.054 > /dev/null
 next_btc
 check_balance 9.94597382 0.05400000
 echo " OK"
@@ -106,7 +105,6 @@ echo -n "Check btc-wire have not read the conflicting 
transaction:"
 sleep 5 # Wait for reconnection
 gateway_down
 check_balance 9.94597382 0.05400000
-check_delta "incoming" ""
 echo " OK"
 
 
diff --git a/test/btc/lifetime.sh b/test/btc/lifetime.sh
index a0260b5..6925100 100644
--- a/test/btc/lifetime.sh
+++ b/test/btc/lifetime.sh
@@ -33,10 +33,10 @@ echo " OK"
 
 echo -n "Do some work:"
 for n in `$SEQ`; do
-    btc-wire-utils -d $WIRE_DIR transfer 0.000$n > /dev/null
+    $WIRE_UTILS transfer 0.000$n > /dev/null
     mine_btc # Mine transactions
 done
-next_btc # Trigger btc_wire
+next_btc # Trigger btc-wire
 check_balance 9.99826299 0.00165000
 for n in `$SEQ`; do
     taler-exchange-wire-gateway-client \
diff --git a/test/btc/maxfee.sh b/test/btc/maxfee.sh
index f9aabea..0c93419 100644
--- a/test/btc/maxfee.sh
+++ b/test/btc/maxfee.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Test btc_wire ability to handle stuck transaction correctly
+## Test btc-wire ability to handle stuck transaction correctly
 
 set -eu
 
@@ -27,10 +27,10 @@ SEQ="seq 10 30"
 
 echo -n "Making wire transfer to exchange:"
 for n in `$SEQ`; do
-    btc-wire-utils -d $WIRE_DIR transfer 0.$n > /dev/null
+    $WIRE_UTILS transfer 0.$n > /dev/null
     mine_btc # Mine transactions
 done
-next_btc # Trigger btc_wire
+next_btc # Trigger btc-wire
 check_balance 5.79983389 4.20000000
 echo " OK"
 
diff --git a/test/btc/reconnect.sh b/test/btc/reconnect.sh
index 275af14..2175649 100644
--- a/test/btc/reconnect.sh
+++ b/test/btc/reconnect.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Check the capacity of wire_gateway and btc_wire to recover from database 
loss
+## Check the capacity of wire-gateway and btc-wire to recover from database 
and node loss
 
 set -eu
 
@@ -23,37 +23,36 @@ echo ""
 
 echo "----- With DB -----"
 echo "Making wire transfer to exchange:"
-btc-wire-utils -d $WIRE_DIR transfer 0.000042 > /dev/null
+$WIRE_UTILS transfer 0.000042 > /dev/null
 next_btc
 check_balance 9.99995009 0.00004200
 echo -n "Requesting exchange incoming transaction list:"
-taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -i | grep BTC:0.000042 > 
/dev/null && echo " OK" || echo " Failed"
+check_delta "incoming?delta=-100" "echo 42"
+echo "OK"
 
 echo "----- Without DB -----"
 
 echo "Stop database"
-pg_ctl stop -D $DB_DIR > /dev/null
+stop_db
 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-utils -d $WIRE_DIR transfer 0.00004 > /dev/null
+$WIRE_UTILS transfer 0.00004 > /dev/null
 next_btc
 check_balance 9.99948077 0.00050200
 echo " OK"
 echo "Stop bitcoin node"
-stop_btc
+stop_node
 echo -n "Requesting exchange incoming transaction list:"
 taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -i 2>&1 | grep -q "504" 
&& echo " OK" || echo " Failed"
 
 echo "----- Reconnect DB -----"
 
 echo "Start database"
-pg_ctl start -D $DB_DIR > /dev/null
-echo "Start bitcoin node"
+start_db
+echo "Resume bitcoin node"
 resume_btc
 sleep 6 # Wait for connection to be available
-echo -n "Requesting exchange incoming transaction list:"
-taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -i | grep BTC:0.00004 > 
/dev/null && echo " OK" || echo " Failed"
 echo -n "Making wire transfer from exchange:"
 taler-exchange-wire-gateway-client \
     -b $BANK_ENDPOINT \
@@ -65,7 +64,7 @@ check_balance 9.99990892 0.00007001
 echo " OK"
 
 echo -n "Requesting exchange's outgoing transaction list:"
-taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -o | grep BTC:0.00002 > 
/dev/null
+check_delta "outgoing?delta=-100" "echo 2"
 echo " OK"
 
 echo "----- Recover DB -----"
@@ -76,20 +75,15 @@ mine_btc # Trigger worker
 sleep 2
 
 echo -n "Checking recover incoming transactions:"
-ALL=`taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -i`;
-for amount in 0.000042 0.00004; do
-    echo $ALL | grep BTC:$amount > /dev/null && echo -n " OK" || echo -n " 
Failed"
-done
-echo ""
+check_delta "incoming?delta=-100" "echo 42 4"
+echo " OK"
 
 echo -n "Requesting exchange's outgoing transaction list:"
-ALL=`taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -o`;
-for amount in 0.00002; do
-    echo $ALL | grep BTC:$amount > /dev/null && echo -n " OK" || echo -n " 
Failed"
-done
-echo ""
+check_delta "outgoing?delta=-100" "echo 2"
+echo " OK"
 
-# Balance should not have changed
+echo -n "Balance should not have changed:"
 check_balance 9.99990892 0.00007001
+echo " OK"
 
 echo "All tests passed!"
\ No newline at end of file
diff --git a/test/btc/reorg.sh b/test/btc/reorg.sh
index e058213..69c4824 100644
--- a/test/btc/reorg.sh
+++ b/test/btc/reorg.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Test btc_wire correctness when a blockchain reorganisation occurs
+## Test btc-wire correctness when a blockchain reorganisation occurs
 
 set -eu
 
@@ -28,21 +28,21 @@ SEQ="seq 10 20"
 echo  "----- Handle reorg incoming transactions -----"
 
 echo "Loose second bitcoin node"
-btc2_deco
+btc_deco
 
 echo -n "Gen incoming transactions:"
 for n in `$SEQ`; do
-    btc-wire-utils -d $WIRE_DIR transfer 0.000$n > /dev/null
+    $WIRE_UTILS transfer 0.000$n > /dev/null
     mine_btc # Mine transactions
 done
-next_btc # Trigger btc_wire
+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:"
 gateway_up
-btc2_fork 22
+btc_fork 22
 check_balance 9.99826299 0.00000000
 gateway_down
 echo " OK"
@@ -56,7 +56,7 @@ echo " OK"
 echo "----- Handle reorg outgoing transactions -----"
 
 echo "Loose second bitcoin node"
-btc2_deco
+btc_deco
 
 echo -n "Gen outgoing transactions:"
 for n in `$SEQ`; do
@@ -73,7 +73,7 @@ echo " OK"
 
 echo -n "Perform fork and check btc-wire still up:"
 gateway_up
-btc2_fork 22
+btc_fork 22
 check_balance 9.99826299 0.00146311
 gateway_up
 echo " OK"
@@ -89,7 +89,7 @@ clear_wallet
 check_balance "*" 0.00000000
 
 echo "Loose second bitcoin node"
-btc2_deco
+btc_deco
 
 echo -n "Generate bounce:"
 for n in `$SEQ`; do
@@ -103,7 +103,7 @@ echo " OK"
 
 echo -n "Perform fork and check btc-wire hard error:"
 gateway_up
-btc2_fork 22
+btc_fork 22
 check_balance "*" 0.00000000
 gateway_down
 echo " OK"
diff --git a/test/btc/stress.sh b/test/btc/stress.sh
index e5bf0fa..680c368 100644
--- a/test/btc/stress.sh
+++ b/test/btc/stress.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Test btc_wire ability to recover from errors in correctness critical paths 
and prevent concurrent sending
+## Test btc-wire ability to recover from errors in correctness critical paths 
and prevent concurrent sending
 
 set -eu
 
@@ -15,7 +15,7 @@ echo "Start database"
 setup_db
 echo "Start bitcoin node"
 init_btc
-echo "Start btc-wire stressed"
+echo "Start stressed btc-wire"
 stress_btc_wire
 echo "Start gateway"
 gateway
@@ -27,7 +27,7 @@ echo "----- Handle incoming -----"
 
 echo -n "Making wire transfer to exchange:"
 for n in `$SEQ`; do
-    btc-wire-utils -d $WIRE_DIR transfer 0.000$n > /dev/null
+    $WIRE_UTILS transfer 0.000$n > /dev/null
     mine_btc # Mine transactions
 done
 next_btc # Confirm all transactions
@@ -50,9 +50,8 @@ for n in `$SEQ`; do
         -C payto://bitcoin/$CLIENT \
         -a BTC:0.0000$n > /dev/null
 done
-sleep 10 # Give time for btc_wire worker to process 
-mine_btc # Mine transactions
-check_balance 9.99605389 0.00373821 
+sleep 10 # Give time for btc-wire worker to process 
+next_btc # Mine transactions
 echo " OK"
 
 echo -n "Requesting exchange outgoing transaction list:"
@@ -63,12 +62,9 @@ echo -n "Check balance:"
 check_balance 9.99605389 0.00373821 
 echo " OK"
 
-next_btc # Mine transactions
-
 echo "----- Recover DB -----"
 
 echo "Reset database"
-sleep 5
 reset_db
 mine_btc # Trigger worker
 sleep 10
@@ -110,7 +106,6 @@ echo " OK"
 echo "----- Recover DB -----"
 
 echo "Reset database"
-sleep 5
 reset_db
 mine_btc # Trigger worker
 sleep 10
diff --git a/test/btc/wire.sh b/test/btc/wire.sh
index cac806f..b064972 100644
--- a/test/btc/wire.sh
+++ b/test/btc/wire.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Test btc_wire correctly receive and send transactions on the blockchain
+## Test btc-wire correctly receive and send transactions on the blockchain
 
 set -eu
 
@@ -27,10 +27,10 @@ echo  "----- Receive -----"
 
 echo -n "Making wire transfer to exchange:"
 for n in `$SEQ`; do
-    btc-wire-utils -d $WIRE_DIR transfer 0.000$n > /dev/null
+    $WIRE_UTILS transfer 0.000$n > /dev/null
     mine_btc # Mine transactions
 done
-next_btc # Trigger btc_wire
+next_btc # Trigger btc-wire
 check_balance 9.95023810 0.04905000
 echo " OK"
 
diff --git a/test/common.sh b/test/common.sh
index 931d724..5de7477 100644
--- a/test/common.sh
+++ b/test/common.sh
@@ -38,6 +38,8 @@ BTC_CLI2="bitcoin-cli -datadir=$WIRE_DIR2"
 ETH_CLI="geth -datadir=$WIRE_DIR"
 ETH_CLI2="geth -datadir=$WIRE_DIR2"
 
+# ----- Common ----- #
+
 # Load test.conf as bash variables
 function load_config() {
     cp ${BASH_SOURCE%/*}/conf/$CONFIG $CONF
@@ -73,6 +75,11 @@ function check_down() {
     fi
 }
 
+function stop_node() {
+    kill $NODE_PID 
+    wait $NODE_PID
+}
+
 # ----- Database ----- #
 
 # Create new postgresql cluster and init database schema
@@ -86,16 +93,27 @@ function setup_db() {
 
 # Erase database
 function reset_db() {
+    sleep 5 # Wait for loop to stop
     $WIRE_UTILS resetdb $CONF
 }
 
+# Stop database
+function stop_db() {
+    pg_ctl stop -D $DB_DIR >> log/postgres.log
+}
+
+# Start database
+function start_db() {
+    pg_ctl start -D $DB_DIR >> log/postgres.log
+}
+
 # ----- Bitcoin node ----- #
 
 # Start a bitcoind regtest node, generate money, wallet and addresses
 function init_btc() {
     cp ${BASH_SOURCE%/*}/conf/${BTC_CONFIG:-bitcoin.conf} 
$WIRE_DIR/bitcoin.conf
     bitcoind -datadir=$WIRE_DIR $* &>> log/node.log &
-    BTC_PID="$!"
+    NODE_PID="$!"
     # Wait for RPC server to be online
     $BTC_CLI -rpcwait getnetworkinfo > /dev/null
     # Create wire wallet
@@ -123,12 +141,12 @@ function init_btc2() {
 }
 
 # Disconnect the two nodes
-function btc2_deco() {
+function btc_deco() {
     $BTC_CLI disconnectnode 127.0.0.1:8346
 }
 
 # Create a fork on the second node and reconnect the two node
-function btc2_fork() {
+function btc_fork() {
     $BTC_CLI2 generatetoaddress $1 $RESERVE > /dev/null
     $BTC_CLI addnode 127.0.0.1:8346 onetry
     sleep 1
@@ -138,7 +156,7 @@ function btc2_fork() {
 function resume_btc() {
     # Restart node
     bitcoind -datadir=$WIRE_DIR $* &>> log/node.log &
-    BTC_PID="$!" 
+    NODE_PID="$!" 
     # Load wallets
     for wallet in wire client reserve; do
         $BTC_CLI -rpcwait loadwallet $wallet > /dev/null
@@ -147,13 +165,8 @@ function resume_btc() {
     $BTC_CLI addnode 127.0.0.1:8346 onetry
 }
 
-function stop_btc() {
-    kill $BTC_PID 
-    wait $BTC_PID
-}
-
 function restart_btc() {
-    stop_btc
+    stop_node
     resume_btc $*
 }
 
@@ -166,9 +179,9 @@ function mine_btc() {
 function next_btc() {
     # Mine enough block to confirm previous transactions
     mine_btc ${1:-$CONFIRMATION}
-    # Wait for btc_wire to catch up
+    # Wait for btc-wire to catch up
     sleep 0.2
-    # Mine one more block to trigger btc_wire
+    # Mine one more block to trigger btc-wire
     mine_btc
 }
 
@@ -196,16 +209,16 @@ function check_balance() {
 
 # Start btc-wire
 function btc_wire() {    
-    cargo build --bin btc-wire --release &> log/cargo.log
-    target/release/btc-wire $CONF &> log/wire.log &
+    cargo build --bin btc-wire --release &>> log/cargo.log
+    target/release/btc-wire $CONF &>> log/wire.log &
     WIRE_PID="$!"
 }
 
-# Start multiple btc_wire with random failures in parallel
+# Start multiple btc-wire with random failures in parallel
 function stress_btc_wire() {
-   cargo build --bin btc-wire --release --features fail &> log/cargo.log
-   target/release/btc-wire $CONF &> log/wire.log & 
-   target/release/btc-wire $CONF &> log/wire1.log & 
+   cargo build --bin btc-wire --release --features fail &>> log/cargo.log
+   target/release/btc-wire $CONF &>> log/wire.log & 
+   target/release/btc-wire $CONF &>> log/wire1.log & 
 }
 
 # ----- Ethereum node ----- #
@@ -242,9 +255,10 @@ function init_eth() {
   }
 }" > $DIR/genesis.json
     # Initialize blockchain
-    $ETH_CLI init $DIR/genesis.json &> log/node.log
+    $ETH_CLI init $DIR/genesis.json &>> log/node.log
     # Start node
-    $ETH_CLI --miner.recommit 0s --miner.gasprice 0 $* &> log/node.log &
+    $ETH_CLI --miner.recommit 0s --miner.gasprice 0 $* &>> log/node.log &
+    NODE_PID="$!"
     sleep 1
     # Create wire address
     WIRE=`eth-wire-cli initwallet $CONF | grep -oP '(?<=is ).*'`
@@ -254,9 +268,9 @@ function init_eth() {
 # Start a seconf geth dev node connected to the first one
 function init_eth2() {
     # Initialize blockchain
-    $ETH_CLI2 init $DIR/genesis.json &> log/node2.log
+    $ETH_CLI2 init $DIR/genesis.json &>> log/node2.log
     # Start node
-    $ETH_CLI2 --port 30305 --miner.recommit 0s --miner.gasprice 0 $* &> 
log/node2.log &
+    $ETH_CLI2 --port 30305 --miner.recommit 0s --miner.gasprice 0 $* &>> 
log/node2.log &
     sleep 1
     # Create etherbase account for mining
     $ETH_CLI2 account new --password <(echo "password") &> /dev/null
@@ -265,17 +279,32 @@ function init_eth2() {
 }
 
 # Disconnect the two nodes
-function eth2_deco() {
+function eth_deco() {
     $WIRE_UTILS disconnect $WIRE_DIR2
 }
 
 # Create a fork on the second node and reconnect the two node
-function eth2_fork() {
+function eth_fork() {
     $WIRE_UTILS2 mine $RESERVE ${1:-}
     $WIRE_UTILS connect $WIRE_DIR2
     sleep 5
 }
 
+# Restart an initialized geth dev node
+function resume_eth() { 
+    # Start node
+    $ETH_CLI --port 30305 --miner.recommit 0s --miner.gasprice 0 $* &>> 
log/node2.log &
+    NODE_PID="$!"
+    sleep 1
+    # Try to connect nodes
+    $WIRE_UTILS connect $WIRE_DIR2 &> /dev/null || true
+}
+
+function restart_eth() {
+    stop_node
+    resume_eth $*
+}
+
 # Check client and wire balance
 function check_balance_eth() {
     local CLIENT_BALANCE=`$WIRE_UTILS balance $CLIENT`
@@ -295,11 +324,18 @@ function check_balance_eth() {
 
 # Start eth-wire
 function eth_wire() {
-    cargo build --bin eth-wire --release &> log/cargo.log
-    target/release/eth-wire $CONF &> log/wire.log &
+    cargo build --bin eth-wire --release &>> log/cargo.log
+    target/release/eth-wire $CONF &>> log/wire.log &
     WIRE_PID="$!"
 }
 
+# Start multiple eth-wire with random failures in parallel
+function stress_eth_wire() {
+    cargo build --bin eth-wire --release --features fail &>> log/cargo.log
+    target/release/eth-wire $CONF &>> log/wire.log &
+    target/release/eth-wire $CONF &>> log/wire1.log &
+}
+
 # Mine ethereum blocks
 function mine_eth() {
     $WIRE_UTILS mine $RESERVE ${1:-}
@@ -317,7 +353,7 @@ function next_eth() {
 
 # ----- Gateway ------ #
 
-# Start wire_gateway in test mode
+# Start wire-gateway in test mode
 function gateway() {
     cargo build --bin wire-gateway --release --features test &> /dev/null
     target/release/wire-gateway $CONF &>> log/gateway.log & 
diff --git a/test/eth/hell.sh b/test/eth/hell.sh
new file mode 100644
index 0000000..115f700
--- /dev/null
+++ b/test/eth/hell.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+
+## Test eth-wire correctness when a blockchain reorganisation occurs leading 
to past incoming transaction conflict
+
+set -eu
+
+source "${BASH_SOURCE%/*}/../common.sh"
+SCHEMA=eth.sql
+CONFIG=taler_eth.conf
+
+echo  "----- Setup -----"
+echo "Load config file"
+load_config
+echo "Start database"
+setup_db
+echo "Start ethereum node"
+init_eth
+echo "Start second ethereum node"
+init_eth2
+echo "Start eth-wire"
+eth_wire
+echo "Start gateway"
+gateway
+echo ""
+
+echo  "----- Handle reorg conflicting incoming deposit -----"
+
+echo "Loose second ethereum node"
+eth_deco
+
+echo -n "Making wire transfer to exchange:"
+$WIRE_UTILS deposit $CLIENT $WIRE 0.00 42
+next_eth # Trigger eth-wire
+check_balance_eth 999580000 420000
+echo " OK"
+
+echo -n "Perform fork and check eth-wire hard error:"
+gateway_up
+eth_fork 10
+check_balance_eth 1000000000 0
+gateway_down
+echo " OK"
+
+echo -n "Generate conflict:"
+$WIRE_UTILS abandon $CLIENT
+next_eth 5
+check_balance_eth 999999999 0
+echo " OK"
+
+echo -n "Check eth-wire have not read the conflicting transaction:"
+gateway_down
+check_balance_eth 999999999 0
+echo " OK"
+
+# Recover by paying for the customer ?
+
+echo  "----- Reset -----"
+echo "Cleanup"
+cleanup
+source "${BASH_SOURCE%/*}/../common.sh"
+echo "Load config file"
+load_config
+echo "Start database"
+setup_db
+echo "Start ethereum node"
+init_eth
+echo "Start second ethereum node"
+init_eth2
+echo "Start eth-wire"
+eth_wire
+echo "Start gateway"
+gateway
+echo ""
+
+echo  "----- Handle reorg conflicting incoming bounce -----"
+
+echo "Loose second ethereum node"
+eth_deco
+
+echo -n "Bounce:"
+$WIRE_UTILS send $CLIENT $WIRE 0.00 42
+sleep 1
+next_eth 6
+check_balance_eth 999999000 1000
+echo " OK"
+
+echo -n "Perform fork and check eth-wire hard error:"
+gateway_up
+eth_fork 10
+check_balance_eth 1000000000 0
+gateway_down
+echo " OK"
+
+echo -n "Generate conflict:"
+$WIRE_UTILS abandon $CLIENT
+next_eth 5
+check_balance_eth 999999999 0
+echo " OK"
+
+echo -n "Check eth-wire have not read the conflicting transaction:"
+gateway_down
+check_balance_eth 999999999 0
+echo " OK"
+
+
+echo "All tests passed!"
\ No newline at end of file
diff --git a/test/eth/lifetime.sh b/test/eth/lifetime.sh
index 37368c8..935e0a4 100644
--- a/test/eth/lifetime.sh
+++ b/test/eth/lifetime.sh
@@ -32,7 +32,7 @@ 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
+$WIRE_UTILS deposit $CLIENT $WIRE 0.000 `$SEQ` > /dev/null
 next_eth # Trigger eth-wire
 check_balance_eth 999835000 165000
 for n in `$SEQ`; do
diff --git a/test/eth/reconnect.sh b/test/eth/reconnect.sh
new file mode 100644
index 0000000..bcb522f
--- /dev/null
+++ b/test/eth/reconnect.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+
+## Check the capacity of wire-gateway and eth-wire to recover from database 
and node loss
+
+set -eu
+
+source "${BASH_SOURCE%/*}/../common.sh"
+SCHEMA=eth.sql
+CONFIG=taler_eth.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 ""
+
+echo "----- With DB -----"
+
+echo -n "Making wire transfer to exchange:"
+$WIRE_UTILS deposit $CLIENT $WIRE 0.0000 42
+next_eth # Trigger eth-wire
+check_balance_eth 999995800 4200
+echo " OK"
+
+echo -n "Requesting exchange incoming transaction list:"
+check_delta "incoming?delta=-100" "echo 42"
+echo " OK"
+
+echo  "----- Without DB -----"
+
+echo "Stop database"
+stop_db
+echo "Making incomplete wire transfer to exchange"
+$WIRE_UTILS send $CLIENT $WIRE 0.0000 42
+echo -n "Making wire transfer to exchange:"
+$WIRE_UTILS deposit $CLIENT $WIRE 0.0000 4
+next_eth
+check_balance_eth 999987600 12400
+echo "OK"
+echo "Stop ethereum node"
+stop_node
+echo -n "Requesting exchange incoming transaction list:"
+taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -i 2>&1 | grep -q "504" 
&& echo " OK" || echo " Failed"
+
+echo "----- Reconnect DB -----"
+
+echo "Start database"
+start_db
+echo "Resume ethereum node"
+resume_eth
+sleep 6 # Wait for connection to be available
+echo -n "Making wire transfer from exchange:"
+    taler-exchange-wire-gateway-client \
+        -b $BANK_ENDPOINT \
+        -C payto://ethereum/$CLIENT \
+        -a ETH:0.00002 > /dev/null
+sleep 1
+mine_eth # Mine transactions
+check_balance_eth 999992800 7200
+echo " OK"
+
+echo -n "Requesting exchange's outgoing transaction list:"
+check_delta "outgoing?delta=-100" "echo 2"
+echo " OK"
+
+echo  "----- Recover DB -----"
+
+echo "Reset database"
+reset_db
+mine_eth 1 # Trigger worker
+sleep 2
+
+echo -n "Checking recover incoming transactions:"
+check_delta "incoming?delta=-100" "echo 42 4"
+echo " OK"
+
+echo -n "Requesting exchange's outgoing transaction list:"
+check_delta "outgoing?delta=-100" "echo 2"
+echo " OK"
+
+echo -n "Balance should not have changed:"
+check_balance_eth 999992800 7200
+echo " OK"
+
+
+echo "All tests passed!"
\ No newline at end of file
diff --git a/test/eth/reorg.sh b/test/eth/reorg.sh
index 890033d..691964d 100644
--- a/test/eth/reorg.sh
+++ b/test/eth/reorg.sh
@@ -28,10 +28,10 @@ SEQ="seq 10 20"
 echo  "----- Handle reorg incoming transactions -----"
 
 echo "Loose second ethereum node"
-eth2_deco
+eth_deco
 
 echo -n "Making wire transfer to exchange:"
-eth-wire-utils -d $WIRE_DIR deposit $CLIENT $WIRE 0.000 `$SEQ`
+$WIRE_UTILS deposit $CLIENT $WIRE 0.000 `$SEQ`
 next_eth # Trigger eth-wire
 check_delta "incoming?delta=-100" "$SEQ" "0.000"
 check_balance_eth 999835000 165000
@@ -39,7 +39,7 @@ echo " OK"
 
 echo -n "Perform fork and check eth-wire hard error:"
 gateway_up
-eth2_fork 10
+eth_fork 10
 check_balance_eth 1000000000 0
 gateway_down
 echo " OK"
@@ -53,7 +53,7 @@ echo " OK"
 echo "----- Handle reorg outgoing transactions -----"
 
 echo "Loose second ethereum node"
-eth2_deco
+eth_deco
 
 echo -n "Making wire transfer from exchange:"
 for n in `$SEQ`; do
@@ -70,7 +70,7 @@ echo " OK"
 
 echo -n "Perform fork and check eth-wire still up:"
 gateway_up
-eth2_fork 10
+eth_fork 10
 check_balance_eth 999835000 165000
 gateway_up
 echo " OK"
@@ -83,10 +83,10 @@ echo " OK"
 echo "----- Handle reorg bounce -----"
 
 echo "Loose second ethereum node"
-eth2_deco
+eth_deco
 
 echo -n "Bounce:"
-eth-wire-utils -d $WIRE_DIR send $CLIENT $WIRE 0.000 `$SEQ`
+$WIRE_UTILS send $CLIENT $WIRE 0.000 `$SEQ`
 sleep 1
 next_eth 6
 check_balance_eth 999840500 159500
@@ -94,7 +94,7 @@ echo " OK"
 
 echo -n "Perform fork and check eth-wire hard error:"
 gateway_up
-eth2_fork 10
+eth_fork 10
 check_balance_eth 999851500 148500
 gateway_down
 echo " OK"
diff --git a/test/eth/stress.sh b/test/eth/stress.sh
new file mode 100644
index 0000000..8aa00c7
--- /dev/null
+++ b/test/eth/stress.sh
@@ -0,0 +1,112 @@
+#!/bin/bash
+
+## Test eth-wire ability to recover from errors in correctness critical paths 
and prevent concurrent sending
+
+set -eu
+
+source "${BASH_SOURCE%/*}/../common.sh"
+SCHEMA=eth.sql
+CONFIG=taler_eth.conf
+
+echo  "----- Setup -----"
+echo "Load config file"
+load_config
+echo "Start database"
+setup_db
+echo "Start ethereum node"
+init_eth
+echo "Start stressed eth-wire"
+stress_eth_wire
+echo "Start gateway"
+gateway
+echo ""
+
+SEQ="seq 10 30"
+
+echo  "----- Handle incoming -----"
+
+echo -n "Making wire transfer to exchange:"
+$WIRE_UTILS deposit $CLIENT $WIRE 0.000 `$SEQ`
+next_eth # Trigger eth-wire
+echo " OK"
+
+echo -n "Requesting exchange incoming transaction list:"
+check_delta "incoming?delta=-100" "$SEQ" "0.000"
+echo " OK"
+
+echo -n "Check balance:"
+check_balance_eth 999580000 420000
+echo " OK"
+
+echo  "----- Handle outgoing -----"
+
+echo -n "Making wire transfer from exchange:"
+for n in `$SEQ`; do
+    taler-exchange-wire-gateway-client \
+        -b $BANK_ENDPOINT \
+        -C payto://ethereum/$CLIENT \
+        -a ETH:0.0000$n > /dev/null
+done
+sleep 1
+next_eth # Mine transactions
+echo " OK"
+
+echo -n "Requesting exchange's outgoing transaction list:"
+check_delta "outgoing?delta=-100" "$SEQ"
+echo " OK"
+
+echo -n "Check balance:"
+check_balance_eth 999622000 378000
+echo " OK"
+
+echo "----- Recover DB -----"
+
+echo "Reset database"
+reset_db
+mine_eth 1 # Trigger worker
+sleep 10
+
+echo -n "Requesting exchange incoming transaction list:"
+check_delta "incoming?delta=-100" "$SEQ" "0.000"
+echo " OK"
+
+echo -n "Requesting exchange outgoing transaction list:"
+check_delta "outgoing?delta=-100" "$SEQ"
+echo " OK"
+
+echo -n "Balance have not changed:"
+check_balance_eth 999622000 378000
+echo " OK"
+
+echo  "----- Handle bounce -----"
+
+echo -n "Bounce:"
+$WIRE_UTILS send $CLIENT $WIRE 0.000 `$SEQ`
+sleep 1
+next_eth
+echo " OK"
+
+echo -n "Check balance:"
+check_balance_eth 999601000 399000
+echo " OK"
+
+echo "----- Recover DB -----"
+
+echo "Reset database"
+reset_db
+mine_eth 1 # Trigger worker
+sleep 10
+
+echo -n "Requesting exchange incoming transaction list:"
+check_delta "incoming?delta=-100" "$SEQ" "0.000"
+echo " OK"
+
+echo -n "Requesting exchange outgoing transaction list:"
+check_delta "outgoing?delta=-100" "$SEQ"
+echo " OK"
+
+echo -n "Balance have not changed:"
+check_balance_eth 999601000 399000
+echo " OK"
+
+echo "All tests passed!"
\ No newline at end of file
diff --git a/test/eth/wire.sh b/test/eth/wire.sh
index ec01a86..686a959 100644
--- a/test/eth/wire.sh
+++ b/test/eth/wire.sh
@@ -26,7 +26,7 @@ SEQ="seq 10 99"
 echo  "----- Receive -----"
 
 echo -n "Making wire transfer to exchange:"
-eth-wire-utils -d $WIRE_DIR deposit $CLIENT $WIRE 0.000 `$SEQ`
+$WIRE_UTILS deposit $CLIENT $WIRE 0.000 `$SEQ`
 next_eth # Trigger eth-wire
 check_balance_eth 995095000 4905000
 echo " OK"
@@ -56,7 +56,7 @@ echo " OK"
 echo  "----- Bounce -----"
 
 echo -n "Bounce:"
-eth-wire-utils -d $WIRE_DIR send $CLIENT $WIRE 0.000 `seq 10 40`
+$WIRE_UTILS send $CLIENT $WIRE 0.000 `seq 10 40`
 sleep 1
 next_eth
 check_balance_eth 995554500 4445500
diff --git a/test/gateway/api.sh b/test/gateway/api.sh
index 97ab224..3656d79 100644
--- a/test/gateway/api.sh
+++ b/test/gateway/api.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-## Test wire_gateway conformance to documentation and its security
+## Test wire-gateway conformance to documentation and its security
 
 set -eu
 
diff --git a/wire-gateway/README.md b/wire-gateway/README.md
index c8b0906..30aa789 100644
--- a/wire-gateway/README.md
+++ b/wire-gateway/README.md
@@ -9,7 +9,7 @@ Rust server for [Taler Wire Gateway HTTP 
API](https://docs.taler.net/core/api-wi
 ## Configuration
 
 ``` ini
-# taler.conf - wire_gateway config
+# taler.conf - wire-gateway config
 [depolymerizer-___]
 PORT          = 8080
 UNIXPATH      =

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]