From 404cf827adf38a60811af46c8fab744a96823989 Mon Sep 17 00:00:00 2001 From: bennyhodl Date: Wed, 20 Nov 2024 14:15:07 -0500 Subject: [PATCH] async manager tests --- .github/workflows/rust.yml | 6 +- dlc-manager/Cargo.toml | 3 +- .../tests/async_manager_execution_tests.rs | 935 ++++++++++++++++++ scripts/get_test_list.sh | 27 +- 4 files changed, 965 insertions(+), 6 deletions(-) create mode 100644 dlc-manager/tests/async_manager_execution_tests.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ed5d4f4a..d50824e2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,7 +2,7 @@ on: push: branches: - master - - 'testci/**' + - "testci/**" pull_request: name: Continuous integration @@ -26,7 +26,7 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: - components: rustfmt + components: rustfmt - name: Check format run: cargo fmt --all --check no-std: @@ -68,7 +68,7 @@ jobs: key: test-cache-${{ github.run_id }}-${{ github.run_number }} - uses: actions/checkout@v4 - id: set-matrix - run: cargo test --no-run && echo "::set-output name=matrix::$(scripts/get_test_list.sh execution manager channel_execution)" + run: cargo test --all-features --no-run && cargo test --no-run && echo "::set-output name=matrix::$(scripts/get_test_list.sh manager async_manager channel_execution)" integration_tests: name: integration-tests needs: integration_tests_prepare diff --git a/dlc-manager/Cargo.toml b/dlc-manager/Cargo.toml index 48cb0101..fae08623 100644 --- a/dlc-manager/Cargo.toml +++ b/dlc-manager/Cargo.toml @@ -14,7 +14,7 @@ std = ["dlc/std", "dlc-messages/std", "dlc-trie/std", "bitcoin/std", "lightning/ fuzztarget = ["rand_chacha"] parallel = ["dlc-trie/parallel"] use-serde = ["serde", "dlc/use-serde", "dlc-messages/use-serde", "dlc-trie/use-serde"] -async = ["dep:async-trait"] +async = ["dep:async-trait", "mocks/async"] [dependencies] async-trait = { version = "0.1.50", optional = true } @@ -46,6 +46,7 @@ secp256k1-zkp = {version = "0.11.0", features = ["hashes", "rand", "rand-std", " serde = "1.0" serde_json = "1.0" simple-wallet = {path = "../simple-wallet"} +tokio = { version = "1.41.1", features = ["macros", "test-util", "rt-multi-thread"] } [[bench]] harness = false diff --git a/dlc-manager/tests/async_manager_execution_tests.rs b/dlc-manager/tests/async_manager_execution_tests.rs new file mode 100644 index 00000000..8046255d --- /dev/null +++ b/dlc-manager/tests/async_manager_execution_tests.rs @@ -0,0 +1,935 @@ +extern crate bitcoin_rpc_provider; +extern crate bitcoin_test_utils; +extern crate bitcoincore_rpc; +extern crate bitcoincore_rpc_json; +extern crate dlc_manager; + +#[macro_use] +#[allow(dead_code)] +mod test_utils; + +#[cfg(feature = "async")] +mod async_tests { + + use crate::test_utils::*; + use bitcoin::Amount; + use bitcoin_test_utils::rpc_helpers::init_clients; + use bitcoincore_rpc::RpcApi; + use dlc_manager::contract::{numerical_descriptor::DifferenceParams, Contract}; + use dlc_manager::manager::Manager; + use dlc_manager::payout_curve::PayoutFunctionPiece; + use dlc_manager::{Blockchain, Oracle, Storage, Wallet}; + use dlc_messages::oracle_msgs::OracleAttestation; + use dlc_messages::{AcceptDlc, OfferDlc, SignDlc}; + use dlc_messages::{CetAdaptorSignatures, Message}; + use electrs_blockchain_provider::ElectrsBlockchainProvider; + use lightning::ln::wire::Type; + use lightning::util::ser::Writeable; + use secp256k1_zkp::rand::{thread_rng, RngCore}; + use secp256k1_zkp::{ecdsa::Signature, EcdsaAdaptorSignature}; + use serde_json::{from_str, to_writer_pretty}; + use simple_wallet::SimpleWallet; + use std::collections::HashMap; + use std::sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::channel, + Arc, Mutex, + }; + use std::thread; + + #[derive(serde::Serialize, serde::Deserialize)] + struct TestVectorPart { + message: T, + #[cfg_attr( + feature = "use-serde", + serde( + serialize_with = "dlc_messages::serde_utils::serialize_hex", + deserialize_with = "dlc_messages::serde_utils::deserialize_hex_string" + ) + )] + serialized: Vec, + } + + #[derive(serde::Serialize, serde::Deserialize)] + struct TestVector { + offer_message: TestVectorPart, + accept_message: TestVectorPart, + sign_message: TestVectorPart, + } + + fn write_message(msg_name: &str, s: T) { + if std::env::var("GENERATE_TEST_VECTOR").is_ok() { + let mut buf = Vec::new(); + s.type_id().write(&mut buf).unwrap(); + s.write(&mut buf).unwrap(); + let t = TestVectorPart { + message: s, + serialized: buf, + }; + to_writer_pretty( + &std::fs::File::create(format!("{}.json", msg_name)).unwrap(), + &t, + ) + .unwrap(); + } + } + + fn create_test_vector() { + if std::env::var("GENERATE_TEST_VECTOR").is_ok() { + let test_vector = TestVector { + offer_message: from_str(&std::fs::read_to_string("offer_message.json").unwrap()) + .unwrap(), + accept_message: from_str(&std::fs::read_to_string("accept_message.json").unwrap()) + .unwrap(), + sign_message: from_str(&std::fs::read_to_string("sign_message.json").unwrap()) + .unwrap(), + }; + let file_name = std::env::var("TEST_VECTOR_OUTPUT_NAME") + .unwrap_or_else(|_| "test_vector.json".to_string()); + to_writer_pretty(std::fs::File::create(file_name).unwrap(), &test_vector).unwrap(); + } + } + + macro_rules! periodic_check { + ($d:expr, $id:expr, $p:ident) => { + $d.lock() + .unwrap() + .periodic_check(true) + .await + .expect("Periodic check error"); + + assert_contract_state!($d, $id, $p); + }; + } + + async fn numerical_common( + nb_oracles: usize, + threshold: usize, + payout_function_pieces_cb: F, + difference_params: Option, + manual_close: bool, + ) where + F: Fn(usize) -> Vec, + { + let oracle_numeric_infos = get_same_num_digits_oracle_numeric_infos(nb_oracles); + let with_diff = difference_params.is_some(); + let contract_descriptor = get_numerical_contract_descriptor( + oracle_numeric_infos.clone(), + payout_function_pieces_cb(*oracle_numeric_infos.nb_digits.iter().min().unwrap()), + difference_params, + ); + manager_execution_test( + get_numerical_test_params( + &oracle_numeric_infos, + threshold, + with_diff, + contract_descriptor, + false, + ), + TestPath::Close, + manual_close, + ) + .await; + } + + async fn numerical_polynomial_common( + nb_oracles: usize, + threshold: usize, + difference_params: Option, + manual_close: bool, + ) { + numerical_common( + nb_oracles, + threshold, + get_polynomial_payout_curve_pieces, + difference_params, + manual_close, + ) + .await; + } + + async fn numerical_common_diff_nb_digits( + nb_oracles: usize, + threshold: usize, + difference_params: Option, + use_max_value: bool, + manual_close: bool, + ) { + let with_diff = difference_params.is_some(); + let oracle_numeric_infos = get_variable_oracle_numeric_infos( + &(0..nb_oracles) + .map(|_| (NB_DIGITS + (thread_rng().next_u32() % 6)) as usize) + .collect::>(), + ); + let contract_descriptor = get_numerical_contract_descriptor( + oracle_numeric_infos.clone(), + get_polynomial_payout_curve_pieces(oracle_numeric_infos.get_min_nb_digits()), + difference_params, + ); + + manager_execution_test( + get_numerical_test_params( + &oracle_numeric_infos, + threshold, + with_diff, + contract_descriptor, + use_max_value, + ), + TestPath::Close, + manual_close, + ) + .await; + } + + #[derive(Eq, PartialEq, Clone)] + enum TestPath { + Close, + Refund, + BadAcceptCetSignature, + BadAcceptRefundSignature, + BadSignCetSignature, + BadSignRefundSignature, + } + + #[tokio::test] + #[ignore] + async fn single_oracle_numerical_test() { + numerical_polynomial_common(1, 1, None, false).await; + } + + #[tokio::test] + #[ignore] + async fn single_oracle_numerical_manual_test() { + numerical_polynomial_common(1, 1, None, true).await; + } + + #[tokio::test] + #[ignore] + async fn single_oracle_numerical_hyperbola_test() { + numerical_common(1, 1, get_hyperbola_payout_curve_pieces, None, false).await; + } + + #[tokio::test] + #[ignore] + async fn three_of_three_oracle_numerical_test() { + numerical_polynomial_common(3, 3, None, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_five_oracle_numerical_test() { + numerical_polynomial_common(5, 2, None, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_five_oracle_numerical_manual_test() { + numerical_polynomial_common(5, 2, None, true).await; + } + + #[tokio::test] + #[ignore] + async fn three_of_three_oracle_numerical_with_diff_test() { + numerical_polynomial_common(3, 3, Some(get_difference_params()), false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_five_oracle_numerical_with_diff_test() { + numerical_polynomial_common(5, 2, Some(get_difference_params()), false).await; + } + + #[tokio::test] + #[ignore] + async fn three_of_five_oracle_numerical_with_diff_test() { + numerical_polynomial_common(5, 3, Some(get_difference_params()), false).await; + } + + #[tokio::test] + #[ignore] + async fn three_of_five_oracle_numerical_with_diff_manual_test() { + numerical_polynomial_common(5, 3, Some(get_difference_params()), true).await; + } + + #[tokio::test] + #[ignore] + async fn enum_single_oracle_test() { + manager_execution_test(get_enum_test_params(1, 1, None), TestPath::Close, false).await; + } + + #[tokio::test] + #[ignore] + async fn enum_single_oracle_manual_test() { + manager_execution_test(get_enum_test_params(1, 1, None), TestPath::Close, true).await; + } + + #[tokio::test] + #[ignore] + async fn enum_3_of_3_test() { + manager_execution_test(get_enum_test_params(3, 3, None), TestPath::Close, false).await; + } + + #[tokio::test] + #[ignore] + async fn enum_3_of_3_manual_test() { + manager_execution_test(get_enum_test_params(3, 3, None), TestPath::Close, true).await; + } + + #[tokio::test] + #[ignore] + async fn enum_3_of_5_test() { + manager_execution_test(get_enum_test_params(5, 3, None), TestPath::Close, false).await; + } + + #[tokio::test] + #[ignore] + async fn enum_3_of_5_manual_test() { + manager_execution_test(get_enum_test_params(5, 3, None), TestPath::Close, true).await; + } + + #[tokio::test] + #[ignore] + async fn enum_and_numerical_with_diff_3_of_5_test() { + manager_execution_test( + get_enum_and_numerical_test_params(5, 3, true, Some(get_difference_params())), + TestPath::Close, + false, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_and_numerical_with_diff_3_of_5_manual_test() { + manager_execution_test( + get_enum_and_numerical_test_params(5, 3, true, Some(get_difference_params())), + TestPath::Close, + true, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_and_numerical_with_diff_5_of_5_test() { + manager_execution_test( + get_enum_and_numerical_test_params(5, 5, true, Some(get_difference_params())), + TestPath::Close, + false, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_and_numerical_with_diff_5_of_5_manual_test() { + manager_execution_test( + get_enum_and_numerical_test_params(5, 5, true, Some(get_difference_params())), + TestPath::Close, + true, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_and_numerical_3_of_5_test() { + manager_execution_test( + get_enum_and_numerical_test_params(5, 3, false, None), + TestPath::Close, + false, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_and_numerical_3_of_5_manual_test() { + manager_execution_test( + get_enum_and_numerical_test_params(5, 3, false, None), + TestPath::Close, + true, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_and_numerical_5_of_5_test() { + manager_execution_test( + get_enum_and_numerical_test_params(5, 5, false, None), + TestPath::Close, + false, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_and_numerical_5_of_5_manual_test() { + manager_execution_test( + get_enum_and_numerical_test_params(5, 5, false, None), + TestPath::Close, + true, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_single_oracle_refund_test() { + manager_execution_test( + get_enum_test_params(1, 1, Some(get_enum_oracles(1, 0))), + TestPath::Refund, + false, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_single_oracle_refund_manual_test() { + manager_execution_test( + get_enum_test_params(1, 1, Some(get_enum_oracles(1, 0))), + TestPath::Refund, + true, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_single_oracle_bad_accept_cet_sig_test() { + manager_execution_test( + get_enum_test_params(1, 1, Some(get_enum_oracles(1, 0))), + TestPath::BadAcceptCetSignature, + false, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_single_oracle_bad_accept_refund_sig_test() { + manager_execution_test( + get_enum_test_params(1, 1, Some(get_enum_oracles(1, 0))), + TestPath::BadAcceptRefundSignature, + false, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_single_oracle_bad_sign_cet_sig_test() { + manager_execution_test( + get_enum_test_params(1, 1, Some(get_enum_oracles(1, 0))), + TestPath::BadSignCetSignature, + false, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn enum_single_oracle_bad_sign_refund_sig_test() { + manager_execution_test( + get_enum_test_params(1, 1, Some(get_enum_oracles(1, 0))), + TestPath::BadSignRefundSignature, + false, + ) + .await; + } + + #[tokio::test] + #[ignore] + async fn two_of_two_oracle_numerical_diff_nb_digits_test() { + numerical_common_diff_nb_digits(2, 2, None, false, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_two_oracle_numerical_diff_nb_digits_manual_test() { + numerical_common_diff_nb_digits(2, 2, None, false, true).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_five_oracle_numerical_diff_nb_digits_test() { + numerical_common_diff_nb_digits(5, 2, None, false, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_five_oracle_numerical_diff_nb_digits_manual_test() { + numerical_common_diff_nb_digits(5, 2, None, false, true).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_two_oracle_numerical_with_diff_diff_nb_digits_test() { + numerical_common_diff_nb_digits(2, 2, Some(get_difference_params()), false, false).await; + } + + #[tokio::test] + #[ignore] + async fn three_of_three_oracle_numerical_with_diff_diff_nb_digits_test() { + numerical_common_diff_nb_digits(3, 3, Some(get_difference_params()), false, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_five_oracle_numerical_with_diff_diff_nb_digits_test() { + numerical_common_diff_nb_digits(5, 2, Some(get_difference_params()), false, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_two_oracle_numerical_with_diff_diff_nb_digits_max_value_test() { + numerical_common_diff_nb_digits(2, 2, Some(get_difference_params()), true, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_three_oracle_numerical_with_diff_diff_nb_digits_max_value_test() { + numerical_common_diff_nb_digits(3, 2, Some(get_difference_params()), true, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_five_oracle_numerical_with_diff_diff_nb_digits_max_value_test() { + numerical_common_diff_nb_digits(5, 2, Some(get_difference_params()), true, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_five_oracle_numerical_with_diff_diff_nb_digits_max_value_manual_test() { + numerical_common_diff_nb_digits(5, 2, Some(get_difference_params()), true, true).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_two_oracle_numerical_diff_nb_digits_max_value_test() { + numerical_common_diff_nb_digits(2, 2, None, true, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_three_oracle_numerical_diff_nb_digits_max_value_test() { + numerical_common_diff_nb_digits(3, 2, None, true, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_five_oracle_numerical_diff_nb_digits_max_value_test() { + numerical_common_diff_nb_digits(5, 2, None, true, false).await; + } + + #[tokio::test] + #[ignore] + async fn two_of_five_oracle_numerical_diff_nb_digits_max_value_manual_test() { + numerical_common_diff_nb_digits(5, 2, None, true, true).await; + } + + fn alter_adaptor_sig(input: &mut CetAdaptorSignatures) { + let sig_index = thread_rng().next_u32() as usize % input.ecdsa_adaptor_signatures.len(); + + let mut copy = input.ecdsa_adaptor_signatures[sig_index] + .signature + .as_ref() + .to_vec(); + let i = + thread_rng().next_u32() as usize % secp256k1_zkp::ffi::ECDSA_ADAPTOR_SIGNATURE_LENGTH; + copy[i] = copy[i].checked_add(1).unwrap_or(0); + input.ecdsa_adaptor_signatures[sig_index].signature = + EcdsaAdaptorSignature::from_slice(©).unwrap(); + } + + fn alter_refund_sig(refund_signature: &Signature) -> Signature { + let mut copy = refund_signature.serialize_compact(); + let i = thread_rng().next_u32() as usize % secp256k1_zkp::constants::COMPACT_SIGNATURE_SIZE; + copy[i] = copy[i].checked_add(1).unwrap_or(0); + Signature::from_compact(©).unwrap() + } + + async fn get_attestations(test_params: &TestParams) -> Vec<(usize, OracleAttestation)> { + for contract_info in test_params.contract_input.contract_infos.iter() { + let mut attestations = Vec::new(); + + for (i, pk) in contract_info.oracles.public_keys.iter().enumerate() { + if let Some(oracle) = test_params + .oracles + .iter() + .find(|x| x.get_public_key() == *pk) + { + if let Ok(attestation) = oracle + .get_attestation(&contract_info.oracles.event_id) + .await + { + attestations.push((i, attestation)); + } + } + } + + if attestations.len() >= contract_info.oracles.threshold as usize { + return attestations; + } + } + + panic!("No attestations found"); + } + + async fn manager_execution_test(test_params: TestParams, path: TestPath, manual_close: bool) { + env_logger::try_init().ok(); + let (alice_send, bob_receive) = channel::>(); + let (bob_send, alice_receive) = channel::>(); + let (sync_send, sync_receive) = channel::<()>(); + let alice_sync_send = sync_send.clone(); + let bob_sync_send = sync_send; + let (_, _, sink_rpc) = init_clients(); + + let mut alice_oracles = HashMap::with_capacity(1); + let mut bob_oracles = HashMap::with_capacity(1); + + for oracle in test_params.oracles.clone() { + let oracle = Arc::new(oracle); + alice_oracles.insert(oracle.get_public_key(), Arc::clone(&oracle)); + bob_oracles.insert(oracle.get_public_key(), Arc::clone(&oracle)); + } + + let alice_store = Arc::new(mocks::memory_storage_provider::MemoryStorage::new()); + let bob_store = Arc::new(mocks::memory_storage_provider::MemoryStorage::new()); + let mock_time = Arc::new(mocks::mock_time::MockTime {}); + mocks::mock_time::set_time((EVENT_MATURITY as u64) - 1); + + let electrs = Arc::new(ElectrsBlockchainProvider::new( + "http://localhost:3004/".to_string(), + bitcoin::Network::Regtest, + )); + + let alice_wallet = Arc::new(SimpleWallet::new( + electrs.clone(), + alice_store.clone(), + bitcoin::Network::Regtest, + )); + + let bob_wallet = Arc::new(SimpleWallet::new( + electrs.clone(), + bob_store.clone(), + bitcoin::Network::Regtest, + )); + + let alice_fund_address = alice_wallet.get_new_address().unwrap(); + let bob_fund_address = bob_wallet.get_new_address().unwrap(); + + sink_rpc + .send_to_address( + &alice_fund_address, + Amount::from_btc(2.0).unwrap(), + None, + None, + None, + None, + None, + None, + ) + .unwrap(); + + sink_rpc + .send_to_address( + &bob_fund_address, + Amount::from_btc(2.0).unwrap(), + None, + None, + None, + None, + None, + None, + ) + .unwrap(); + + let generate_blocks = |nb_blocks: u64| { + let prev_blockchain_height = electrs.get_blockchain_height().unwrap(); + + let sink_address = sink_rpc + .get_new_address(None, None) + .expect("RPC Error") + .assume_checked(); + sink_rpc + .generate_to_address(nb_blocks, &sink_address) + .expect("RPC Error"); + + // Wait for electrs to have processed the new blocks + let mut cur_blockchain_height = prev_blockchain_height; + while cur_blockchain_height < prev_blockchain_height + nb_blocks { + std::thread::sleep(std::time::Duration::from_millis(200)); + cur_blockchain_height = electrs.get_blockchain_height().unwrap(); + } + }; + + generate_blocks(6); + + refresh_wallet(&alice_wallet, 200000000); + refresh_wallet(&bob_wallet, 200000000); + + let alice_manager = Arc::new(Mutex::new( + Manager::new( + Arc::clone(&alice_wallet), + Arc::clone(&alice_wallet), + Arc::clone(&electrs), + alice_store, + alice_oracles, + Arc::clone(&mock_time), + Arc::clone(&electrs), + ) + .unwrap(), + )); + + let alice_manager_loop = Arc::clone(&alice_manager); + let alice_manager_send = Arc::clone(&alice_manager); + + let bob_manager = Arc::new(Mutex::new( + Manager::new( + Arc::clone(&bob_wallet), + Arc::clone(&bob_wallet), + Arc::clone(&electrs), + bob_store, + bob_oracles, + Arc::clone(&mock_time), + Arc::clone(&electrs), + ) + .unwrap(), + )); + + let bob_manager_loop = Arc::clone(&bob_manager); + let bob_manager_send = Arc::clone(&bob_manager); + let alice_send_loop = alice_send.clone(); + let bob_send_loop = bob_send.clone(); + + let alice_expect_error = Arc::new(AtomicBool::new(false)); + let bob_expect_error = Arc::new(AtomicBool::new(false)); + + let alice_expect_error_loop = alice_expect_error.clone(); + let bob_expect_error_loop = bob_expect_error.clone(); + + let path_copy = path.clone(); + let alter_sign = move |msg| match msg { + Message::Sign(mut sign_dlc) => { + match path_copy { + TestPath::BadSignCetSignature => { + alter_adaptor_sig(&mut sign_dlc.cet_adaptor_signatures) + } + TestPath::BadSignRefundSignature => { + sign_dlc.refund_signature = alter_refund_sig(&sign_dlc.refund_signature); + } + _ => {} + } + Some(Message::Sign(sign_dlc)) + } + _ => Some(msg), + }; + + let msg_callback = |msg: &Message| { + if let Message::Sign(s) = msg { + write_message("sign_message", s.clone()); + } + }; + + let alice_handle = receive_loop!( + alice_receive, + alice_manager_loop, + alice_send_loop, + alice_expect_error_loop, + alice_sync_send, + Some, + msg_callback + ); + + let bob_handle = receive_loop!( + bob_receive, + bob_manager_loop, + bob_send_loop, + bob_expect_error_loop, + bob_sync_send, + alter_sign, + msg_callback + ); + + let offer_msg = bob_manager_send + .lock() + .unwrap() + .send_offer( + &test_params.contract_input, + "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166" + .parse() + .unwrap(), + ) + .await + .expect("Send offer error"); + + write_message("offer_message", offer_msg.clone()); + let temporary_contract_id = offer_msg.temporary_contract_id; + bob_send.send(Some(Message::Offer(offer_msg))).unwrap(); + + assert_contract_state!(bob_manager_send, temporary_contract_id, Offered); + + sync_receive.recv().expect("Error synchronizing"); + + assert_contract_state!(alice_manager_send, temporary_contract_id, Offered); + + let (contract_id, _, mut accept_msg) = alice_manager_send + .lock() + .unwrap() + .accept_contract_offer(&temporary_contract_id) + .expect("Error accepting contract offer"); + + write_message("accept_message", accept_msg.clone()); + + assert_contract_state!(alice_manager_send, contract_id, Accepted); + + match path { + TestPath::BadAcceptCetSignature | TestPath::BadAcceptRefundSignature => { + match path { + TestPath::BadAcceptCetSignature => { + alter_adaptor_sig(&mut accept_msg.cet_adaptor_signatures) + } + TestPath::BadAcceptRefundSignature => { + accept_msg.refund_signature = + alter_refund_sig(&accept_msg.refund_signature); + } + _ => {} + }; + bob_expect_error.store(true, Ordering::Relaxed); + alice_send.send(Some(Message::Accept(accept_msg))).unwrap(); + sync_receive.recv().expect("Error synchronizing"); + assert_contract_state!(bob_manager_send, temporary_contract_id, FailedAccept); + } + TestPath::BadSignCetSignature | TestPath::BadSignRefundSignature => { + alice_expect_error.store(true, Ordering::Relaxed); + alice_send.send(Some(Message::Accept(accept_msg))).unwrap(); + // Bob receives accept message + sync_receive.recv().expect("Error synchronizing"); + // Alice receives sign message + sync_receive.recv().expect("Error synchronizing"); + assert_contract_state!(alice_manager_send, contract_id, FailedSign); + } + TestPath::Close | TestPath::Refund => { + alice_send.send(Some(Message::Accept(accept_msg))).unwrap(); + sync_receive.recv().expect("Error synchronizing"); + + assert_contract_state!(bob_manager_send, contract_id, Signed); + + // Should not change state and should not error + periodic_check!(bob_manager_send, contract_id, Signed); + + sync_receive.recv().expect("Error synchronizing"); + + assert_contract_state!(alice_manager_send, contract_id, Signed); + + generate_blocks(6); + + periodic_check!(alice_manager_send, contract_id, Confirmed); + periodic_check!(bob_manager_send, contract_id, Confirmed); + + if !manual_close { + mocks::mock_time::set_time((EVENT_MATURITY as u64) + 1); + } + + // Select the first one to close or refund randomly + let (first, second) = if thread_rng().next_u32() % 2 == 0 { + (alice_manager_send, bob_manager_send) + } else { + (bob_manager_send, alice_manager_send) + }; + + match path { + TestPath::Close => { + let case = thread_rng().next_u64() % 3; + let blocks: Option = if case == 2 { + Some(6) + } else if case == 1 { + Some(1) + } else { + None + }; + + if manual_close { + periodic_check!(first, contract_id, Confirmed); + + let attestations = get_attestations(&test_params).await; + + let f = first.lock().unwrap(); + let contract = f + .close_confirmed_contract(&contract_id, attestations) + .expect("Error closing contract"); + + if let Contract::PreClosed(contract) = contract { + let mut s = second.lock().unwrap(); + let second_contract = + s.get_store().get_contract(&contract_id).unwrap().unwrap(); + if let Contract::Confirmed(signed) = second_contract { + s.on_counterparty_close( + &signed, + contract.signed_cet, + blocks.unwrap_or(0), + ) + .expect("Error registering counterparty close"); + } else { + panic!("Invalid contract state: {:?}", second_contract); + } + } else { + panic!("Invalid contract state {:?}", contract); + } + } else { + periodic_check!(first, contract_id, PreClosed); + } + + // mine blocks for the CET to be confirmed + if let Some(b) = blocks { + generate_blocks(b as u64); + } + + // Randomly check with or without having the CET mined + if case == 2 { + // cet becomes fully confirmed to blockchain + periodic_check!(first, contract_id, Closed); + periodic_check!(second, contract_id, Closed); + } else { + periodic_check!(first, contract_id, PreClosed); + periodic_check!(second, contract_id, PreClosed); + } + } + TestPath::Refund => { + periodic_check!(first, contract_id, Confirmed); + + periodic_check!(second, contract_id, Confirmed); + + mocks::mock_time::set_time( + ((EVENT_MATURITY + dlc_manager::manager::REFUND_DELAY) as u64) + 1, + ); + + generate_blocks(10); + + periodic_check!(first, contract_id, Refunded); + + // Randomly check with or without having the Refund mined. + if thread_rng().next_u32() % 2 == 0 { + generate_blocks(1); + } + + periodic_check!(second, contract_id, Refunded); + } + _ => unreachable!(), + } + } + } + + alice_send.send(None).unwrap(); + bob_send.send(None).unwrap(); + + alice_handle.join().unwrap(); + bob_handle.join().unwrap(); + + create_test_vector(); + } +} diff --git a/scripts/get_test_list.sh b/scripts/get_test_list.sh index a2cfe62c..92002099 100755 --- a/scripts/get_test_list.sh +++ b/scripts/get_test_list.sh @@ -1,9 +1,32 @@ #!/bin/bash +# for TEST_PREFIX in "$@"; do +# TEST_BIN=$(ls ./target/debug/deps/${TEST_PREFIX}* | grep -v '\.d\|\.o') +# echo $TEST_BIN +# LIST=$(${TEST_BIN} --list --format=terse | sed 's/\: test$/,/' | sed 's@[^[:space:],]\+@"'${TEST_BIN}' &"@g') +# RES+=(${LIST}) +# done +# Initialize an empty array to store test names +RES=() + +# Loop over each TEST_PREFIX passed as arguments for TEST_PREFIX in "$@"; do + # Find the matching binaries (ensure we have only one binary in TEST_BIN) TEST_BIN=$(ls ./target/debug/deps/${TEST_PREFIX}* | grep -v '\.d\|\.o') - LIST=$(${TEST_BIN} --list --format=terse | sed 's/\: test$/,/' | sed 's@[^[:space:],]\+@"'${TEST_BIN}' &"@g') - RES+=(${LIST}) + + # Check if TEST_BIN is empty or contains multiple files + if [ -z "$TEST_BIN" ]; then + echo "No matching binaries found for prefix: $TEST_PREFIX" + continue + fi + + # Iterate over each binary and get the test list + for BIN in $TEST_BIN; do + # Get the list of tests, format them using sed, and append to RES array + LIST=$($BIN --list --format=terse | sed 's/\: test$/,/' | sed 's@[^[:space:],]\+@"'${BIN}' &"@g') + # Append the list of tests to the RES array + RES+=($LIST) + done done echo $(echo [${RES[@]}] | sed 's/,]/]/')