depolymerization

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

btc.rs (34114B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2022-2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 
     17 use std::{
     18     net::{IpAddr, Ipv4Addr, SocketAddr},
     19     ops::{Deref, DerefMut},
     20     path::Path,
     21     time::Duration,
     22 };
     23 
     24 use bitcoin::{Address, Amount};
     25 use depolymerizer_bitcoin::{
     26     CONFIG_SOURCE,
     27     config::{RpcAuth, RpcCfg, ServeCfg, WorkerCfg},
     28     payto::BtcWallet,
     29     rpc::{Category, Rpc},
     30     rpc_utils::segwit_min_amount,
     31     taler_utils::btc_to_taler,
     32 };
     33 use ini::Ini;
     34 use taler_common::{
     35     api_common::{EddsaPublicKey, ShortHashCode},
     36     types::base32::Base32,
     37 };
     38 use taler_common::{config::Config, types::payto::Payto};
     39 
     40 use crate::{
     41     retry, retry_opt,
     42     utils::{ChildGuard, TalerCtx, TestCtx, cmd_redirect, patch_config, transfer, unused_port},
     43 };
     44 
     45 pub struct BtcCtx {
     46     btc_node: ChildGuard,
     47     _btc_node2: ChildGuard,
     48     common_rpc: Rpc,
     49     common_rpc2: Rpc,
     50     wire_rpc: Rpc,
     51     client_rpc: Rpc,
     52     reserve_rpc: Rpc,
     53     wire_addr: Address,
     54     pub client_addr: Address,
     55     reserve_addr: Address,
     56     worker_cfg: WorkerCfg,
     57     serve_cfg: ServeCfg,
     58     conf: u16,
     59     ctx: TalerCtx,
     60     node2_addr: String,
     61 }
     62 
     63 impl Deref for BtcCtx {
     64     type Target = TalerCtx;
     65 
     66     fn deref(&self) -> &Self::Target {
     67         &self.ctx
     68     }
     69 }
     70 
     71 impl DerefMut for BtcCtx {
     72     fn deref_mut(&mut self) -> &mut Self::Target {
     73         &mut self.ctx
     74     }
     75 }
     76 
     77 impl BtcCtx {
     78     pub async fn config(
     79         ctx: &TestCtx,
     80         btc_patch: impl FnOnce(&mut Ini, &Path),
     81         cfg_patch: impl FnOnce(&mut Ini, &Path),
     82     ) {
     83         // Patch configs
     84         let port = unused_port();
     85         let rpc_port = unused_port();
     86 
     87         patch_config(
     88             "testbench/conf/bitcoin.conf",
     89             ctx.dir.join("bitcoin.conf"),
     90             |cfg| {
     91                 cfg.with_section(Some("regtest"))
     92                     .set("bind", format!("127.0.0.1:{port}"))
     93                     .set("rpcport", format!("{rpc_port}"));
     94                 btc_patch(cfg, &ctx.dir)
     95             },
     96         );
     97         patch_config(
     98             "testbench/conf/taler_btc.conf",
     99             ctx.dir.join("config.conf"),
    100             |cfg| {
    101                 cfg.with_section(Some("depolymerizer-bitcoin-worker"))
    102                     .set("RPC_BIND", format!("127.0.0.1:{rpc_port}"));
    103                 cfg_patch(cfg, &ctx.dir)
    104             },
    105         );
    106         // Load config
    107         let cfg = Config::from_file(CONFIG_SOURCE, Some(ctx.dir.join("config.conf"))).unwrap();
    108         let rpc_cfg = RpcCfg::parse(&cfg).unwrap();
    109         // Start bitcoin nodes
    110         let _node = cmd_redirect(
    111             "bitcoind",
    112             &[&format!("-datadir={}", ctx.dir.to_string_lossy())],
    113             ctx.log("bitcoind"),
    114         );
    115         // Connect
    116         retry_opt! {
    117             async {
    118                 let mut client = Rpc::common(&rpc_cfg).await?;
    119                 client.get_blockchain_info().await?;
    120                 Ok::<_, anyhow::Error>(())
    121             }.await
    122         };
    123     }
    124 
    125     fn patch_btc_config(from: impl AsRef<Path>, to: impl AsRef<Path>, port: u16, rpc_port: u16) {
    126         patch_config(from, to, |cfg| {
    127             cfg.with_section(Some("regtest"))
    128                 .set("bind", format!("127.0.0.1:{port}"))
    129                 .set("rpcport", format!("{rpc_port}"));
    130         })
    131     }
    132 
    133     pub async fn setup(ctx: &TestCtx, config: &str, stressed: bool) -> Self {
    134         let mut ctx = TalerCtx::new(ctx, "depolymerizer-bitcoin", config, stressed);
    135 
    136         // Choose unused port
    137         let btc_port = unused_port();
    138         let btc_rpc_port = unused_port();
    139         let btc2_port = unused_port();
    140         let btc2_rpc_port = unused_port();
    141 
    142         // Bitcoin config
    143         Self::patch_btc_config(
    144             "testbench/conf/bitcoin.conf",
    145             ctx.wire_dir.join("bitcoin.conf"),
    146             btc_port,
    147             btc_rpc_port,
    148         );
    149         Self::patch_btc_config(
    150             "testbench/conf/bitcoin.conf",
    151             ctx.wire2_dir.join("bitcoin.conf"),
    152             btc2_port,
    153             btc2_rpc_port,
    154         );
    155         patch_config(&ctx.conf, &ctx.conf, |cfg| {
    156             cfg.with_section(Some("depolymerizer-bitcoin-worker"))
    157                 .set("RPC_BIND", format!("127.0.0.1:{btc_rpc_port}"))
    158                 .set("WALLET_NAME", "wire")
    159                 .set(
    160                     "RPC_COOKIE_FILE",
    161                     ctx.wire_dir.join("regtest/.cookie").to_string_lossy(),
    162                 );
    163         });
    164 
    165         // Load config
    166         let config = Config::from_file(CONFIG_SOURCE, Some(&ctx.conf)).unwrap();
    167         let cfg = WorkerCfg::parse(&config).unwrap();
    168         // Start bitcoin nodes
    169         let btc_node = cmd_redirect(
    170             "bitcoind",
    171             &[&format!("-datadir={}", ctx.wire_dir.to_string_lossy())],
    172             ctx.log("bitcoind"),
    173         );
    174         let _btc_node2 = cmd_redirect(
    175             "bitcoind",
    176             &[&format!("-datadir={}", ctx.wire2_dir.to_string_lossy())],
    177             ctx.log("bitcoind2"),
    178         );
    179 
    180         // Setup wallets
    181         let mut common_rpc = retry_opt! { Rpc::common(&cfg.rpc_cfg).await };
    182         retry_opt! { common_rpc.get_blockchain_info().await };
    183         let node2_addr = format!("127.0.0.1:{btc2_port}");
    184         common_rpc.add_node(&node2_addr).await.unwrap();
    185 
    186         for name in ["wire", "client", "reserve"] {
    187             common_rpc.create_wallet(name, "").await.unwrap();
    188         }
    189         let common_rpc2 = retry_opt! {
    190             Rpc::common(&RpcCfg {
    191                 addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), btc2_rpc_port),
    192                 auth: RpcAuth::Cookie(ctx
    193                         .wire2_dir
    194                         .join("regtest/.cookie")
    195                         .to_string_lossy()
    196                         .to_string()),
    197             }).await
    198         };
    199 
    200         // Generate money
    201         let mut reserve_rpc = Rpc::wallet(&cfg.rpc_cfg, "reserve").await.unwrap();
    202         let mut client_rpc = Rpc::wallet(&cfg.rpc_cfg, "client").await.unwrap();
    203         let mut wire_rpc = Rpc::wallet(&cfg.rpc_cfg, "wire").await.unwrap();
    204         let reserve_addr = reserve_rpc.gen_addr().await.unwrap();
    205         let client_addr = client_rpc.gen_addr().await.unwrap();
    206         let wire_addr = wire_rpc.gen_addr().await.unwrap();
    207         common_rpc.mine(101, &reserve_addr).await.unwrap();
    208         reserve_rpc
    209             .send(&client_addr, &(Amount::ONE_BTC * 10), None, false)
    210             .await
    211             .unwrap();
    212         common_rpc.mine(1, &reserve_addr).await.unwrap();
    213 
    214         patch_config(&ctx.conf, &ctx.conf, |cfg| {
    215             cfg.with_section(Some("depolymerizer-bitcoin"))
    216                 .set("NAME", "Exchange Owner")
    217                 .set("WALLET", wire_addr.to_string());
    218         });
    219 
    220         let config = Config::from_file(CONFIG_SOURCE, Some(&ctx.conf)).unwrap();
    221         let serve_cfg = ServeCfg::parse(&config).unwrap();
    222 
    223         // Setup & run
    224         ctx.dbinit();
    225         ctx.setup();
    226         ctx.run().await;
    227 
    228         Self {
    229             ctx,
    230             btc_node,
    231             common_rpc,
    232             wire_rpc,
    233             client_rpc,
    234             reserve_rpc,
    235             wire_addr,
    236             client_addr,
    237             reserve_addr,
    238             conf: cfg.confirmation as u16,
    239             worker_cfg: cfg,
    240             serve_cfg,
    241             _btc_node2,
    242             common_rpc2,
    243             node2_addr,
    244         }
    245     }
    246 
    247     pub fn reset_db(&mut self) {
    248         self.ctx.reset_db();
    249         self.ctx.setup();
    250     }
    251 
    252     pub async fn stop_node(&mut self) {
    253         // We need to kill bitcoin gracefully to avoid corruption
    254         self.common_rpc.stop().await.unwrap();
    255         self.btc_node.0.wait().unwrap();
    256     }
    257 
    258     pub async fn cluster_deco(&mut self) {
    259         self.common_rpc
    260             .disconnect_node(&self.node2_addr)
    261             .await
    262             .unwrap();
    263     }
    264 
    265     pub async fn cluster_fork(&mut self) {
    266         let node1_height = self.common_rpc.get_blockchain_info().await.unwrap().blocks;
    267         let node2_height = self.common_rpc2.get_blockchain_info().await.unwrap().blocks;
    268         let diff = node1_height - node2_height;
    269         self.common_rpc2
    270             .mine((diff + 1) as u16, &self.reserve_addr)
    271             .await
    272             .unwrap();
    273         self.common_rpc.add_node(&self.node2_addr).await.unwrap();
    274     }
    275 
    276     pub async fn restart_node(&mut self, additional_args: &[&str]) {
    277         self.stop_node().await;
    278         self.resume_node(additional_args).await;
    279     }
    280 
    281     pub async fn resume_node(&mut self, additional_args: &[&str]) {
    282         let datadir = format!("-datadir={}", self.ctx.wire_dir.to_string_lossy());
    283         let mut args = vec![datadir.as_str()];
    284         args.extend_from_slice(additional_args);
    285         self.btc_node = cmd_redirect("bitcoind", &args, self.ctx.log("bitcoind"));
    286         self.common_rpc = retry_opt! { Rpc::common(&self.worker_cfg.rpc_cfg).await };
    287         self.common_rpc.add_node(&self.node2_addr).await.unwrap();
    288         for name in ["client", "reserve", "wire"] {
    289             self.common_rpc.load_wallet(name).await.ok();
    290         }
    291 
    292         self.reserve_rpc = Rpc::wallet(&self.worker_cfg.rpc_cfg, "reserve")
    293             .await
    294             .unwrap();
    295         self.client_rpc = Rpc::wallet(&self.worker_cfg.rpc_cfg, "client")
    296             .await
    297             .unwrap();
    298         self.wire_rpc = Rpc::wallet(&self.worker_cfg.rpc_cfg, "wire").await.unwrap();
    299     }
    300 
    301     /* ----- Transaction ------ */
    302 
    303     pub async fn credit(&mut self, amount: Amount, metadata: &EddsaPublicKey) {
    304         self.client_rpc
    305             .send_segwit_key(&self.wire_addr, &amount, metadata)
    306             .await
    307             .unwrap();
    308     }
    309 
    310     pub async fn debit(&mut self, amount: Amount, metadata: &ShortHashCode) {
    311         transfer(
    312             &self.ctx.gateway_url,
    313             metadata,
    314             Payto::new(BtcWallet(self.client_addr.clone()))
    315                 .as_payto()
    316                 .as_full_payto("name"),
    317             &btc_to_taler(&amount.to_signed().unwrap(), &self.worker_cfg.currency),
    318         )
    319         .await
    320     }
    321 
    322     pub async fn malformed_credit(&mut self, amount: &Amount) {
    323         self.client_rpc
    324             .send(&self.wire_addr, amount, None, false)
    325             .await
    326             .unwrap();
    327     }
    328 
    329     pub async fn reset_wallet(&mut self) {
    330         let amount = self.wire_balance().await;
    331         self.wire_rpc
    332             .send(&self.client_addr, &amount, None, true)
    333             .await
    334             .unwrap();
    335         self.next_block().await;
    336     }
    337 
    338     async fn abandon(rpc: &mut Rpc) {
    339         let list = rpc.list_since_block(None, 1).await.unwrap();
    340         for tx in list.transactions {
    341             if tx.category == Category::Send && tx.confirmations == 0 {
    342                 rpc.abandon_tx(&tx.txid).await.unwrap();
    343             }
    344         }
    345     }
    346 
    347     pub async fn abandon_wire(&mut self) {
    348         Self::abandon(&mut self.wire_rpc).await;
    349     }
    350 
    351     pub async fn abandon_client(&mut self) {
    352         Self::abandon(&mut self.client_rpc).await;
    353     }
    354 
    355     /* ----- Mining ----- */
    356 
    357     async fn mine(&mut self, nb: u16) {
    358         self.common_rpc.mine(nb, &self.reserve_addr).await.unwrap();
    359     }
    360 
    361     pub async fn next_conf(&mut self) {
    362         self.mine(self.conf).await
    363     }
    364 
    365     pub async fn next_block(&mut self) {
    366         self.mine(1).await
    367     }
    368 
    369     /* ----- Balances ----- */
    370 
    371     pub async fn client_balance(&mut self) -> Amount {
    372         self.client_rpc.get_balance().await.unwrap()
    373     }
    374 
    375     pub async fn wire_balance(&mut self) -> Amount {
    376         self.wire_rpc.get_balance().await.unwrap()
    377     }
    378 
    379     pub async fn expect_client_balance(&mut self, balance: Amount, mine: bool) {
    380         retry! {{
    381             let check = balance == self.client_balance().await;
    382             if !check && mine {
    383                 self.next_block().await;
    384             }
    385             check
    386         }}
    387     }
    388 
    389     pub async fn expect_wire_balance(&mut self, balance: Amount, mine: bool) {
    390         retry! {{
    391             let check = balance == self.wire_balance().await;
    392             if !check && mine {
    393                 self.next_block().await;
    394             }
    395             check
    396         }}
    397     }
    398 
    399     /* ----- Wire Gateway ----- */
    400 
    401     pub async fn expect_credits(&self, txs: &[(EddsaPublicKey, Amount)]) {
    402         let txs: Vec<_> = txs
    403             .iter()
    404             .map(|(metadata, amount)| {
    405                 (
    406                     metadata.clone(),
    407                     btc_to_taler(&amount.to_signed().unwrap(), &self.worker_cfg.currency),
    408                 )
    409             })
    410             .collect();
    411         self.ctx.expect_credits(&txs).await
    412     }
    413 
    414     pub async fn expect_debits(&self, txs: &[(ShortHashCode, Amount)]) {
    415         let txs: Vec<_> = txs
    416             .iter()
    417             .map(|(metadata, amount)| {
    418                 (
    419                     metadata.clone(),
    420                     btc_to_taler(&amount.to_signed().unwrap(), &self.worker_cfg.currency),
    421                 )
    422             })
    423             .collect();
    424         self.ctx.expect_debits(&txs).await
    425     }
    426 }
    427 
    428 /// Test btc-wire correctly receive and send transactions on the blockchain
    429 pub async fn wire(ctx: TestCtx) {
    430     ctx.step("Setup");
    431     let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false).await;
    432 
    433     ctx.step("Credit");
    434     {
    435         // Send transactions
    436         let mut balance = ctx.wire_balance().await;
    437         let mut txs = Vec::new();
    438         for n in 10..100 {
    439             let metadata = Base32::rand();
    440             let amount = Amount::from_sat(n * 1000);
    441             ctx.credit(amount, &metadata).await;
    442             txs.push((metadata, amount));
    443             balance += amount;
    444             ctx.next_block().await;
    445         }
    446         ctx.next_conf().await;
    447         ctx.expect_credits(&txs).await;
    448         ctx.expect_wire_balance(balance, false).await;
    449     };
    450 
    451     ctx.step("Debit");
    452     {
    453         let mut balance = ctx.client_balance().await;
    454         let mut txs = Vec::new();
    455         for n in 10..100 {
    456             let metadata = Base32::rand();
    457             let amount = Amount::from_sat(n * 100);
    458             balance += amount;
    459             ctx.debit(amount, &metadata).await;
    460             txs.push((metadata, amount));
    461         }
    462         ctx.next_block().await;
    463         ctx.expect_debits(&txs).await;
    464         ctx.expect_client_balance(balance, true).await;
    465     }
    466 
    467     ctx.step("Bounce");
    468     {
    469         ctx.reset_wallet().await;
    470         // Send bad transactions
    471         let mut balance = ctx.wire_balance().await;
    472         for n in 10..40 {
    473             ctx.malformed_credit(&Amount::from_sat(n * 1000)).await;
    474             balance += ctx.worker_cfg.bounce_fee;
    475         }
    476         ctx.next_conf().await;
    477         ctx.expect_wire_balance(balance, true).await;
    478     }
    479 }
    480 
    481 /// Check btc-wire and wire-gateway correctly stop when a lifetime limit is configured
    482 pub async fn lifetime(ctx: TestCtx) {
    483     ctx.step("Setup");
    484     let mut ctx = BtcCtx::setup(&ctx, "taler_btc_lifetime.conf", false).await;
    485     ctx.step("Check lifetime");
    486     // Start up
    487     retry! { ctx.wire_running() && ctx.gateway_running() };
    488     // Consume wire lifetime
    489     for _ in 0..=ctx.worker_cfg.lifetime.unwrap() + 2 {
    490         ctx.credit(segwit_min_amount(), &Base32::rand()).await;
    491         ctx.next_block().await;
    492         tokio::time::sleep(Duration::from_millis(100)).await;
    493     }
    494     retry! { !ctx.wire_running() };
    495     // Consume gateway lifetime
    496     for _ in 0..=ctx.serve_cfg.lifetime.unwrap() {
    497         ctx.debit(segwit_min_amount(), &Base32::rand()).await;
    498         ctx.next_block().await;
    499     }
    500     // End down
    501     retry! { !ctx.wire_running() && !ctx.gateway_running() };
    502 }
    503 
    504 /// Check the capacity of wire-gateway and btc-wire to recover from database and node loss
    505 pub async fn reconnect(ctx: TestCtx) {
    506     ctx.step("Setup");
    507     let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false).await;
    508 
    509     let mut credits = Vec::new();
    510     let mut debits = Vec::new();
    511 
    512     ctx.step("With DB");
    513     {
    514         let metadata = Base32::rand();
    515         let amount = Amount::from_sat(42000);
    516         ctx.credit(amount, &metadata).await;
    517         credits.push((metadata, amount));
    518         ctx.next_block().await;
    519         ctx.next_conf().await;
    520         ctx.expect_credits(&credits).await;
    521     };
    522 
    523     ctx.step("Without DB");
    524     {
    525         ctx.stop_db();
    526         ctx.malformed_credit(&Amount::from_sat(24000)).await;
    527         let metadata = Base32::rand();
    528         let amount = Amount::from_sat(40000);
    529         ctx.credit(amount, &metadata).await;
    530         credits.push((metadata, amount));
    531         ctx.stop_node().await;
    532         ctx.expect_gateway_down().await;
    533     }
    534 
    535     ctx.step("Reconnect DB");
    536     {
    537         ctx.resume_db();
    538         ctx.resume_node(&[]).await;
    539         let metadata = Base32::rand();
    540         let amount = Amount::from_sat(2000);
    541         ctx.debit(amount, &metadata).await;
    542         debits.push((metadata, amount));
    543         ctx.next_conf().await;
    544         ctx.expect_debits(&debits).await;
    545         ctx.expect_credits(&credits).await;
    546     }
    547 
    548     ctx.step("Recover DB");
    549     {
    550         let balance = ctx.wire_balance().await;
    551         ctx.reset_db();
    552         ctx.next_block().await;
    553         ctx.expect_debits(&debits).await;
    554         ctx.expect_credits(&credits).await;
    555         ctx.expect_wire_balance(balance, true).await;
    556     }
    557 }
    558 
    559 /// Test btc-wire ability to recover from errors in correctness critical paths and prevent concurrent sending
    560 pub async fn stress(ctx: TestCtx) {
    561     ctx.step("Setup");
    562     let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", true).await;
    563 
    564     let mut credits = Vec::new();
    565     let mut debits = Vec::new();
    566 
    567     ctx.step("Credit");
    568     {
    569         let mut balance = ctx.wire_balance().await;
    570         for n in 10..30 {
    571             let metadata = Base32::rand();
    572             let amount = Amount::from_sat(n * 1000);
    573             ctx.credit(amount, &metadata).await;
    574             credits.push((metadata, amount));
    575             balance += amount;
    576             ctx.next_block().await;
    577         }
    578         ctx.next_conf().await;
    579         ctx.expect_credits(&credits).await;
    580         ctx.expect_wire_balance(balance, true).await;
    581     };
    582 
    583     ctx.step("Debit");
    584     {
    585         let mut balance = ctx.client_balance().await;
    586         for n in 10..30 {
    587             let metadata = Base32::rand();
    588             let amount = Amount::from_sat(n * 100);
    589             balance += amount;
    590             ctx.debit(amount, &metadata).await;
    591             debits.push((metadata, amount));
    592         }
    593         ctx.next_block().await;
    594         ctx.expect_debits(&debits).await;
    595         ctx.expect_client_balance(balance, true).await;
    596     }
    597 
    598     ctx.step("Bounce");
    599     {
    600         ctx.reset_wallet().await;
    601         let mut balance = ctx.wire_balance().await;
    602         for n in 10..30 {
    603             ctx.malformed_credit(&Amount::from_sat(n * 1000)).await;
    604             balance += ctx.worker_cfg.bounce_fee;
    605         }
    606         ctx.next_conf().await;
    607         ctx.expect_wire_balance(balance, true).await;
    608     }
    609 
    610     ctx.step("Recover DB");
    611     {
    612         let balance = ctx.wire_balance().await;
    613         ctx.reset_db();
    614         ctx.next_block().await;
    615         ctx.expect_debits(&debits).await;
    616         ctx.expect_credits(&credits).await;
    617         ctx.expect_wire_balance(balance, true).await;
    618     }
    619 }
    620 
    621 /// Test btc-wire ability to handle conflicting outgoing transactions
    622 pub async fn conflict(tctx: TestCtx) {
    623     tctx.step("Setup");
    624     let mut ctx = BtcCtx::setup(&tctx, "taler_btc.conf", false).await;
    625 
    626     ctx.step("Conflict send");
    627     {
    628         // Perform credit
    629         let amount = Amount::from_sat(4200000);
    630         ctx.credit(amount, &Base32::rand()).await;
    631         ctx.next_conf().await;
    632         ctx.expect_wire_balance(amount, true).await;
    633         let client = ctx.client_balance().await;
    634         let wire = ctx.wire_balance().await;
    635 
    636         // Perform debit
    637         ctx.debit(Amount::from_sat(400000), &Base32::rand()).await;
    638         retry! { ctx.wire_balance().await < wire };
    639 
    640         // Abandon pending transaction
    641         ctx.restart_node(&["-minrelaytxfee=0.0001"]).await;
    642         ctx.abandon_wire().await;
    643         ctx.expect_client_balance(client, false).await;
    644         ctx.expect_wire_balance(wire, false).await;
    645 
    646         // Generate conflict
    647         ctx.debit(Amount::from_sat(500000), &Base32::rand()).await;
    648         retry! { ctx.wire_balance().await < wire };
    649 
    650         // Resend conflicting transaction
    651         ctx.restart_node(&[]).await;
    652         ctx.next_block().await;
    653         let wire = ctx.wire_balance().await;
    654         retry! { ctx.wire_balance().await < wire };
    655     }
    656 
    657     ctx.step("Setup");
    658     drop(ctx);
    659     let mut ctx = BtcCtx::setup(&tctx, "taler_btc.conf", false).await;
    660     ctx.credit(Amount::from_sat(3000000), &Base32::rand()).await;
    661     ctx.next_block().await;
    662 
    663     ctx.step("Conflict bounce");
    664     {
    665         // Perform bounce
    666         let wire = ctx.wire_balance().await;
    667         let bounce_amount = Amount::from_sat(4000000);
    668         ctx.malformed_credit(&bounce_amount).await;
    669         ctx.next_conf().await;
    670         let fee = ctx.worker_cfg.bounce_fee;
    671         ctx.expect_wire_balance(wire + fee, true).await;
    672 
    673         // Abandon pending transaction
    674         ctx.restart_node(&["-minrelaytxfee=0.0001"]).await;
    675         ctx.abandon_wire().await;
    676         ctx.expect_wire_balance(wire + bounce_amount, false).await;
    677 
    678         // Generate conflict
    679         let amount = Amount::from_sat(50000);
    680         ctx.debit(amount, &Base32::rand()).await;
    681         retry! { ctx.wire_balance().await < (wire + bounce_amount) };
    682 
    683         // Resend conflicting transaction
    684         ctx.restart_node(&[]).await;
    685         let wire = ctx.wire_balance().await;
    686         ctx.next_block().await;
    687         retry! { ctx.wire_balance().await < wire };
    688     }
    689 }
    690 
    691 /// Test btc-wire correctness when a blockchain reorganization occurs
    692 pub async fn reorg(ctx: TestCtx) {
    693     ctx.step("Setup");
    694     let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false).await;
    695 
    696     ctx.step("Handle reorg incoming transactions");
    697     {
    698         // Loose second bitcoin node
    699         ctx.cluster_deco().await;
    700 
    701         // Perform credits
    702         let before = ctx.wire_balance().await;
    703         for n in 10..21 {
    704             ctx.credit(Amount::from_sat(n * 10000), &Base32::rand())
    705                 .await;
    706             ctx.next_block().await;
    707         }
    708         let after = ctx.wire_balance().await;
    709 
    710         // Perform fork and check btc-wire hard error
    711         ctx.expect_gateway_up().await;
    712         ctx.cluster_fork().await;
    713         ctx.expect_wire_balance(before, false).await;
    714         ctx.expect_gateway_down().await;
    715 
    716         // Recover orphaned transaction
    717         ctx.mine(12).await;
    718         ctx.expect_wire_balance(after, false).await;
    719         ctx.expect_gateway_up().await;
    720     }
    721 
    722     ctx.step("Handle reorg outgoing transactions");
    723     {
    724         // Loose second bitcoin node
    725         ctx.cluster_deco().await;
    726 
    727         // Perform debits
    728         let before = ctx.client_balance().await;
    729         let mut after = ctx.client_balance().await;
    730         for n in 10..21 {
    731             let amount = Amount::from_sat(n * 100);
    732             ctx.debit(amount, &Base32::rand()).await;
    733             after += amount;
    734         }
    735         ctx.next_block().await;
    736         ctx.expect_client_balance(after, true).await;
    737 
    738         // Perform fork and check btc-wire still up
    739         ctx.expect_gateway_up().await;
    740         ctx.cluster_fork().await;
    741         ctx.expect_client_balance(before, false).await;
    742         ctx.expect_gateway_up().await;
    743 
    744         // Recover orphaned transaction
    745         ctx.next_conf().await;
    746         ctx.expect_client_balance(after, false).await;
    747     }
    748 
    749     ctx.step("Handle reorg bounce");
    750     {
    751         ctx.reset_wallet().await;
    752 
    753         // Loose second bitcoin node
    754         ctx.cluster_deco().await;
    755 
    756         // Perform bounce
    757         let before = ctx.wire_balance().await;
    758         let mut after = ctx.wire_balance().await;
    759         for n in 10..21 {
    760             ctx.malformed_credit(&Amount::from_sat(n * 1000)).await;
    761             after += ctx.worker_cfg.bounce_fee;
    762         }
    763         ctx.next_conf().await;
    764         ctx.expect_wire_balance(after, true).await;
    765 
    766         // Perform fork and check btc-wire hard error
    767         ctx.expect_gateway_up().await;
    768         ctx.cluster_fork().await;
    769         ctx.expect_wire_balance(before, false).await;
    770         ctx.expect_gateway_down().await;
    771 
    772         // Recover orphaned transaction
    773         ctx.mine(12).await;
    774         ctx.expect_wire_balance(after, false).await;
    775         ctx.expect_gateway_up().await;
    776     }
    777 }
    778 
    779 /// Test btc-wire correctness when a blockchain reorganization occurs leading to past incoming transaction conflict
    780 pub async fn hell(tctx: TestCtx) {
    781     macro_rules! step {
    782         ($ctx:ident, $action:expr) => {
    783             // Loose second bitcoin node
    784             $ctx.cluster_deco().await;
    785 
    786             // Perform action
    787             $action;
    788 
    789             // Perform fork and check btc-wire hard error
    790             $ctx.expect_gateway_up().await;
    791             $ctx.cluster_fork().await;
    792             $ctx.expect_gateway_down().await;
    793 
    794             // Generate conflict
    795             $ctx.restart_node(&["-minrelaytxfee=0.001"]).await;
    796             $ctx.abandon_client().await;
    797             let amount = Amount::from_sat(54000);
    798             $ctx.credit(amount, &Base32::rand()).await;
    799             $ctx.expect_wire_balance(amount, true).await;
    800 
    801             // Check btc-wire suspend operation
    802             let bounce_amount = Amount::from_sat(34000);
    803             $ctx.malformed_credit(&bounce_amount).await;
    804             $ctx.next_conf().await;
    805             $ctx.expect_wire_balance(amount + bounce_amount, true).await;
    806             $ctx.expect_gateway_down().await;
    807         };
    808     }
    809 
    810     tctx.step("Setup");
    811     let mut ctx = BtcCtx::setup(&tctx, "taler_btc.conf", false).await;
    812     ctx.step("Handle reorg conflicting incoming credit");
    813     step!(ctx, {
    814         let amount = Amount::from_sat(420000);
    815         ctx.credit(amount, &Base32::rand()).await;
    816         ctx.next_conf().await;
    817         ctx.expect_wire_balance(amount, true).await;
    818     });
    819 
    820     drop(ctx);
    821     tctx.step("Setup");
    822     let mut ctx = BtcCtx::setup(&tctx, "taler_btc.conf", false).await;
    823     ctx.step("Handle reorg conflicting incoming credit");
    824     step!(ctx, {
    825         let amount = Amount::from_sat(420000);
    826         ctx.malformed_credit(&amount).await;
    827         ctx.next_conf().await;
    828         let fee = ctx.worker_cfg.bounce_fee;
    829         ctx.expect_wire_balance(fee, true).await;
    830     });
    831 }
    832 
    833 /// Test btc-wire ability to learn and protect itself from blockchain behavior
    834 pub async fn analysis(ctx: TestCtx) {
    835     ctx.step("Setup");
    836     let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false).await;
    837 
    838     ctx.step("Learn from reorg");
    839 
    840     // Loose second bitcoin node
    841     ctx.cluster_deco().await;
    842 
    843     // Perform credit
    844     let before = ctx.wire_balance().await;
    845     ctx.credit(Amount::from_sat(42000), &Base32::rand()).await;
    846     ctx.next_conf().await;
    847     let after = ctx.wire_balance().await;
    848 
    849     // Perform fork and check btc-wire hard error
    850     ctx.expect_gateway_up().await;
    851     ctx.cluster_fork().await;
    852     ctx.expect_wire_balance(before, false).await;
    853     ctx.expect_gateway_down().await;
    854 
    855     // Recover orphaned transaction
    856     ctx.next_conf().await;
    857     ctx.next_block().await; // Conf have changed
    858     ctx.expect_wire_balance(after, false).await;
    859     ctx.expect_gateway_up().await;
    860 
    861     // Loose second bitcoin node
    862     ctx.cluster_deco().await;
    863 
    864     // Perform credit
    865     let before = ctx.wire_balance().await;
    866     ctx.credit(Amount::from_sat(42000), &Base32::rand()).await;
    867     ctx.next_conf().await;
    868 
    869     // Perform fork and check btc-wire learned from previous attack
    870     ctx.expect_gateway_up().await;
    871     ctx.cluster_fork().await;
    872     ctx.expect_wire_balance(before, false).await;
    873     ctx.expect_gateway_up().await;
    874 }
    875 
    876 /// Test btc-wire ability to handle stuck transaction correctly
    877 pub async fn bumpfee(tctx: TestCtx) {
    878     tctx.step("Setup");
    879     let mut ctx = BtcCtx::setup(&tctx, "taler_btc_bump.conf", false).await;
    880 
    881     // Perform credits to allow wire to perform debits latter
    882     for n in 10..13 {
    883         ctx.credit(Amount::from_sat(n * 100000), &Base32::rand())
    884             .await;
    885         ctx.next_block().await;
    886     }
    887     ctx.next_conf().await;
    888 
    889     ctx.step("Bump fee");
    890     {
    891         // Perform debit
    892         let mut client = ctx.client_balance().await;
    893         let wire = ctx.wire_balance().await;
    894         let amount = Amount::from_sat(40000);
    895         ctx.debit(amount, &Base32::rand()).await;
    896         retry! { ctx.wire_balance().await < wire };
    897 
    898         // Bump min relay fee making the previous debit stuck
    899         ctx.restart_node(&["-minrelaytxfee=0.0001"]).await;
    900 
    901         // Check bump happen
    902         client += amount;
    903         ctx.expect_client_balance(client, true).await;
    904     }
    905 
    906     ctx.step("Bump fee reorg");
    907     {
    908         // Loose second bitcoin node
    909         ctx.cluster_deco().await;
    910 
    911         // Perform debit
    912         let mut client = ctx.client_balance().await;
    913         let wire = ctx.wire_balance().await;
    914         let amount = Amount::from_sat(40000);
    915         ctx.debit(amount, &Base32::rand()).await;
    916         retry! { ctx.wire_balance().await < wire };
    917 
    918         // Bump min relay fee and fork making the previous debit stuck and problematic
    919         ctx.cluster_fork().await;
    920         ctx.restart_node(&["-minrelaytxfee=0.0001"]).await;
    921 
    922         // Check bump happen
    923         client += amount;
    924         ctx.expect_client_balance(client, true).await;
    925     }
    926     ctx.step("Setup");
    927     drop(ctx);
    928     let mut ctx = BtcCtx::setup(&tctx, "taler_btc_bump.conf", true).await;
    929 
    930     // Perform credits to allow wire to perform debits latter
    931     for n in 10..61 {
    932         ctx.credit(Amount::from_sat(n * 100000), &Base32::rand())
    933             .await;
    934         ctx.next_block().await;
    935     }
    936     ctx.next_conf().await;
    937 
    938     ctx.step("Bump fee stress");
    939     {
    940         // Loose second bitcoin node
    941         ctx.cluster_deco().await;
    942 
    943         // Perform debits
    944         let client = ctx.client_balance().await;
    945         let wire = ctx.wire_balance().await;
    946         let mut total_amount = Amount::ZERO;
    947         for n in 10..31 {
    948             let amount = Amount::from_sat(n * 10000);
    949             total_amount += amount;
    950             ctx.debit(amount, &Base32::rand()).await;
    951         }
    952         retry! { ctx.wire_balance().await < wire - total_amount };
    953 
    954         // Bump min relay fee making the previous debits stuck
    955         ctx.restart_node(&["-minrelaytxfee=0.0001"]).await;
    956 
    957         // Check bump happen
    958         ctx.expect_client_balance(client + total_amount, true).await;
    959     }
    960 }
    961 
    962 /// Test btc-wire handle transaction fees exceeding limits
    963 pub async fn maxfee(ctx: TestCtx) {
    964     ctx.step("Setup");
    965     let mut ctx = BtcCtx::setup(&ctx, "taler_btc.conf", false).await;
    966 
    967     // Perform credits to allow wire to perform debits latter
    968     for n in 10..31 {
    969         ctx.credit(Amount::from_sat(n * 100000), &Base32::rand())
    970             .await;
    971         ctx.next_block().await;
    972     }
    973     ctx.next_conf().await;
    974 
    975     let client = ctx.client_balance().await;
    976     let wire = ctx.wire_balance().await;
    977     let mut total_amount = Amount::ZERO;
    978 
    979     ctx.step("Too high fee");
    980     {
    981         // Change fee config
    982         ctx.restart_node(&["-maxtxfee=0.0000001", "-minrelaytxfee=0.0000001"])
    983             .await;
    984 
    985         // Perform debits
    986         for n in 10..31 {
    987             let amount = Amount::from_sat(n * 10000);
    988             total_amount += amount;
    989             ctx.debit(amount, &Base32::rand()).await;
    990         }
    991         ctx.mine(2).await;
    992 
    993         // Check no transaction happen
    994         ctx.expect_wire_balance(wire, false).await;
    995         ctx.expect_client_balance(client, false).await;
    996     }
    997 
    998     ctx.step("Good feed");
    999     {
   1000         // Restore default config
   1001         ctx.restart_node(&[]).await;
   1002 
   1003         // Check transaction now have been made
   1004         ctx.expect_client_balance(client + total_amount, true).await;
   1005     }
   1006 }
   1007 
   1008 /// Test btc-wire ability to configure itself from bitcoin configuration
   1009 pub async fn config(ctx: TestCtx) {
   1010     // Connect with cookie files
   1011     ctx.step("Cookie");
   1012     BtcCtx::config(
   1013         &ctx,
   1014         |btc, dir| {
   1015             btc.with_section(None::<&str>).set(
   1016                 "rpccookiefile",
   1017                 dir.join("catch_me_if_you_can").to_string_lossy(),
   1018             );
   1019         },
   1020         |cfg, dir| {
   1021             cfg.with_section(Some("depolymerizer-bitcoin-worker")).set(
   1022                 "RPC_COOKIE_FILE",
   1023                 dir.join("catch_me_if_you_can").to_string_lossy(),
   1024             );
   1025         },
   1026     )
   1027     .await;
   1028 
   1029     // Connect with password
   1030     ctx.step("Password");
   1031     BtcCtx::config(
   1032         &ctx,
   1033         |btc, _| {
   1034             btc.with_section(None::<&str>)
   1035                 .set("rpcuser", "bob")
   1036                 .set("rpcpassword", "password");
   1037         },
   1038         |cfg, _| {
   1039             cfg.with_section(Some("depolymerizer-bitcoin-worker"))
   1040                 .set("RPC_AUTH_METHOD", "basic")
   1041                 .set("RPC_USERNAME", "bob")
   1042                 .set("RPC_PASSWORD", "password");
   1043         },
   1044     )
   1045     .await;
   1046 
   1047     // Connect with token
   1048     ctx.step("Token");
   1049     BtcCtx::config(
   1050         &ctx,
   1051         |btc, _| {
   1052             btc.with_section(None::<&str>)
   1053                 .set("rpcauth", "bob:9641cec731e1fad1ded02e1d31536e44$36b8b8af0a38104997a57f017805ff56bf8963ae4a2ed40252ca0e0e070fc19e");
   1054         },
   1055         |cfg, _| {
   1056             cfg.with_section(Some("depolymerizer-bitcoin-worker"))
   1057                 .set("RPC_AUTH_METHOD", "basic")
   1058                 .set("RPC_USERNAME", "bob")
   1059                 .set("RPC_PASSWORD", "password");
   1060         },
   1061     ).await;
   1062 }