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 }