diff options
author | Antoine A <> | 2023-01-31 09:09:24 +0100 |
---|---|---|
committer | Antoine A <> | 2023-01-31 15:29:13 +0100 |
commit | 88b8c4d62424ccb1804cfea8bbf4d686226261b3 (patch) | |
tree | 55bd8091ed859472ed05c38de2f0e6446a3c938c | |
parent | 67c682100c7efcf58d53418c45e6611e5f6eab82 (diff) | |
download | depolymerization-88b8c4d62424ccb1804cfea8bbf4d686226261b3.tar.gz depolymerization-88b8c4d62424ccb1804cfea8bbf4d686226261b3.tar.bz2 depolymerization-88b8c4d62424ccb1804cfea8bbf4d686226261b3.zip |
Run integration tests in parallel
-rw-r--r-- | Cargo.lock | 68 | ||||
-rw-r--r-- | instrumentation/Cargo.toml | 3 | ||||
-rw-r--r-- | instrumentation/src/btc.rs | 286 | ||||
-rw-r--r-- | instrumentation/src/eth.rs | 255 | ||||
-rw-r--r-- | instrumentation/src/gateway.rs | 33 | ||||
-rw-r--r-- | instrumentation/src/main.rs | 120 | ||||
-rw-r--r-- | instrumentation/src/utils.rs | 332 | ||||
-rw-r--r-- | makefile | 2 | ||||
-rwxr-xr-x | script/prepare.sh | 4 |
9 files changed, 617 insertions, 486 deletions
@@ -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", ] @@ -2009,6 +2011,12 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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/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() +} @@ -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..c2466b4 100755 --- a/script/prepare.sh +++ b/script/prepare.sh @@ -22,11 +22,11 @@ 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-23.1/bitcoin-23.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-23.1/* ~/bitcoin echo "Ⅲ - Install Go Ethereum (Geth) v1.10.24" cd $DIR |