depolymerization

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

commit a8ef065e84b5628db961eb87fc54822cb242db19
parent 8e866a37cbfbea19b7da6c062fea493efa26acdc
Author: Antoine A <>
Date:   Wed, 17 Nov 2021 15:12:15 +0100

Make integration test run on testnet

Diffstat:
Msrc/bin/test.rs | 161++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/lib.rs | 16++++++++++++----
Msrc/main.rs | 2+-
3 files changed, 144 insertions(+), 35 deletions(-)

diff --git a/src/bin/test.rs b/src/bin/test.rs @@ -1,12 +1,13 @@ use core::panic; -use std::panic::AssertUnwindSafe; +use std::{collections::HashSet, panic::AssertUnwindSafe}; use bitcoincore_rpc::{ - bitcoin::{Address, Amount}, + bitcoin::{Address, Amount, Txid}, + json::GetTransactionResultDetailCategory as Category, Client, RpcApi, }; use depolymerization::{ - rpc::{common_rpc, dirty_guess_network, last_transaction, wallet_rpc, Network, CLIENT, WIRE}, + rpc::{common_rpc, dirty_guess_network, network_dir_path, wallet_rpc, Network, CLIENT, WIRE}, utils::rand_key, ClientExtended, }; @@ -18,73 +19,173 @@ use owo_colors::OwoColorize; /// As we test against an bitcoin JSON-RPC server and ordering of transaction is important /// we want our tests to run sequentially and share commons rpc connections. pub fn main() { - let network = dirty_guess_network(); + let test_amount = Amount::from_sat(420); + // Network check + let network = dirty_guess_network(); match network { Network::MainNet => { panic!("Do not run tests on the mainnet, you are going to loose money") } - Network::TestNet => println!("Running on testnet, slow network mining"), + Network::TestNet => println!("{}", "Running on testnet, slow network mining".yellow()), Network::RegTest => println!("Running on regtest, fast manual mining"), } - let rpc = common_rpc(network).unwrap(); - rpc.load_wallet(&WIRE).ok(); - rpc.load_wallet(&CLIENT).ok(); + + // Wallet check + { + let existing_wallets: HashSet<String> = + std::fs::read_dir(network_dir_path(network).join("wallets")) + .unwrap() + .filter_map(|it| it.ok()) + .map(|it| it.file_name().to_string_lossy().to_string()) + .collect(); + + let rpc = common_rpc(network).expect("Failed to open common client"); + if !existing_wallets.contains(CLIENT) || !existing_wallets.contains(WIRE) { + println!("Generate tests wallets"); + // Create wallets + rpc.create_wallet(&WIRE, None, None, None, None).unwrap(); + rpc.create_wallet(&CLIENT, None, None, None, None).unwrap(); + } + + // Load wallets + rpc.load_wallet(&WIRE).ok(); + rpc.load_wallet(&CLIENT).ok(); + } + + // Client initialization let client_rpc = wallet_rpc(network, CLIENT); let wire_rpc = wallet_rpc(network, WIRE); let client_addr = client_rpc.get_new_address(None, None).unwrap(); let wire_addr = wire_rpc.get_new_address(None, None).unwrap(); - println!( - "Initial state:\n{} {}\n{} {}", - WIRE, - wire_rpc.get_balance(None, None).unwrap(), - CLIENT, - client_rpc.get_balance(None, None).unwrap() - ); + + // Balance check + { + let balance = client_rpc.get_balance(None, None).unwrap(); + let min_balance = test_amount * 3; + if balance < min_balance { + println!( + "{}", + format_args!( + "Client wallet have only {}, {} are required to perform the test", + balance, min_balance + ) + .yellow() + ); + match network { + Network::MainNet | Network::TestNet => { + if network == Network::MainNet { + println!("Send coins to this address: {}", client_addr); + } else { + println!("Request coins from a faucet such as https://bitcoinfaucet.uo1.net/send.php to this address: {}", client_addr); + } + println!("Waiting for the transaction..."); + while client_rpc.get_balance(None, None).unwrap() < min_balance { + client_rpc.wait_for_new_block(0).ok(); + } + } + Network::RegTest => { + println!("Add 50B to client wallet"); + client_rpc + .generate_to_address( + 101, /* Need 100 blocks to validate */ + &client_addr, + ) + .unwrap(); + } + } + } + + println!( + "Initial state:\n{} {}\n{} {}", + WIRE, + wire_rpc.get_balance(None, None).unwrap(), + CLIENT, + client_rpc.get_balance(None, None).unwrap() + ); + } run_test("OpReturn metadata", || { + // Send metadata let msg = "J'aime le chocolat".as_bytes(); - client_rpc - .send_op_return(&wire_addr, Amount::from_sat(4200), msg) + let id = client_rpc + .send_op_return(&wire_addr, test_amount, msg) .unwrap(); + // Check in mempool + assert!( + tx_exist(&client_rpc, &id, 0, Category::Send).unwrap(), + "Not in mempool" + ); + // Check mined next_block(network, &client_rpc, &client_addr); - let last = last_transaction(&wire_rpc).unwrap(); - let (_, decoded) = wire_rpc.get_tx_op_return(&last).unwrap(); - assert_eq!(&msg, &decoded.as_slice()); + assert!( + tx_exist(&wire_rpc, &id, 1, Category::Receive).unwrap(), + "Not mined" + ); + // Check extract + let (_, extracted) = wire_rpc.get_tx_op_return(&id).unwrap(); + assert_eq!(msg, extracted, "Corrupted metadata"); }); run_test("SegWit metadata", || { + // Send metadata let key = rand_key(); - client_rpc - .send_segwit_key(&wire_addr, Amount::from_sat(4200), &key) + let id = client_rpc + .send_segwit_key(&wire_addr, test_amount, &key) .unwrap(); + // Check in mempool + assert!( + tx_exist(&client_rpc, &id, 0, Category::Send).unwrap(), + "Not in mempool" + ); + // Check mined next_block(network, &client_rpc, &client_addr); - let last = last_transaction(&wire_rpc).unwrap(); - let (_, decoded) = wire_rpc.get_tx_segwit_key(&last).unwrap(); - assert_eq!(key, decoded); + assert!( + tx_exist(&wire_rpc, &id, 1, Category::Receive).unwrap(), + "Not mined" + ); + // Check extract + let (_, extracted) = wire_rpc.get_tx_segwit_key(&id).unwrap(); + assert_eq!(key, extracted, "Corrupted metadata"); }); } fn next_block(network: Network, rpc: &Client, address: &Address) { match network { - Network::TestNet => { + Network::RegTest => { // Manually mine a block rpc.generate_to_address(1, address).unwrap(); } _ => { // Wait for the next block - rpc.wait_for_new_block(60 * 60 * 1000).unwrap(); + rpc.wait_for_new_block(0).ok(); } } } fn run_test(name: &str, test: impl FnOnce() -> ()) -> bool { - println!("{}", name.cyan()); + println!("{}", format_args!("{} start", name).cyan()); let result = std::panic::catch_unwind(AssertUnwindSafe(test)); if result.is_ok() { - println!("{}", "Ok".green()) + println!("{}", format_args!("{} OK", name).green()) } else { - println!("{}", "Err".red()) + dbg!(&result); + println!("{}", format_args!("{} ERR", name).red()) } return result.is_ok(); } + +fn tx_exist( + rpc: &Client, + id: &Txid, + min_confirmation: i32, + detail: Category, +) -> bitcoincore_rpc::Result<bool> { + let txs = rpc.list_transactions(None, None, None, None).unwrap(); + let found = txs.into_iter().any(|tx| { + tx.detail.category == detail + && tx.info.confirmations >= min_confirmation + && tx.info.txid == *id + }); + Ok(found) +} diff --git a/src/lib.rs b/src/lib.rs @@ -320,10 +320,9 @@ pub mod rpc { pub const CLIENT: &str = "client"; pub const WIRE: &str = "wire"; - pub const RPC_URL: &str = "http://localhost:18443"; /// Bitcoin networks - #[derive(Debug, Clone, Copy)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Network { MainNet, TestNet, @@ -340,6 +339,15 @@ pub mod rpc { } } + pub fn rpc_url(network: Network) -> String { + let port = match network { + Network::MainNet => 8332, + Network::TestNet => 18332, + Network::RegTest => 18443, + }; + format!("https://127.0.0.1:{}", port) + } + pub fn data_dir_path() -> PathBuf { // https://github.com/bitcoin/bitcoin/blob/master/doc/bitcoin-conf.md#:~:text=By%20default%2C%20the%20configuration%20file,file%3E%20option%20in%20the%20bitcoin. if cfg!(target_os = "windows") { @@ -365,14 +373,14 @@ pub mod rpc { pub fn common_rpc(network: Network) -> bitcoincore_rpc::Result<Client> { Client::new( - RPC_URL, + &rpc_url(network), Auth::CookieFile(network_dir_path(network).join(".cookie")), ) } pub fn wallet_rpc(network: Network, wallet: &str) -> Client { Client::new( - &format!("{}/wallet/{}", RPC_URL, wallet), + &format!("{}/wallet/{}", rpc_url(network), wallet), Auth::CookieFile(network_dir_path(network).join(".cookie")), ) .expect(&format!("Failed to open wallet '{}' client", wallet)) diff --git a/src/main.rs b/src/main.rs @@ -214,7 +214,7 @@ fn main() { println!("Receive message using {}", method.to_string()); let (_, mut hash) = received_since(&wire_rpc, None).unwrap(); loop { - wire_rpc.wait_for_new_block(60 * 60 * 1000).ok(); + wire_rpc.wait_for_new_block(0).ok(); println!("new block"); let (ids, nhash) = received_since(&wire_rpc, Some(&hash)).unwrap(); hash = nhash;