gnunet-svn
[Top][All Lists]
Advanced

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

[taler-depolymerization] branch master updated (67c6821 -> ce00d1f)


From: gnunet
Subject: [taler-depolymerization] branch master updated (67c6821 -> ce00d1f)
Date: Tue, 31 Jan 2023 15:51:19 +0100

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

antoine pushed a change to branch master
in repository depolymerization.

    from 67c6821  Update dependencies
     new 88b8c4d  Run integration tests in parallel
     new ce00d1f  Update node versions

The 2 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                     |  68 +++++----
 README.md                      |   6 +-
 instrumentation/Cargo.toml     |   3 +
 instrumentation/src/btc.rs     | 286 +++++++++++++++++------------------
 instrumentation/src/eth.rs     | 255 ++++++++++++++++---------------
 instrumentation/src/gateway.rs |  33 ++--
 instrumentation/src/main.rs    | 120 +++++++++++----
 instrumentation/src/utils.rs   | 332 +++++++++++++++++++++++++----------------
 makefile                       |   2 +-
 script/prepare.sh              |   8 +-
 10 files changed, 622 insertions(+), 491 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 6ee38be..816e6e2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -54,9 +54,9 @@ checksum = 
"4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
 
 [[package]]
 name = "async-trait"
-version = "0.1.63"
+version = "0.1.64"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1"
+checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -212,9 +212,9 @@ checksum = 
"37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
 
 [[package]]
 name = "cc"
-version = "1.0.78"
+version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
 
 [[package]]
 name = "cfg-if"
@@ -513,9 +513,9 @@ dependencies = [
 
 [[package]]
 name = "cxx"
-version = "1.0.87"
+version = "1.0.88"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e"
+checksum = "322296e2f2e5af4270b54df9e85a02ff037e271af20ba3e7fe1575515dc840b8"
 dependencies = [
  "cc",
  "cxxbridge-flags",
@@ -525,9 +525,9 @@ dependencies = [
 
 [[package]]
 name = "cxx-build"
-version = "1.0.87"
+version = "1.0.88"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200"
+checksum = "017a1385b05d631e7875b1f151c9f012d37b53491e2a87f65bff5c262b2111d8"
 dependencies = [
  "cc",
  "codespan-reporting",
@@ -540,15 +540,15 @@ dependencies = [
 
 [[package]]
 name = "cxxbridge-flags"
-version = "1.0.87"
+version = "1.0.88"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea"
+checksum = "c26bbb078acf09bc1ecda02d4223f03bdd28bd4874edcb0379138efc499ce971"
 
 [[package]]
 name = "cxxbridge-macro"
-version = "1.0.87"
+version = "1.0.88"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e"
+checksum = "357f40d1f06a24b60ae1fe122542c1fb05d28d32acb2aed064e84bc2ad1e252e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -605,9 +605,9 @@ dependencies = [
 
 [[package]]
 name = "deadpool-postgres"
-version = "0.10.4"
+version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "051c50d234dab03bd29a537859bb7f50cca718c90b05e5c5746b9ae2a73f7278"
+checksum = "836a24a9d49deefe610b8b60c767a7412e9a931d79a89415cd2d2d71630ca8d7"
 dependencies = [
  "deadpool",
  "log",
@@ -643,9 +643,9 @@ checksum = 
"0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
 
 [[package]]
 name = "either"
-version = "1.8.0"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
 
 [[package]]
 name = "env_logger"
@@ -784,9 +784,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
+checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -794,15 +794,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
+checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
+checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -811,21 +811,21 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
+checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
 
 [[package]]
 name = "futures-task"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
+checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
 
 [[package]]
 name = "futures-util"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
+checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
 dependencies = [
  "futures-core",
  "futures-macro",
@@ -1079,8 +1079,10 @@ dependencies = [
  "hex",
  "libdeflater",
  "owo-colors",
+ "rust-ini",
  "signal-child",
  "tempfile",
+ "thread-local-panic-hook",
  "ureq",
 ]
 
@@ -2008,6 +2010,12 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "thread-local-panic-hook"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "e70399498abd3ec85f99a2f2d765c8638588e20361678af93a9f47de96719743"
+
 [[package]]
 name = "time"
 version = "0.3.17"
@@ -2071,9 +2079,9 @@ checksum = 
"cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 [[package]]
 name = "tokio"
-version = "1.24.2"
+version = "1.25.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
+checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
 dependencies = [
  "autocfg",
  "bytes",
diff --git a/README.md b/README.md
index 572608b..0885a8e 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@
 
 ## Install from source
 
-Cargo version 1.16.1 or above is required. You can get cargo from your
+Cargo version 1.66.1 or above is required. You can get cargo from your
 distribution package manager or from [rustup.rs](https://rustup.rs/).
 
 ```
@@ -37,11 +37,11 @@ Depolymerizer require:
 
 #### Bitcoin
 
-[Bitcoind](https://bitcoincore.org/) version 23.0 is expected
+[Bitcoind](https://bitcoincore.org/) version 24.0 is expected
 
 #### Ethereum
 
-[Geth](https://geth.ethereum.org/) version 1.10.20 is expected
+[Geth](https://geth.ethereum.org/) version 1.10.26 is expected
 
 ### Initialization
 
diff --git a/instrumentation/Cargo.toml b/instrumentation/Cargo.toml
index 2887084..6e6e70c 100644
--- a/instrumentation/Cargo.toml
+++ b/instrumentation/Cargo.toml
@@ -31,6 +31,9 @@ owo-colors = "3.5.0"
 color-backtrace = "0.5.1"
 # Send signal to child processes
 signal-child = "1.0.5"
+# Edit toml files
+rust-ini = "0.18.0"
+thread-local-panic-hook = "0.1.0"
 
 
 [build-dependencies]
diff --git a/instrumentation/src/btc.rs b/instrumentation/src/btc.rs
index cfe113a..b9b9fc6 100644
--- a/instrumentation/src/btc.rs
+++ b/instrumentation/src/btc.rs
@@ -31,10 +31,11 @@ use btc_wire::{
     WireState,
 };
 use common::{currency::CurrencyBtc, metadata::OutMetadata, postgres::NoTls, 
rand_slice};
+use tempfile::TempDir;
 
 use crate::utils::{
     check_incoming, check_outgoing, cmd_redirect, cmd_redirect_ok, print_now, 
retry, retry_opt,
-    transfer, ChildGuard, CommonCtx, Dirs,
+    transfer, unused_port, ChildGuard, TalerCtx, TestCtx,
 };
 
 pub const CLIENT: &str = "client";
@@ -228,48 +229,42 @@ pub struct BtcCtx {
     reserve_addr: Address,
     state: WireState,
     conf: u16,
-    common: CommonCtx,
+    ctx: TalerCtx,
+    node2_addr: String,
 }
 
 impl Deref for BtcCtx {
-    type Target = CommonCtx;
+    type Target = TalerCtx;
 
     fn deref(&self) -> &Self::Target {
-        &self.common
+        &self.ctx
     }
 }
 
 impl DerefMut for BtcCtx {
     fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.common
+        &mut self.ctx
     }
 }
 
 impl BtcCtx {
-    pub fn setup(config: &str, stressed: bool) -> Self {
-        Self::_setup(config, stressed)
-    }
-
-    pub fn config(config: &str) {
-        std::fs::create_dir_all("log").unwrap();
-        for file in std::fs::read_dir("log").unwrap() {
-            std::fs::write(file.unwrap().path(), "").unwrap();
-        }
+    pub fn config(test_name: &str, config: &str) {
         // Generate temporary dirs
-        let dirs = Dirs::generate();
+        let ctx = TestCtx::new(test_name);
         // Bitcoin config
         let config = PathBuf::from_str("instrumentation/conf")
             .unwrap()
             .join(config);
-        std::fs::copy(config, dirs.wire_dir.join("bitcoin.conf")).unwrap();
+        let wire_dir = TempDir::new().unwrap();
+        let wire_dir = wire_dir.path();
+        std::fs::copy(config, wire_dir.join("bitcoin.conf")).unwrap();
         // Load config
-        let config =
-            BitcoinConfig::load(dirs.wire_dir.join("bitcoin.conf"), 
CurrencyBtc::Dev).unwrap();
+        let config = BitcoinConfig::load(wire_dir.join("bitcoin.conf"), 
CurrencyBtc::Dev).unwrap();
         // Start bitcoin nodes
         let _btc_node = cmd_redirect(
             "bitcoind",
-            &[&format!("-datadir={}", dirs.wire_dir.to_string_lossy())],
-            "log/node.log",
+            &[&format!("-datadir={}", wire_dir.to_string_lossy())],
+            &ctx.log("bitcoind"),
         );
         // Connect
         retry(|| {
@@ -280,52 +275,65 @@ impl BtcCtx {
         })
     }
 
-    fn _setup(taler_config: &str, stressed: bool) -> Self {
-        let dirs = CommonCtx::setup_dirs(taler_config);
-        // Prepare config
-        let (btc_config2, state) = {
-            // Bitcoin config
-            let config = PathBuf::from_str("instrumentation/conf")
-                .unwrap()
-                .join("bitcoin.conf");
-            std::fs::copy(config, dirs.wire_dir.join("bitcoin.conf")).unwrap();
-            std::fs::copy(
-                "instrumentation/conf/bitcoin2.conf",
-                dirs.wire_dir2.join("bitcoin.conf"),
-            )
-            .unwrap();
-            // Load config
-            let state = WireState::load_taler_config(Some(&dirs.conf));
-            (
-                BitcoinConfig::load(dirs.wire_dir2.join("bitcoin.conf"), 
state.currency).unwrap(),
-                state,
-            )
-        };
+    fn patch_config(from: &str, to: PathBuf, port: u16, rpc_port: u16) {
+        let mut config = ini::Ini::load_from_file(from).unwrap();
+        config
+            .with_section(Some("regtest"))
+            .set("port", port.to_string())
+            .set("rpcport", rpc_port.to_string());
+        config.write_to_file(to).unwrap();
+    }
+
+    pub fn setup(ctx: &TestCtx, taler_config: &str, stressed: bool) -> Self {
+        let mut ctx = TalerCtx::new(ctx, "btc-wire", taler_config, stressed);
+        // Choose unused port
+        let btc_port = unused_port();
+        let btc_rpc_port = unused_port();
+        let btc2_port = unused_port();
+        let btc2_rpc_port = unused_port();
+        // Bitcoin config
+        Self::patch_config(
+            "instrumentation/conf/bitcoin.conf",
+            ctx.wire_dir.join("bitcoin.conf"),
+            btc_port,
+            btc_rpc_port,
+        );
+        Self::patch_config(
+            "instrumentation/conf/bitcoin2.conf",
+            ctx.wire2_dir.join("bitcoin.conf"),
+            btc2_port,
+            btc2_rpc_port,
+        );
+        // Load config
+        let state = WireState::load_taler_config(Some(&ctx.conf));
+        let btc_config2 =
+            BitcoinConfig::load(ctx.wire2_dir.join("bitcoin.conf"), 
state.currency).unwrap();
         // Start bitcoin nodes
         let btc_node = cmd_redirect(
             "bitcoind",
-            &[&format!("-datadir={}", dirs.wire_dir.to_string_lossy())],
-            "log/node.log",
+            &[&format!("-datadir={}", ctx.wire_dir.to_string_lossy())],
+            &ctx.log("bitcoind"),
         );
-        let btc_node2 = cmd_redirect(
+        let _btc_node2 = cmd_redirect(
             "bitcoind",
-            &[&format!("-datadir={}", dirs.wire_dir2.to_string_lossy())],
-            "log/node2.log",
+            &[&format!("-datadir={}", ctx.wire2_dir.to_string_lossy())],
+            &ctx.log("bitcoind2"),
         );
 
-        let common = CommonCtx::setup(dirs, "btc-wire", stressed, |dirs| {
-            // Generate wallet
-            cmd_redirect_ok(
-                "btc-wire",
-                &["-c", dirs.conf.to_str().unwrap(), "initwallet"],
-                "log/cmd.log",
-                "wire initwallet",
-            );
-        });
+        ctx.init_db();
+        // Generate wallet
+        cmd_redirect_ok(
+            "btc-wire",
+            &["-c", ctx.conf.to_str().unwrap(), "initwallet"],
+            &ctx.log("cmd"),
+            "wire initwallet",
+        );
+        ctx.run();
 
         // Setup wallets
         let mut common_rpc = retry_opt(|| Rpc::common(&state.btc_config).ok());
-        common_rpc.add_node("127.0.0.1:8346").unwrap();
+        let node2_addr = format!("127.0.0.1:{btc2_port}");
+        common_rpc.add_node(&node2_addr).unwrap();
 
         for name in ["client", "reserve"] {
             common_rpc.create_wallet(name, "").unwrap();
@@ -346,7 +354,7 @@ impl BtcCtx {
         common_rpc.mine(1, &reserve_addr).unwrap();
 
         Self {
-            common,
+            ctx,
             btc_node,
             common_rpc,
             wire_rpc,
@@ -357,14 +365,15 @@ impl BtcCtx {
             reserve_addr,
             conf: state.confirmation as u16,
             state,
-            _btc_node2: btc_node2,
+            _btc_node2,
             common_rpc2,
+            node2_addr,
         }
     }
 
     pub fn reset_db(&mut self) {
         let hash: BlockHash = self.common_rpc.get_genesis().unwrap();
-        let mut db = 
self.common.taler_conf.db_config().connect(NoTls).unwrap();
+        let mut db = self.ctx.taler_conf.db_config().connect(NoTls).unwrap();
         let mut tx = db.transaction().unwrap();
         // Clear transaction tables and reset state
         tx.batch_execute("DELETE FROM tx_in;DELETE FROM tx_out;DELETE FROM 
bounce;")
@@ -392,12 +401,12 @@ impl BtcCtx {
     }
 
     pub fn cluster_deco(&mut self) {
-        self.common_rpc.disconnect_node("127.0.0.1:8346").unwrap();
+        self.common_rpc.disconnect_node(&self.node2_addr).unwrap();
     }
 
     pub fn cluster_fork(&mut self, length: u16) {
         self.common_rpc2.mine(length, &self.reserve_addr).unwrap();
-        self.common_rpc.add_node("127.0.0.1:8346").unwrap();
+        self.common_rpc.add_node(&self.node2_addr).unwrap();
     }
 
     pub fn restart_node(&mut self, additional_args: &[&str]) {
@@ -406,12 +415,12 @@ impl BtcCtx {
     }
 
     pub fn resume_node(&mut self, additional_args: &[&str]) {
-        let datadir = format!("-datadir={}", 
self.common.dirs.wire_dir.to_string_lossy());
+        let datadir = format!("-datadir={}", 
self.ctx.wire_dir.to_string_lossy());
         let mut args = vec![datadir.as_str()];
         args.extend_from_slice(additional_args);
-        self.btc_node = cmd_redirect("bitcoind", &args, "log/node.log");
+        self.btc_node = cmd_redirect("bitcoind", &args, 
&self.ctx.log("bitcoind"));
         self.common_rpc = retry_opt(|| 
Rpc::common(&self.state.btc_config).ok());
-        self.common_rpc.add_node("127.0.0.1:8346").unwrap();
+        self.common_rpc.add_node(&self.node2_addr).unwrap();
         for name in ["client", "reserve", "wire"] {
             self.common_rpc.load_wallet(name).ok();
         }
@@ -431,7 +440,7 @@ impl BtcCtx {
 
     pub fn debit(&mut self, amount: Amount, metadata: [u8; 32]) {
         transfer(
-            &self.common.gateway_url,
+            &self.ctx.gateway_url,
             &metadata,
             &self.state.base_url,
             btc_payto_url(&self.client_addr),
@@ -524,7 +533,7 @@ impl BtcCtx {
                 )
             })
             .collect();
-        self.common.expect_credits(&txs)
+        self.ctx.expect_credits(&txs)
     }
 
     pub fn expect_debits(&self, txs: &[([u8; 32], Amount)]) {
@@ -537,30 +546,16 @@ impl BtcCtx {
                 )
             })
             .collect();
-        self.common.expect_debits(&self.state.base_url, &txs)
+        self.ctx.expect_debits(&self.state.base_url, &txs)
     }
 }
 
-pub const TESTS: &[(fn(), &str)] = &[
-    (wire, "btc_wire"),
-    (lifetime, "btc_lifetime"),
-    (reconnect, "btc_reconnect"),
-    (stress, "btc_stress"),
-    (conflict, "btc_conflict"),
-    (reorg, "btc_reorg"),
-    (hell, "btc_hell"),
-    (analysis, "btc_analysis"),
-    (bumpfee, "btc_bumpfee"),
-    (maxfee, "btc_maxfee"),
-    (config, "btc_config"),
-];
-
 /// Test btc-wire correctly receive and send transactions on the blockchain
-fn wire() {
-    println!("Setup");
-    let mut ctx = BtcCtx::setup("taler_btc.conf", false);
+pub fn wire(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false);
 
-    println!("Credit");
+    ctx.step("Credit");
     {
         // Send transactions
         let mut balance = ctx.wire_balance();
@@ -578,7 +573,7 @@ fn wire() {
         ctx.expect_wire_balance(balance, true);
     };
 
-    println!("Debit");
+    ctx.step("Debit");
     {
         let mut balance = ctx.client_balance();
         let mut txs = Vec::new();
@@ -594,7 +589,7 @@ fn wire() {
         ctx.expect_client_balance(balance, true);
     }
 
-    println!("Bounce");
+    ctx.step("Bounce");
     {
         ctx.reset_wallet();
         // Send bad transactions
@@ -609,10 +604,10 @@ fn wire() {
 }
 
 /// Check btc-wire and wire-gateway correctly stop when a lifetime limit is 
configured
-fn lifetime() {
-    println!("Setup");
-    let mut ctx = BtcCtx::setup("taler_btc_lifetime.conf", false);
-    println!("Check lifetime");
+pub fn lifetime(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = BtcCtx::setup(&ctx, "taler_btc_lifetime.conf", false);
+    ctx.step("Check lifetime");
     // Start up
     retry(|| ctx.wire_running() && ctx.gateway_running());
     // Consume lifetime
@@ -629,14 +624,14 @@ fn lifetime() {
 }
 
 /// Check the capacity of wire-gateway and btc-wire to recover from database 
and node loss
-fn reconnect() {
-    println!("Setup");
-    let mut ctx = BtcCtx::setup("taler_btc.conf", false);
+pub fn reconnect(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false);
 
     let mut credits = Vec::new();
     let mut debits = Vec::new();
 
-    println!("With DB");
+    ctx.step("With DB");
     {
         let metadata = rand_slice();
         let amount = Amount::from_sat(42000);
@@ -647,7 +642,7 @@ fn reconnect() {
         ctx.expect_credits(&credits);
     };
 
-    println!("Without DB");
+    ctx.step("Without DB");
     {
         ctx.stop_db();
         ctx.malformed_credit(&Amount::from_sat(24000));
@@ -659,7 +654,7 @@ fn reconnect() {
         ctx.expect_error();
     }
 
-    println!("Reconnect DB");
+    ctx.step("Reconnect DB");
     {
         ctx.resume_db();
         ctx.resume_node(&[]);
@@ -676,7 +671,7 @@ fn reconnect() {
         ctx.expect_credits(&credits);
     }
 
-    println!("Recover DB");
+    ctx.step("Recover DB");
     {
         let balance = ctx.wire_balance();
         ctx.reset_db();
@@ -688,14 +683,14 @@ fn reconnect() {
 }
 
 /// Test btc-wire ability to recover from errors in correctness critical paths 
and prevent concurrent sending
-fn stress() {
-    println!("Setup");
-    let mut ctx = BtcCtx::setup("taler_btc.conf", true);
+pub fn stress(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", true);
 
     let mut credits = Vec::new();
     let mut debits = Vec::new();
 
-    println!("Credit");
+    ctx.step("Credit");
     {
         let mut balance = ctx.wire_balance();
         for n in 10..30 {
@@ -711,7 +706,7 @@ fn stress() {
         ctx.expect_wire_balance(balance, true);
     };
 
-    println!("Debit");
+    ctx.step("Debit");
     {
         let mut balance = ctx.client_balance();
         for n in 10..30 {
@@ -726,7 +721,7 @@ fn stress() {
         ctx.expect_client_balance(balance, true);
     }
 
-    println!("Bounce");
+    ctx.step("Bounce");
     {
         ctx.reset_wallet();
         let mut balance = ctx.wire_balance();
@@ -738,7 +733,7 @@ fn stress() {
         ctx.expect_wire_balance(balance, true);
     }
 
-    println!("Recover DB");
+    ctx.step("Recover DB");
     {
         let balance = ctx.wire_balance();
         ctx.reset_db();
@@ -750,11 +745,11 @@ fn stress() {
 }
 
 /// Test btc-wire ability to handle conflicting outgoing transactions
-fn conflict() {
-    println!("Setup");
-    let mut ctx = BtcCtx::setup("taler_btc.conf", false);
+pub fn conflict(tctx: TestCtx) {
+    tctx.step("Setup");
+    let mut ctx = BtcCtx::setup(&tctx, "taler_btc.conf", false);
 
-    println!("Conflict send");
+    ctx.step("Conflict send");
     {
         // Perform credit
         let amount = Amount::from_sat(4200000);
@@ -785,13 +780,13 @@ fn conflict() {
         retry(|| ctx.wire_balance() < wire);
     }
 
-    println!("Setup");
+    ctx.step("Setup");
     drop(ctx);
-    let mut ctx = BtcCtx::setup("taler_btc.conf", false);
+    let mut ctx = BtcCtx::setup(&tctx, "taler_btc.conf", false);
     ctx.credit(Amount::from_sat(3000000), rand_slice());
     ctx.next_block();
 
-    println!("Conflict bounce");
+    ctx.step("Conflict bounce");
     {
         // Perform bounce
         let wire = ctx.wire_balance();
@@ -820,11 +815,11 @@ fn conflict() {
 }
 
 /// Test btc-wire correctness when a blockchain reorganization occurs
-fn reorg() {
-    println!("Setup");
-    let mut ctx = BtcCtx::setup("taler_btc.conf", false);
+pub fn reorg(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false);
 
-    println!("Handle reorg incoming transactions");
+    ctx.step("Handle reorg incoming transactions");
     {
         // Loose second bitcoin node
         ctx.cluster_deco();
@@ -849,7 +844,7 @@ fn reorg() {
         ctx.expect_gateway_up();
     }
 
-    println!("Handle reorg outgoing transactions");
+    ctx.step("Handle reorg outgoing transactions");
     {
         // Loose second bitcoin node
         ctx.cluster_deco();
@@ -876,7 +871,7 @@ fn reorg() {
         ctx.expect_client_balance(after, false);
     }
 
-    println!("Handle reorg bounce");
+    ctx.step("Handle reorg bounce");
     {
         ctx.reset_wallet();
 
@@ -907,11 +902,11 @@ fn reorg() {
 }
 
 /// Test btc-wire correctness when a blockchain reorganization occurs leading 
to past incoming transaction conflict
-fn hell() {
-    fn step(name: &str, action: impl FnOnce(&mut BtcCtx)) {
-        println!("Setup");
-        let mut ctx = BtcCtx::setup("taler_btc.conf", false);
-        println!("{}", name);
+pub fn hell(ctx: TestCtx) {
+    fn step(ctx: &TestCtx, name: &str, action: impl FnOnce(&mut BtcCtx)) {
+        ctx.step("Setup");
+        let mut ctx = BtcCtx::setup(ctx, "taler_btc.conf", false);
+        ctx.step(name);
 
         // Loose second bitcoin node
         ctx.cluster_deco();
@@ -939,14 +934,14 @@ fn hell() {
         ctx.expect_gateway_down();
     }
 
-    step("Handle reorg conflicting incoming credit", |ctx| {
+    step(&ctx, "Handle reorg conflicting incoming credit", |ctx| {
         let amount = Amount::from_sat(420000);
         ctx.credit(amount, rand_slice());
         ctx.next_conf();
         ctx.expect_wire_balance(amount, true);
     });
 
-    step("Handle reorg conflicting incoming bounce", |ctx| {
+    step(&ctx, "Handle reorg conflicting incoming bounce", |ctx| {
         let amount = Amount::from_sat(420000);
         ctx.malformed_credit(&amount);
         ctx.next_conf();
@@ -956,11 +951,11 @@ fn hell() {
 }
 
 /// Test btc-wire ability to learn and protect itself from blockchain behavior
-fn analysis() {
-    println!("Setup");
-    let mut ctx = BtcCtx::setup("taler_btc.conf", false);
+pub fn analysis(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false);
 
-    println!("Learn from reorg");
+    ctx.step("Learn from reorg");
 
     // Loose second bitcoin node
     ctx.cluster_deco();
@@ -1000,9 +995,9 @@ fn analysis() {
 }
 
 /// Test btc-wire ability to handle stuck transaction correctly
-fn bumpfee() {
-    println!("Setup");
-    let mut ctx = BtcCtx::setup("taler_btc_bump.conf", false);
+pub fn bumpfee(tctx: TestCtx) {
+    tctx.step("Setup");
+    let mut ctx = BtcCtx::setup(&tctx, "taler_btc_bump.conf", false);
 
     // Perform credits to allow wire to perform debits latter
     for n in 10..13 {
@@ -1011,7 +1006,7 @@ fn bumpfee() {
     }
     ctx.next_conf();
 
-    println!("Bump fee");
+    ctx.step("Bump fee");
     {
         // Perform debit
         let mut client = ctx.client_balance();
@@ -1028,7 +1023,7 @@ fn bumpfee() {
         ctx.expect_client_balance(client, true);
     }
 
-    println!("Bump fee reorg");
+    ctx.step("Bump fee reorg");
     {
         // Loose second bitcoin node
         ctx.cluster_deco();
@@ -1048,10 +1043,9 @@ fn bumpfee() {
         client += amount;
         ctx.expect_client_balance(client, true);
     }
-
-    println!("Setup");
+    ctx.step("Setup");
     drop(ctx);
-    let mut ctx = BtcCtx::setup("taler_btc_bump.conf", true);
+    let mut ctx = BtcCtx::setup(&tctx, "taler_btc_bump.conf", true);
 
     // Perform credits to allow wire to perform debits latter
     for n in 10..61 {
@@ -1060,7 +1054,7 @@ fn bumpfee() {
     }
     ctx.next_conf();
 
-    println!("Bump fee stress");
+    ctx.step("Bump fee stress");
     {
         // Loose second bitcoin node
         ctx.cluster_deco();
@@ -1085,9 +1079,9 @@ fn bumpfee() {
 }
 
 /// Test btc-wire handle transaction fees exceeding limits
-fn maxfee() {
-    println!("Setup");
-    let mut ctx = BtcCtx::setup("taler_btc.conf", false);
+pub fn maxfee(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false);
 
     // Perform credits to allow wire to perform debits latter
     for n in 10..31 {
@@ -1100,7 +1094,7 @@ fn maxfee() {
     let wire = ctx.wire_balance();
     let mut total_amount = Amount::ZERO;
 
-    println!("Too high fee");
+    ctx.step("Too high fee");
     {
         // Change fee config
         ctx.restart_node(&["-maxtxfee=0.0000001", "-minrelaytxfee=0.0000001"]);
@@ -1118,7 +1112,7 @@ fn maxfee() {
         ctx.expect_client_balance(client, true);
     }
 
-    println!("Good feed");
+    ctx.step("Good feed");
     {
         // Restore default config
         ctx.restart_node(&[]);
@@ -1129,10 +1123,10 @@ fn maxfee() {
 }
 
 /// Test btc-wire ability to configure itself from bitcoin configuration
-fn config() {
+pub fn config(ctx: TestCtx) {
     for n in 0..5 {
         let config_name = format!("bitcoin_auth{}.conf", n);
-        println!("Config {}", config_name);
-        BtcCtx::config(&config_name);
+        ctx.step(format!("Config {}", config_name));
+        BtcCtx::config(&format!("config/{config_name}"), &config_name);
     }
 }
diff --git a/instrumentation/src/eth.rs b/instrumentation/src/eth.rs
index 265c43c..ab44106 100644
--- a/instrumentation/src/eth.rs
+++ b/instrumentation/src/eth.rs
@@ -15,7 +15,6 @@
 */
 
 use std::{
-    io::Write,
     ops::{Deref, DerefMut},
     path::Path,
     thread::sleep,
@@ -32,7 +31,7 @@ use ethereum_types::{H160, H256, U256};
 
 use crate::utils::{
     check_incoming, check_outgoing, cmd_out, cmd_redirect, cmd_redirect_ok, 
print_now, retry,
-    retry_opt, transfer, ChildGuard, CommonCtx,
+    retry_opt, transfer, unused_port, ChildGuard, TalerCtx, TestCtx,
 };
 
 fn wait_for_pending(rpc: &mut Rpc) {
@@ -208,43 +207,43 @@ struct EthCtx {
     reserve_addr: H160,
     state: WireState,
     conf: u16,
-    common: CommonCtx,
+    ctx: TalerCtx,
     passwd: String,
 }
 
 impl Deref for EthCtx {
-    type Target = CommonCtx;
+    type Target = TalerCtx;
 
     fn deref(&self) -> &Self::Target {
-        &self.common
+        &self.ctx
     }
 }
 
 impl DerefMut for EthCtx {
     fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.common
+        &mut self.ctx
     }
 }
 
 impl EthCtx {
-    pub fn setup(config: &str, stressed: bool) -> Self {
-        let dirs = CommonCtx::setup_dirs(config);
+    pub fn setup(ctx: &TestCtx, config: &str, stressed: bool) -> Self {
+        let mut ctx = TalerCtx::new(ctx, "eth-wire", config, stressed);
         // Init chain
         let passwd = std::env::var("PASSWORD").unwrap();
-        let pswd_path = dirs.dir.path().join("pswd");
+        let pswd_path = ctx.dir.path().join("pswd");
         std::fs::write(&pswd_path, passwd.as_bytes()).unwrap();
         for _ in ["reserve", "client"] {
             cmd_redirect_ok(
                 "geth",
                 &[
                     "--datadir",
-                    dirs.wire_dir.to_str().unwrap(),
+                    ctx.wire_dir.to_str().unwrap(),
                     "account",
                     "new",
                     "--password",
                     pswd_path.to_str().unwrap(),
                 ],
-                "log/node.log",
+                &ctx.log("geth"),
                 "create account",
             )
         }
@@ -252,20 +251,20 @@ impl EthCtx {
             "geth",
             &[
                 "--datadir",
-                dirs.wire_dir2.to_str().unwrap(),
+                ctx.wire2_dir.to_str().unwrap(),
                 "account",
                 "new",
                 "--password",
                 pswd_path.to_str().unwrap(),
             ],
-            "log/node2.log",
+            &ctx.log("geth2"),
             "create account",
         );
         let list = cmd_out(
             "geth",
             &[
                 "--datadir",
-                dirs.wire_dir.to_str().unwrap(),
+                ctx.wire_dir.to_str().unwrap(),
                 "account",
                 "list",
             ],
@@ -296,58 +295,63 @@ impl EthCtx {
           }}",
             addr
         );
-        std::fs::write(dirs.wire_dir.join("genesis.json"), 
genesis.as_bytes()).unwrap();
+        std::fs::write(ctx.wire_dir.join("genesis.json"), 
genesis.as_bytes()).unwrap();
 
         cmd_redirect_ok(
             "geth",
             &[
                 "--datadir",
-                dirs.wire_dir.to_str().unwrap(),
+                ctx.wire_dir.to_str().unwrap(),
                 "init",
-                dirs.wire_dir.join("genesis.json").to_str().unwrap(),
+                ctx.wire_dir.join("genesis.json").to_str().unwrap(),
             ],
-            "log/node.log",
+            &ctx.log("geth"),
             "init chain",
         );
         cmd_redirect_ok(
             "geth",
             &[
                 "--datadir",
-                dirs.wire_dir2.to_str().unwrap(),
+                ctx.wire2_dir.to_str().unwrap(),
                 "init",
-                dirs.wire_dir.join("genesis.json").to_str().unwrap(),
+                ctx.wire_dir.join("genesis.json").to_str().unwrap(),
             ],
-            "log/node2.log",
+            &ctx.log("geth2"),
             "init chain2",
         );
-
         let node = cmd_redirect(
             "geth",
             &[
                 "--datadir",
-                dirs.wire_dir.to_str().unwrap(),
+                ctx.wire_dir.to_str().unwrap(),
                 "--miner.gasprice",
                 "10",
+                "--authrpc.port",
+                &unused_port().to_string(),
+                "--port",
+                &unused_port().to_string(),
             ],
-            "log/node.log",
+            &ctx.log("geth"),
         );
-        let mut rpc = retry_opt(|| Rpc::new(&dirs.wire_dir).ok());
+        let mut rpc = retry_opt(|| Rpc::new(&ctx.wire_dir).ok());
+        ctx.init_db();
 
-        let common = CommonCtx::setup(dirs, "eth-wire", stressed, |dirs| {
-            // Generate wallet
-            let out = cmd_out(
-                "eth-wire",
-                &["-c", dirs.conf.to_str().unwrap(), "initwallet"],
-            );
-            let payto = format!("\n{}", out.lines().nth(6).unwrap());
-            std::fs::OpenOptions::new()
-                .append(true)
-                .open(&dirs.conf)
-                .unwrap()
-                .write_all(payto.as_bytes())
-                .unwrap();
-        });
-        let state = WireState::load_taler_config(Some(&common.dirs.conf));
+        // Generate wallet
+        let out = cmd_out(
+            "eth-wire",
+            &["-c", ctx.conf.to_str().unwrap(), "initwallet"],
+        );
+
+        let mut config = ini::Ini::load_from_file(&ctx.conf).unwrap();
+        config.with_section(Some("depolymerizer-ethereum")).set(
+            "PAYTO",
+            out.lines().nth(6).unwrap().split(" = ").last().unwrap(),
+        );
+        config.write_to_file(&ctx.conf).unwrap();
+
+        ctx.run();
+
+        let state = WireState::load_taler_config(Some(&ctx.conf));
         let accounts = rpc.list_accounts().unwrap();
         let reserve_addr = accounts[0];
         let client_addr = accounts[1];
@@ -364,14 +368,14 @@ impl EthCtx {
             wire_addr,
             conf: state.confirmation as u16,
             state,
-            common,
+            ctx,
             passwd,
         }
     }
 
     pub fn reset_db(&mut self) {
         let block = self.rpc.earliest_block().unwrap();
-        let mut db = 
self.common.taler_conf.db_config().connect(NoTls).unwrap();
+        let mut db = self.ctx.taler_conf.db_config().connect(NoTls).unwrap();
         let mut tx = db.transaction().unwrap();
         // Clear transaction tables and reset state
         tx.batch_execute("DELETE FROM tx_in;DELETE FROM tx_out;DELETE FROM 
bounce;")
@@ -414,18 +418,18 @@ impl EthCtx {
     }
 
     pub fn cluster_deco(&mut self) {
-        let path = self.dirs.dir.path().join("chain");
+        let path = self.ctx.dir.path().join("chain");
         let path = path.to_str().unwrap();
         Self::export(&mut self.rpc, path);
         cmd_redirect_ok(
             "geth",
             &[
                 "--datadir",
-                self.dirs.wire_dir2.to_str().unwrap(),
+                self.ctx.wire2_dir.to_str().unwrap(),
                 "import",
                 path,
             ],
-            "log/node2.log",
+            &self.ctx.log("geth2"),
             "import chain",
         );
     }
@@ -435,19 +439,19 @@ impl EthCtx {
             "geth",
             &[
                 "--datadir",
-                self.dirs.wire_dir2.to_str().unwrap(),
-                "--authrpc.port",
-                "8552",
-                "--port",
-                "30305",
+                self.ctx.wire2_dir.to_str().unwrap(),
                 "--miner.gasprice",
                 "10",
+                "--authrpc.port",
+                &unused_port().to_string(),
+                "--port",
+                &unused_port().to_string(),
             ],
-            "log/node2.log",
+            &self.ctx.log("geth2"),
         );
-        let mut rpc = retry_opt(|| Rpc::new(&self.dirs.wire_dir2).ok());
+        let mut rpc = retry_opt(|| Rpc::new(&self.ctx.wire2_dir).ok());
         Self::_mine(&mut rpc, &self.reserve_addr, length, &self.passwd);
-        let path = self.dirs.dir.path().join("chain");
+        let path = self.ctx.dir.path().join("chain");
         let path = path.to_str().unwrap();
         Self::export(&mut rpc, path);
         drop(node2);
@@ -456,11 +460,11 @@ impl EthCtx {
             "geth",
             &[
                 "--datadir",
-                self.dirs.wire_dir.to_str().unwrap(),
+                self.ctx.wire_dir.to_str().unwrap(),
                 "import",
                 path,
             ],
-            "log/node.log",
+            &self.ctx.log("geth"),
             "import chain",
         );
         self.resume_node(&[]);
@@ -472,15 +476,22 @@ impl EthCtx {
     }
 
     pub fn resume_node(&mut self, additional_args: &[&str]) {
-        let datadir = format!("-datadir={}", 
self.dirs.wire_dir.to_string_lossy());
+        let datadir = format!("-datadir={}", 
self.ctx.wire_dir.to_string_lossy());
         let mut args = vec![datadir.as_str()];
         args.extend_from_slice(additional_args);
         self.node = cmd_redirect(
             "geth",
-            &["--datadir", self.dirs.wire_dir.to_str().unwrap()],
-            "log/node.log",
+            &[
+                "--datadir",
+                self.ctx.wire_dir.to_str().unwrap(),
+                "--authrpc.port",
+                &unused_port().to_string(),
+                "--port",
+                &unused_port().to_string(),
+            ],
+            &self.ctx.log("geth"),
         );
-        self.rpc = retry_opt(|| Rpc::new(&self.dirs.wire_dir).ok());
+        self.rpc = retry_opt(|| Rpc::new(&self.ctx.wire_dir).ok());
         for addr in [&self.wire_addr, &self.client_addr, &self.reserve_addr] {
             self.rpc.unlock_account(addr, &self.passwd).unwrap();
         }
@@ -500,7 +511,7 @@ impl EthCtx {
 
     pub fn debit(&mut self, amount: U256, metadata: [u8; 32]) {
         transfer(
-            &self.common.gateway_url,
+            &self.ctx.gateway_url,
             &metadata,
             &self.state.base_url,
             eth_payto_url(&self.client_addr),
@@ -609,7 +620,7 @@ impl EthCtx {
             .iter()
             .map(|(metadata, amount)| (*metadata, eth_to_taler(amount, 
self.state.currency)))
             .collect();
-        self.common.expect_credits(&txs)
+        self.ctx.expect_credits(&txs)
     }
 
     pub fn expect_debits(&self, txs: &[([u8; 32], U256)]) {
@@ -617,28 +628,16 @@ impl EthCtx {
             .iter()
             .map(|(metadata, amount)| (*metadata, eth_to_taler(amount, 
self.state.currency)))
             .collect();
-        self.common.expect_debits(&self.state.base_url, &txs)
+        self.ctx.expect_debits(&self.state.base_url, &txs)
     }
 }
 
-pub const TESTS: &[(fn(), &str)] = &[
-    (wire, "eth_wire"),
-    (lifetime, "eth_lifetime"),
-    (reconnect, "eth_reconnect"),
-    (stress, "eth_stress"),
-    (reorg, "eth_reorg"),
-    (hell, "eth_hell"),
-    (analysis, "eth_analysis"),
-    (bumpfee, "eth_bumpfee"),
-    (maxfee, "eth_maxfee"),
-];
-
 /// Test eth-wire correctly receive and send transactions on the blockchain
-fn wire() {
-    println!("Setup");
-    let mut ctx = EthCtx::setup("taler_eth.conf", false);
+pub fn wire(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = EthCtx::setup(&ctx, "taler_eth.conf", false);
 
-    println!("Credit");
+    ctx.step("Credit");
     {
         // Send transactions
         let mut balance = ctx.wire_balance();
@@ -655,7 +654,7 @@ fn wire() {
         ctx.expect_wire_balance(balance, true);
     };
 
-    println!("Debit");
+    ctx.step("Debit");
     {
         let mut balance = ctx.client_balance();
         let mut txs = Vec::new();
@@ -671,7 +670,7 @@ fn wire() {
         ctx.expect_client_balance(balance, true);
     }
 
-    println!("Bounce");
+    ctx.step("Bounce");
     {
         // Send bad transactions
         let mut balance = ctx.wire_balance();
@@ -685,10 +684,10 @@ fn wire() {
 }
 
 /// Test eth-wire and wire-gateway correctly stop when a lifetime limit is 
configured
-fn lifetime() {
-    println!("Setup");
-    let mut ctx = EthCtx::setup("taler_eth_lifetime.conf", false);
-    println!("Check lifetime");
+pub fn lifetime(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = EthCtx::setup(&ctx, "taler_eth_lifetime.conf", false);
+    ctx.step("Check lifetime");
     // Start up
     retry(|| ctx.wire_running() && ctx.gateway_running());
     // Consume lifetime
@@ -704,14 +703,14 @@ fn lifetime() {
 }
 
 /// Check the capacity of wire-gateway and eth-wire to recover from database 
and node loss
-fn reconnect() {
-    println!("Setup");
-    let mut ctx = EthCtx::setup("taler_eth.conf", false);
+pub fn reconnect(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = EthCtx::setup(&ctx, "taler_eth.conf", false);
 
     let mut credits = Vec::new();
     let mut debits = Vec::new();
 
-    println!("With DB");
+    ctx.step("With DB");
     {
         let metadata = rand_slice();
         let amount = ctx.amount(42000);
@@ -722,7 +721,7 @@ fn reconnect() {
         ctx.expect_credits(&credits);
     };
 
-    println!("Without DB");
+    ctx.step("Without DB");
     {
         ctx.stop_db();
         ctx.malformed_credit(ctx.amount(24000));
@@ -734,7 +733,7 @@ fn reconnect() {
         ctx.expect_error();
     }
 
-    println!("Reconnect DB");
+    ctx.step("Reconnect DB");
     {
         ctx.resume_db();
         ctx.resume_node(&[]);
@@ -751,7 +750,7 @@ fn reconnect() {
         ctx.expect_credits(&credits);
     }
 
-    println!("Recover DB");
+    ctx.step("Recover DB");
     {
         ctx.next_block();
         sleep(Duration::from_secs(3));
@@ -767,14 +766,14 @@ fn reconnect() {
 }
 
 /// Test eth-wire ability to recover from errors in correctness critical paths 
and prevent concurrent sending
-fn stress() {
-    println!("Setup");
-    let mut ctx = EthCtx::setup("taler_eth.conf", true);
+pub fn stress(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = EthCtx::setup(&ctx, "taler_eth.conf", true);
 
     let mut credits = Vec::new();
     let mut debits = Vec::new();
 
-    println!("Credit");
+    ctx.step("Credit");
     {
         let mut balance = ctx.wire_balance();
         for n in 10..30 {
@@ -789,7 +788,7 @@ fn stress() {
         ctx.expect_wire_balance(balance, true);
     };
 
-    println!("Debit");
+    ctx.step("Debit");
     {
         let mut balance = ctx.client_balance();
         for n in 10..30 {
@@ -804,7 +803,7 @@ fn stress() {
         ctx.expect_client_balance(balance, true);
     }
 
-    println!("Bounce");
+    ctx.step("Bounce");
     {
         let mut balance = ctx.wire_balance();
         for n in 10..30 {
@@ -815,7 +814,7 @@ fn stress() {
         ctx.expect_wire_balance(balance, true);
     }
 
-    println!("Recover DB");
+    ctx.step("Recover DB");
     {
         let balance = ctx.wire_balance();
         ctx.reset_db();
@@ -827,11 +826,11 @@ fn stress() {
 }
 
 /// Test eth-wire correctness when a blockchain reorganization occurs
-fn reorg() {
-    println!("Setup");
-    let mut ctx = EthCtx::setup("taler_eth.conf", false);
+pub fn reorg(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = EthCtx::setup(&ctx, "taler_eth.conf", false);
 
-    println!("Handle reorg incoming transactions");
+    ctx.step("Handle reorg incoming transactions");
     {
         // Loose second node
         ctx.cluster_deco();
@@ -856,7 +855,7 @@ fn reorg() {
         ctx.expect_gateway_up();
     }
 
-    println!("Handle reorg outgoing transactions");
+    ctx.step("Handle reorg outgoing transactions");
     {
         // Loose second node
         ctx.cluster_deco();
@@ -883,7 +882,7 @@ fn reorg() {
         ctx.expect_client_balance(after, false);
     }
 
-    println!("Handle reorg bounce");
+    ctx.step("Handle reorg bounce");
     {
         // Loose second node
         ctx.cluster_deco();
@@ -914,11 +913,11 @@ fn reorg() {
 }
 
 /// Test eth-wire correctness when a blockchain reorganization occurs leading 
to past incoming transaction conflict
-fn hell() {
-    fn step(name: &str, action: impl FnOnce(&mut EthCtx)) {
-        println!("Setup");
-        let mut ctx = EthCtx::setup("taler_eth.conf", false);
-        println!("{}", name);
+pub fn hell(ctx: TestCtx) {
+    fn step(ctx: &TestCtx, name: &str, action: impl FnOnce(&mut EthCtx)) {
+        ctx.step("Setup");
+        let mut ctx = EthCtx::setup(ctx, "taler_eth.conf", false);
+        ctx.step(name);
 
         // Loose second node
         ctx.cluster_deco();
@@ -946,14 +945,14 @@ fn hell() {
         ctx.expect_gateway_down();
     }
 
-    step("Handle reorg conflicting incoming credit", |ctx| {
+    step(&ctx, "Handle reorg conflicting incoming credit", |ctx| {
         let amount = ctx.amount(420000);
         ctx.credit(amount, rand_slice());
         ctx.next_conf();
         ctx.expect_wire_balance(amount, true);
     });
 
-    step("Handle reorg conflicting incoming bounce", |ctx| {
+    step(&ctx, "Handle reorg conflicting incoming bounce", |ctx| {
         let amount = ctx.amount(420000);
         ctx.malformed_credit(amount);
         ctx.next_conf();
@@ -962,11 +961,11 @@ fn hell() {
 }
 
 /// Test eth-wire ability to learn and protect itself from blockchain behavior
-fn analysis() {
-    println!("Setup");
-    let mut ctx = EthCtx::setup("taler_eth.conf", false);
+pub fn analysis(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = EthCtx::setup(&ctx, "taler_eth.conf", false);
 
-    println!("Learn from reorg");
+    ctx.step("Learn from reorg");
 
     // Loose second node
     ctx.cluster_deco();
@@ -1005,15 +1004,15 @@ fn analysis() {
 }
 
 /// Test eth-wire ability to handle stuck transaction correctly
-fn bumpfee() {
-    println!("Setup");
-    let mut ctx = EthCtx::setup("taler_eth_bump.conf", false);
+pub fn bumpfee(tctx: TestCtx) {
+    tctx.step("Setup");
+    let mut ctx = EthCtx::setup(&tctx, "taler_eth_bump.conf", false);
 
     // Perform credits to allow wire to perform debits latter
     ctx.credit(ctx.amount(90000000), rand_slice());
     ctx.next_conf();
 
-    println!("Bump fee");
+    ctx.step("Bump fee");
     {
         // Perform debit
         let mut client = ctx.client_balance();
@@ -1030,7 +1029,7 @@ fn bumpfee() {
         ctx.expect_client_balance(client, true);
     }
 
-    println!("Bump fee reorg");
+    ctx.step("Bump fee reorg");
     {
         // Loose second node
         ctx.cluster_deco();
@@ -1051,15 +1050,15 @@ fn bumpfee() {
         ctx.expect_client_balance(client, true);
     }
 
-    println!("Setup");
+    ctx.step("Setup");
     drop(ctx);
-    let mut ctx = EthCtx::setup("taler_eth_bump.conf", true);
+    let mut ctx = EthCtx::setup(&tctx, "taler_eth_bump.conf", true);
 
     // Perform credit to allow wire to perform debits latter
     ctx.credit(ctx.amount(9000000), rand_slice());
     ctx.next_conf();
 
-    println!("Bump fee stress");
+    ctx.step("Bump fee stress");
     {
         // Loose second node
         ctx.cluster_deco();
@@ -1084,9 +1083,9 @@ fn bumpfee() {
 }
 
 /// Test eth-wire handle transaction fees exceeding limits
-fn maxfee() {
-    println!("Setup");
-    let mut ctx = EthCtx::setup("taler_eth.conf", false);
+pub fn maxfee(ctx: TestCtx) {
+    ctx.step("Setup");
+    let mut ctx = EthCtx::setup(&ctx, "taler_eth.conf", false);
 
     // Perform credit to allow wire to perform debits latter
     ctx.credit(ctx.amount(9000000), rand_slice());
@@ -1096,7 +1095,7 @@ fn maxfee() {
     let wire = ctx.wire_balance();
     let mut total_amount = U256::zero();
 
-    println!("Too high fee");
+    ctx.step("Too high fee");
     {
         // Change fee config
         ctx.restart_node(&["--rpc.txfeecap", "0.00001"]);
@@ -1114,7 +1113,7 @@ fn maxfee() {
         ctx.expect_client_balance(client, true);
     }
 
-    println!("Good feed");
+    ctx.step("Good feed");
     {
         // Restore default config
         ctx.restart_node(&[""]);
diff --git a/instrumentation/src/gateway.rs b/instrumentation/src/gateway.rs
index ad58a21..3f24dab 100644
--- a/instrumentation/src/gateway.rs
+++ b/instrumentation/src/gateway.rs
@@ -28,11 +28,9 @@ use ureq::Response;
 
 use crate::{
     btc::BtcCtx,
-    utils::{cmd_out, cmd_redirect_ok, gateway_error},
+    utils::{cmd_out, cmd_redirect_ok, gateway_error, TestCtx},
 };
 
-pub const TESTS: &[(fn(), &str)] = &[(api, "api"), (auth, "auth")];
-
 fn client_transfer(gateway_url: &str, payto_url: &str, amount: &str) -> String 
{
     cmd_out(
         "taler-exchange-wire-gateway-client",
@@ -51,11 +49,11 @@ fn http_code(response: Result<Response, ureq::Error>) -> 
u16 {
 }
 
 /// Test wire-gateway conformance to documentation and its security
-fn api() {
-    println!("Setup");
-    let ctx = BtcCtx::setup("taler_btc.conf", false);
+pub fn api(ctx: TestCtx) {
+    ctx.step("Setup");
+    let ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false);
 
-    println!("Gateway API");
+    ctx.step("Gateway API");
     {
         // Perform debits
         let mut amounts = Vec::new();
@@ -106,7 +104,7 @@ fn api() {
         }
     };
 
-    println!("Endpoint & Method");
+    ctx.step("Endpoint & Method");
     {
         // Unknown endpoint
         gateway_error(&format!("{}test", ctx.gateway_url), 404);
@@ -117,7 +115,7 @@ fn api() {
     let amount = &format!("{}:0.00042", ctx.taler_conf.currency.to_str());
     let payto = btc_payto_url(&ctx.client_addr).to_string();
 
-    println!("Request format");
+    ctx.step("Request format");
     {
         // Bad payto_url
         for url in [
@@ -149,7 +147,7 @@ fn api() {
         }
     }
 
-    println!("Transfer idempotence");
+    ctx.step("Transfer idempotence");
     {
         let mut request = TransferRequest {
             request_uid: Base32::from(rand_slice()),
@@ -175,7 +173,7 @@ fn api() {
         );
     }
 
-    println!("Security");
+    ctx.step("Security");
     {
         let big_hello: String = (0..1000).map(|_| "Hello_world").collect();
         // Huge body
@@ -214,11 +212,12 @@ fn api() {
 }
 
 /// Check btc-wire and wire-gateway correctly stop when a lifetime limit is 
configured
-fn auth() {
-    println!("Setup");
-    let ctx = BtcCtx::setup("taler_btc_auth.conf", false);
+pub fn auth(ctx: TestCtx) {
+    ctx.step("Setup");
+    let ctx = BtcCtx::setup(&ctx, "taler_btc_auth.conf", false);
+
+    ctx.step("Authentication");
 
-    println!("Authentication");
     // No auth
     assert_eq!(
         http_code(ureq::get(&format!("{}history.outgoing", 
ctx.gateway_url)).call()),
@@ -230,7 +229,7 @@ fn auth() {
         "taler-exchange-wire-gateway-client",
         &[
             "--config",
-            ctx.dirs.conf.to_str().unwrap(),
+            ctx.conf.to_str().unwrap(),
             "-s",
             "exchange-accountcredentials-admin",
             "-C",
@@ -238,7 +237,7 @@ fn auth() {
             "-a",
             &format!("{}:0.00042", ctx.taler_conf.currency.to_str()),
         ],
-        "log/client.log",
+        &ctx.log("client"),
         "",
     );
 }
diff --git a/instrumentation/src/main.rs b/instrumentation/src/main.rs
index 14a261f..c33b981 100644
--- a/instrumentation/src/main.rs
+++ b/instrumentation/src/main.rs
@@ -14,11 +14,15 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 
-use std::{path::PathBuf, time::Instant};
+use std::{panic::catch_unwind, path::PathBuf, time::Instant};
 
 use clap::Parser;
+use color_backtrace::termcolor::NoColor;
 use common::{config::TalerConfig, currency::Currency};
 use owo_colors::OwoColorize;
+use thread_local_panic_hook::set_hook;
+
+use crate::utils::TestCtx;
 
 mod btc;
 mod eth;
@@ -43,17 +47,10 @@ enum Cmd {
     /// Perform offline tests on local private blockchain
     Offline {
         /// With tests to run
-        #[clap(value_enum, global = true, default_value = "all")]
-        kind: TestKind,
+        #[clap(global = true, default_value = "")]
+        filters: Vec<String>,
     },
 }
-#[derive(clap::ValueEnum, Clone, Debug, PartialEq, Eq)]
-enum TestKind {
-    All,
-    Gateway,
-    Btc,
-    Eth,
-}
 
 pub fn main() {
     common::log::init();
@@ -71,29 +68,90 @@ pub fn main() {
             }
             println!("Instrumentation test successful");
         }
-        Cmd::Offline { kind } => {
-            let mut tests = Vec::new();
-            if kind == TestKind::All || kind == TestKind::Gateway {
-                tests.extend_from_slice(gateway::TESTS);
-            }
-            if kind == TestKind::All || kind == TestKind::Btc {
-                tests.extend_from_slice(btc::TESTS);
-            }
-            if kind == TestKind::All || kind == TestKind::Eth {
-                tests.extend_from_slice(eth::TESTS);
-            }
+        Cmd::Offline { filters } => {
+            std::fs::remove_dir_all("log").ok();
+            std::fs::create_dir_all("log").unwrap();
+
             let start = Instant::now();
-            for (test, name) in &tests {
-                let start = Instant::now();
-                println!("{}", name.magenta());
-                test();
-                println!(
-                    "{} {}",
-                    "OK".green(),
-                    format_args!("{:?}", start.elapsed()).bright_black()
-                );
+            let tests: Vec<_> = TESTS
+                .iter()
+                .filter(|(_, name)| {
+                    filters.is_empty() || filters.iter().any(|f| 
name.starts_with(f))
+                })
+                .map(|(action, name)| {
+                    let n = name.to_string();
+                    let join = std::thread::spawn(move || {
+                        let start = Instant::now();
+                        let ctx = TestCtx::new(&n);
+                        let tmp = ctx.clone();
+                        set_hook(Box::new(move |info| {
+                            let mut buf = Vec::new();
+                            color_backtrace::BacktracePrinter::new()
+                                .print_panic_info(info, &mut NoColor::new(&mut 
buf))
+                                .ok();
+                            let str = 
String::from_utf8(buf).unwrap_or_default();
+                            tmp.out.lock().unwrap().push_str(&str);
+                        }));
+                        let tmp = ctx.clone();
+                        let result = catch_unwind(|| {
+                            action(tmp);
+                        });
+                        let lock = ctx.out.lock().unwrap();
+                        (result, start.elapsed(), lock.clone())
+                    });
+                    (join, name)
+                })
+                .collect();
+
+            let len = tests.len();
+            for (handle, name) in tests {
+                let (result, time, out) = handle.join().unwrap();
+                match result {
+                    Ok(_) => {
+                        println!(
+                            "{} {} {}",
+                            name.magenta(),
+                            "OK".green(),
+                            format_args!("{}s", time.as_secs()).bright_black()
+                        );
+                    }
+                    Err(_) => {
+                        println!(
+                            "{} {} {}",
+                            name.magenta(),
+                            "ERR".red(),
+                            format_args!("{}s", time.as_secs()).bright_black()
+                        );
+                        println!("{}", out.bright_black());
+                    }
+                }
             }
-            println!("{} tests in {:?}", tests.len(), start.elapsed());
+            println!("{} tests in {}s", len, start.elapsed().as_secs());
         }
     }
 }
+
+pub const TESTS: &[(fn(TestCtx), &str)] = &[
+    (gateway::api, "gateway_api"),
+    (gateway::auth, "gateway_auth"),
+    (btc::wire, "btc_wire"),
+    (btc::lifetime, "btc_lifetime"),
+    (btc::reconnect, "btc_reconnect"),
+    (btc::stress, "btc_stress"),
+    (btc::conflict, "btc_conflict"),
+    (btc::reorg, "btc_reorg"),
+    (btc::hell, "btc_hell"),
+    (btc::analysis, "btc_analysis"),
+    (btc::bumpfee, "btc_bumpfee"),
+    (btc::maxfee, "btc_maxfee"),
+    (btc::config, "btc_config"),
+    (eth::wire, "eth_wire"),
+    (eth::lifetime, "eth_lifetime"),
+    (eth::reconnect, "eth_reconnect"),
+    (eth::stress, "eth_stress"),
+    (eth::reorg, "eth_reorg"),
+    (eth::hell, "eth_hell"),
+    (eth::analysis, "eth_analysis"),
+    (eth::bumpfee, "eth_bumpfee"),
+    (eth::maxfee, "eth_maxfee"),
+];
diff --git a/instrumentation/src/utils.rs b/instrumentation/src/utils.rs
index f35eefc..8377f5a 100644
--- a/instrumentation/src/utils.rs
+++ b/instrumentation/src/utils.rs
@@ -18,9 +18,12 @@ use std::{
     fmt::Display,
     fmt::Write as _,
     io::Write as _,
-    path::{Path, PathBuf},
+    net::{Ipv4Addr, SocketAddrV4, TcpListener, TcpStream},
+    ops::{Deref, DerefMut},
+    path::PathBuf,
     process::{Child, Command, Stdio},
     str::FromStr,
+    sync::{Arc, Mutex},
     thread::sleep,
     time::{Duration, Instant},
 };
@@ -127,35 +130,6 @@ pub fn check_outgoing(base_url: &str, url: &Url, txs: 
&[([u8; 32], Amount)]) ->
             })
         })
 }
-
-pub struct Dirs {
-    pub dir: TempDir,
-    pub wire_dir: PathBuf,
-    pub wire_dir2: PathBuf,
-    pub db_dir: PathBuf,
-    pub conf: PathBuf,
-}
-
-impl Dirs {
-    pub fn generate() -> Self {
-        let dir = TempDir::new().unwrap();
-        let wire_dir = dir.path().join("wire");
-        let wire_dir2 = dir.path().join("wire2");
-        let db_dir = dir.path().join("db");
-        for dir in [&wire_dir, &wire_dir2, &db_dir] {
-            std::fs::create_dir_all(dir).unwrap();
-        }
-        let conf = dir.path().join("taler.conf");
-        Self {
-            wire_dir,
-            wire_dir2,
-            db_dir,
-            conf,
-            dir,
-        }
-    }
-}
-
 pub struct ChildGuard(pub Child);
 
 impl Drop for ChildGuard {
@@ -211,7 +185,7 @@ pub fn retry_opt<T>(mut lambda: impl FnMut() -> Option<T>) 
-> T {
     let start = Instant::now();
     loop {
         let result = lambda();
-        if result.is_none() && start.elapsed() < Duration::from_secs(20) {
+        if result.is_none() && start.elapsed() < Duration::from_secs(60) {
             sleep(Duration::from_millis(500));
         } else {
             return result.unwrap();
@@ -223,84 +197,135 @@ pub fn retry(mut lambda: impl FnMut() -> bool) {
     retry_opt(|| lambda().then(|| ()))
 }
 
-pub struct CommonCtx {
-    pub dirs: Dirs,
-    gateway: ChildGuard,
-    pub gateway_url: String,
-    pub taler_conf: TalerConfig,
-    db: ChildGuard,
-    wire: ChildGuard,
-    _wire2: Option<ChildGuard>,
+#[derive(Clone)]
+pub struct TestCtx {
+    pub log_dir: String,
+    pub out: Arc<Mutex<String>>,
 }
 
-impl CommonCtx {
-    pub fn setup_dirs(config: &str) -> Dirs {
-        // Setup logs
-        {
-            // Create log dir if no exist
-            std::fs::create_dir_all("log").unwrap();
-            // Clear all previous logs file
-            for file in std::fs::read_dir("log").unwrap() {
-                std::fs::write(file.unwrap().path(), "").unwrap();
-            }
-        }
-        // Generate temporary dirs
-        let dirs = Dirs::generate();
-        // Prepare config
-        {
-            // Generate taler config from base
-            let config = PathBuf::from_str("instrumentation/conf")
-                .unwrap()
-                .join(config);
-            let mut config = std::fs::read_to_string(config).unwrap();
-            write!(
-                &mut config,
-                "\nCONF_PATH = {}\nIPC_PATH={}",
-                dirs.wire_dir.to_string_lossy(),
-                dirs.wire_dir.to_string_lossy()
-            )
-            .unwrap();
-            std::fs::write(&dirs.conf, config).unwrap();
-        }
-
+impl TestCtx {
+    pub fn new(name: &str) -> Self {
+        // Create log dir
+        let log_dir = format!("log/{name}").into();
+        std::fs::remove_dir_all(&log_dir).ok();
+        std::fs::create_dir_all(&log_dir).unwrap();
         // Generate password
         let pwd: String = (0..30).map(|_| fastrand::alphanumeric()).collect();
         std::env::set_var("PASSWORD", &pwd);
 
-        dirs
+        Self {
+            log_dir,
+            out: Arc::new(Mutex::new(String::new())),
+        }
+    }
+
+    pub fn log(&self, name: &str) -> String {
+        format!("{}/{name}.log", self.log_dir)
     }
 
-    pub fn setup(dirs: Dirs, name: &str, stressed: bool, init_wallet: impl 
FnOnce(&Dirs)) -> Self {
-        let taler_conf = TalerConfig::load(Some(&dirs.conf));
+    pub fn step(&self, disp: impl Display) {
+        let it: &mut String = &mut self.out.lock().unwrap();
+        writeln!(it, "{disp}").unwrap();
+    }
+}
+
+pub struct TalerCtx {
+    pub dir: TempDir,
+    pub wire_dir: PathBuf,
+    pub wire2_dir: PathBuf,
+    pub db_dir: PathBuf,
+    pub conf: PathBuf,
+    pub taler_conf: TalerConfig,
+    ctx: TestCtx,
+    db: ChildGuard,
+    wire_name: String,
+    stressed: bool,
+    gateway: Option<ChildGuard>,
+    pub gateway_url: String,
+    wire: Option<ChildGuard>,
+    wire2: Option<ChildGuard>,
+    pub gateway_port: u16,
+}
+
+impl TalerCtx {
+    pub fn new(ctx: &TestCtx, wire_name: impl Into<String>, config: &str, 
stressed: bool) -> Self {
+        // Create temporary dir
+        let dir = TempDir::new().unwrap();
+        let conf = dir.path().join("taler.conf");
+
+        // Create common dirs
+        let wire_dir = dir.path().join("wire");
+        let wire2_dir = dir.path().join("wire2");
+        let db_dir = dir.path().join("db");
+        for dir in [&wire_dir, &wire2_dir, &db_dir] {
+            std::fs::create_dir_all(dir).unwrap();
+        }
+
+        // Find unused port
+        let gateway_port = unused_port();
+        let db_port = unused_port();
+
+        // Generate taler config from base
+        let config = PathBuf::from_str("instrumentation/conf")
+            .unwrap()
+            .join(config);
+        let mut config = ini::Ini::load_from_file(config).unwrap();
+        let section = config
+            .sections()
+            .find(|it| {
+                it.map(|it| it.starts_with("depolymerizer-"))
+                    .unwrap_or(false)
+            })
+            .unwrap_or_default()
+            .map(|it| it.to_string());
+        config
+            .with_section(Some("exchange-accountcredentials-admin"))
+            .set(
+                "WIRE_GATEWAY_URL",
+                format!("http://localhost:{gateway_port}/";),
+            );
+        config
+            .with_section(section)
+            .set("CONF_PATH", wire_dir.to_string_lossy())
+            .set("IPC_PATH", wire_dir.to_string_lossy())
+            .set(
+                "DB_URL",
+                
format!("postgres://localhost:{db_port}/postgres?user=postgres&password=password"),
+            )
+            .set("PORT", gateway_port.to_string());
+
+        config.write_to_file(&conf).unwrap();
+        let taler_conf = TalerConfig::load(Some(&conf));
+
         // Setup database
         let db = {
             // Init databases files
             cmd_redirect_ok(
                 "initdb",
-                &[dirs.db_dir.to_string_lossy().as_ref()],
-                "log/postgres.log",
+                &[db_dir.to_string_lossy().as_ref()],
+                &ctx.log("postgres"),
                 "init_db",
             );
             // Generate database config
             std::fs::write(
-                dirs.db_dir.join("postgresql.conf"),
+                db_dir.join("postgresql.conf"),
                 format!(
-                    "port=5454\nunix_socket_directories='{}'\n",
-                    dirs.db_dir.to_string_lossy().as_ref()
+                    "port={db_port}\nunix_socket_directories='{}'\n",
+                    db_dir.to_string_lossy().as_ref()
                 ),
             )
             .unwrap();
-            let db = Self::_start_db(&dirs.db_dir);
+            let db = TalerCtx::start_db(&db_dir, &ctx);
             retry(|| {
                 let mut psql = ChildGuard(
                     Command::new("psql")
-                        .args(["-h", "localhost", "-p", "5454", "postgres"])
+                        .args(["-h", "localhost", "-p", &db_port.to_string(), 
"postgres"])
                         .stderr(Stdio::null())
                         .stdout(
                             std::fs::File::options()
                                 .append(true)
                                 .create(true)
-                                .open("log/postgres.log")
+                                .open(&ctx.log("postgres"))
                                 .unwrap(),
                         )
                         .stdin(Stdio::piped())
@@ -321,93 +346,116 @@ impl CommonCtx {
             db
         };
 
-        // Wire
-        let wire_path = format!("target/release/{}", name);
-        let mut args = vec!["build", "--bin", name, "--release"];
-        if stressed {
-            args.extend_from_slice(&["--features", "fail"]);
+        Self {
+            ctx: ctx.clone(),
+            gateway_url: format!("http://localhost:{}/";, taler_conf.port()),
+            dir,
+            wire_dir,
+            wire2_dir,
+            db_dir,
+            conf,
+            taler_conf,
+            db,
+            wire_name: wire_name.into(),
+            stressed,
+            gateway: None,
+            wire: None,
+            wire2: None,
+            gateway_port,
         }
-        cmd_redirect_ok("cargo", &args, "log/cargo.log", "build wire");
-        cmd_redirect_ok(
-            &wire_path,
-            &["-c", dirs.conf.to_string_lossy().as_ref(), "initdb"],
-            "log/cmd.log",
-            "wire initdb",
-        );
+    }
+
+    fn wire_args<'a>(&'a self, args: &[&'a str]) -> Vec<&'a str> {
+        let mut tmp = vec!["run", "--bin", &self.wire_name, "--release"];
+        if self.stressed {
+            tmp.extend_from_slice(&["--features", "fail"]);
+        }
+        tmp.push("--");
+        tmp.extend_from_slice(args);
+        return tmp;
+    }
 
-        init_wallet(&dirs);
+    pub fn init_db(&self) {
+        // Init db
+        retry(|| {
+            cmd_redirect(
+                "cargo",
+                &self.wire_args(&["-c", self.conf.to_string_lossy().as_ref(), 
"initdb"]),
+                &self.log("cmd"),
+            )
+            .0
+            .wait()
+            .unwrap()
+            .success()
+        })
+    }
 
+    pub fn run(&mut self) {
         // Start wires
-        let wire = cmd_redirect(
-            &wire_path,
-            &["-c", dirs.conf.to_string_lossy().as_ref()],
-            "log/wire.log",
-        );
-        let wire2 = stressed.then(|| {
+        self.wire = Some(cmd_redirect(
+            "cargo",
+            &self.wire_args(&["-c", self.conf.to_string_lossy().as_ref()]),
+            &self.log("wire"),
+        ));
+        self.wire2 = self.stressed.then(|| {
             cmd_redirect(
-                &wire_path,
-                &["-c", dirs.conf.to_string_lossy().as_ref()],
-                "log/wire2.log",
+                "cargo",
+                &self.wire_args(&["-c", self.conf.to_string_lossy().as_ref()]),
+                &self.log("wire1"),
             )
         });
 
-        // Gateway
-        cmd_redirect_ok(
+        // Run gateway
+        self.gateway = Some(cmd_redirect(
             "cargo",
             &[
-                "build",
+                "run",
                 "--bin",
                 "wire-gateway",
                 "--release",
                 "--features",
                 "test",
+                "--",
+                "-c",
+                self.conf.to_string_lossy().as_ref(),
             ],
-            "log/cargo.log",
-            "build gateway",
-        );
-        let gateway = cmd_redirect(
-            "target/release/wire-gateway",
-            &["-c", dirs.conf.to_string_lossy().as_ref()],
-            "log/gateway.log",
-        );
-
-        let gateway_url = format!("http://localhost:{}/";, taler_conf.port());
-
-        Self {
-            dirs,
-            gateway,
-            gateway_url,
-            taler_conf,
-            wire,
-            db,
-            _wire2: wire2,
-        }
+            &self.log("gateway"),
+        ));
+        retry(|| {
+            TcpStream::connect(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 
self.gateway_port)).is_ok()
+        });
     }
 
     /* ----- Process ----- */
 
     #[must_use]
     pub fn wire_running(&mut self) -> bool {
-        self.wire.0.try_wait().unwrap().is_none()
+        self.wire.as_mut().unwrap().0.try_wait().unwrap().is_none()
     }
 
     #[must_use]
     pub fn gateway_running(&mut self) -> bool {
-        self.gateway.0.try_wait().unwrap().is_none()
+        self.gateway
+            .as_mut()
+            .unwrap()
+            .0
+            .try_wait()
+            .unwrap()
+            .is_none()
     }
 
     /* ----- Database ----- */
 
-    fn _start_db(path: &Path) -> ChildGuard {
+    fn start_db(db_dir: &PathBuf, ctx: &TestCtx) -> ChildGuard {
         cmd_redirect(
             "postgres",
-            &["-D", path.to_string_lossy().as_ref()],
-            "log/postgres.log",
+            &["-D", db_dir.to_string_lossy().as_ref()],
+            &ctx.log("postgres"),
         )
     }
 
     pub fn resume_db(&mut self) {
-        self.db = Self::_start_db(&self.dirs.db_dir);
+        self.db = Self::start_db(&self.db_dir, &self.ctx);
     }
 
     pub fn stop_db(&mut self) {
@@ -436,3 +484,25 @@ impl CommonCtx {
         retry(|| check_gateway_down(&self.gateway_url));
     }
 }
+
+impl Deref for TalerCtx {
+    type Target = TestCtx;
+
+    fn deref(&self) -> &Self::Target {
+        &self.ctx
+    }
+}
+
+impl DerefMut for TalerCtx {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.ctx
+    }
+}
+
+pub fn unused_port() -> u16 {
+    TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))
+        .unwrap()
+        .local_addr()
+        .unwrap()
+        .port()
+}
diff --git a/makefile b/makefile
index acacd8e..0367d14 100644
--- a/makefile
+++ b/makefile
@@ -7,7 +7,7 @@ segwit_demo:
        cargo run --release --bin segwit-demo
 
 test:
-       cargo run --bin instrumentation -- offline
+       RUST_BACKTRACE=full cargo run -r --bin instrumentation -- offline
 
 msrv:
        cargo msrv --min 1.63.0 --max 1.63.0 --linear
\ No newline at end of file
diff --git a/script/prepare.sh b/script/prepare.sh
index 137dff5..d95d9d7 100755
--- a/script/prepare.sh
+++ b/script/prepare.sh
@@ -22,19 +22,19 @@ echo "Found version $PG_VER"
 
 echo "Ⅱ - Install bitcoind version 0.23"
 cd $DIR
-curl -L 
https://bitcoincore.org/bin/bitcoin-core-23.0/bitcoin-23.0-x86_64-linux-gnu.tar.gz
 -o btc.tar.gz
+curl -L 
https://bitcoincore.org/bin/bitcoin-core-24.0.1/bitcoin-24.0.1-x86_64-linux-gnu.tar.gz
 -o btc.tar.gz
 tar xvzf btc.tar.gz
 rm -rfv ~/bitcoin
 mkdir -pv ~/bitcoin
-mv -v bitcoin-23.0/* ~/bitcoin
+mv -v bitcoin-24.0.1/* ~/bitcoin
 
 echo "Ⅲ - Install Go Ethereum (Geth) v1.10.24"
 cd $DIR
-curl -L 
https://gethstore.blob.core.windows.net/builds/geth-alltools-linux-amd64-1.10.24-972007a5.tar.gz
 -o geth.tar.gz
+curl -L 
https://gethstore.blob.core.windows.net/builds/geth-alltools-linux-amd64-1.10.26-e5eb32ac.tar.gz
 -o geth.tar.gz
 tar xvzf geth.tar.gz
 rm -rfv ~/geth
 mkdir -pv ~/geth
-mv -v geth-alltools-linux-amd64-1.10.24-972007a5/* ~/geth
+mv -v geth-alltools-linux-amd64-1.10.26-e5eb32ac/* ~/geth
 
 echo "Ⅳ - Install Lightning Network Daemon v0.15.5-beta"
 cd $DIR

-- 
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]