diff --git a/Cargo.lock b/Cargo.lock index e7fe6a4a47..2e2a6318a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8034,6 +8034,7 @@ dependencies = [ "minotari_wallet_ffi", "minotari_wallet_grpc_client", "rand", + "regex", "reqwest", "serde_json", "tari_common", diff --git a/applications/minotari_merge_mining_proxy/src/proxy.rs b/applications/minotari_merge_mining_proxy/src/proxy.rs index 5d95452c81..b6a5ded9cb 100644 --- a/applications/minotari_merge_mining_proxy/src/proxy.rs +++ b/applications/minotari_merge_mining_proxy/src/proxy.rs @@ -1141,7 +1141,7 @@ mod test { "id": "0", "method": rpc_method, "params": { - "wallet_address": "489r43gR8bDMJNBf4Q6sL9CNERvZQrTqjRCSESqgWQEWWq2UGAfj2voaw3zBtD7U8CQ391Nc1PDHUHiN85yhbZnCDasqzyX", + "wallet_address": "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A", } }), Method::GetVersion => json!({ diff --git a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs index 571c77f464..54cf88eecf 100644 --- a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs +++ b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs @@ -57,10 +57,13 @@ const LOG_TARGET: &str = "minotari_mm_proxy::proxy"; #[allow(clippy::too_many_lines)] pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { + trace!(target: LOG_TARGET, "{:?}", cli); let config_path = cli.common.config_path(); let cfg = load_configuration(&config_path, true, cli.non_interactive_mode, &cli, cli.common.network)?; + trace!(target: LOG_TARGET, "{:?}", cfg); let mut config = MergeMiningProxyConfig::load_from(&cfg)?; config.set_base_path(cli.common.get_base_path()); + trace!(target: LOG_TARGET, "{:?}", config); // Get reputable monerod URLs let mut assigned_dynamic_fail = false; diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index 38a86efbfc..2c35fde72d 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -49,6 +49,7 @@ thiserror = "^1.0.20" time = "0.3.36" tokio = { version = "1.36", features = ["macros", "time", "sync", "rt-multi-thread"] } tonic = "0.12.3" +regex = "1.11.0" [package.metadata.cargo-machete] ignored = ["minotari_wallet_ffi", "minotari_chat_ffi"] diff --git a/integration_tests/README.md b/integration_tests/README.md index 2da1ce5eca..9fb62336f4 100644 --- a/integration_tests/README.md +++ b/integration_tests/README.md @@ -45,9 +45,10 @@ In its simplest form you can run the tests from the project route with `cargo te ## Concurrency -- The set of wallet FFI tests (`"@wallet-ffi and not @broken"`) should not be run with `--concurrency` greater than 1, - and because it defaults to 64, it should be limited to `--concurrency 1` on the command line. This is also true for - running it in CI. +- The set of wallet FFI tests (`"@wallet-ffi and not @broken"`) should not be run with `--concurrency` greater than 1. + We coded `.max_concurrent_scenarios(1)`, as concurrency defaults to 64, but it can also be limited to + `--concurrency 1` on the command line. This is also true for running it in CI. + **Note:** Specifying `--concurrency X` on the command line overrides `max_concurrent_scenarios(1)` set in the code. ```shell cargo test --release --test cucumber -- --tags "@wallet-ffi and not @broken" --concurrency 1 --retry 2 diff --git a/integration_tests/log4rs/cucumber.yml b/integration_tests/log4rs/cucumber.yml index e015173db8..bf8042aa82 100644 --- a/integration_tests/log4rs/cucumber.yml +++ b/integration_tests/log4rs/cucumber.yml @@ -145,6 +145,22 @@ appenders: pattern: "{{log_dir}}/log/libp2p.{}.log" encoder: pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} {f}.{L} {i} [{t}] {l:5} {m}{n}" + # An appender named "cucumber_detail" that writes to a file with a custom pattern encoder + cucumber_detail: + kind: rolling_file + path: "{{log_dir}}/log/cucumber_detail.log" + policy: + kind: compound + trigger: + kind: size + limit: 100mb + roller: + kind: fixed_window + base: 1 + count: 5 + pattern: "{{log_dir}}/log/cucumber_detail.{}.log" + encoder: + pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} {f}.{L} {i} [{t}] {l:5} {m}{n}" # We don't want prints during cucumber test, everything useful will in logs. # root: @@ -167,6 +183,10 @@ loggers: level: info # we have only single print, and it's info appenders: - stdout + cucumber_detail: + level: debug + appenders: + - cucumber_detail c: level: debug #trace #debug appenders: @@ -210,7 +230,7 @@ loggers: # merge mining proxy minotari_mm_proxy::proxy: - level: debug + level: debug #trace #debug appenders: - proxy additive: false diff --git a/integration_tests/src/base_node_process.rs b/integration_tests/src/base_node_process.rs index 4a66a956a0..6762725d4c 100644 --- a/integration_tests/src/base_node_process.rs +++ b/integration_tests/src/base_node_process.rs @@ -47,7 +47,7 @@ use tonic::transport::Channel; use crate::{get_peer_seeds, get_port, wait_for_service, ServiceType, TariWorld}; -const LOG_TARGET: &str = "cucumber::bas_node_process"; +const LOG_TARGET: &str = "cucumber_detail::base_node_process"; #[derive(Clone)] pub struct BaseNodeProcess { diff --git a/integration_tests/src/ffi/contacts_liveness_data.rs b/integration_tests/src/ffi/contacts_liveness_data.rs index e29e88d125..60aa1d6ee5 100644 --- a/integration_tests/src/ffi/contacts_liveness_data.rs +++ b/integration_tests/src/ffi/contacts_liveness_data.rs @@ -56,7 +56,7 @@ impl ContactsLivenessData { WalletAddress::from_ptr(ptr) } - pub fn get_latency(&self) -> i32 { + pub fn get_latency(&self) -> u32 { let latency; let mut error = 0; unsafe { diff --git a/integration_tests/src/ffi/ffi_import.rs b/integration_tests/src/ffi/ffi_import.rs index da125d9040..0be928d7a8 100644 --- a/integration_tests/src/ffi/ffi_import.rs +++ b/integration_tests/src/ffi/ffi_import.rs @@ -29,33 +29,24 @@ pub type TariPendingInboundTransaction = c_void; pub type TariCompletedTransaction = c_void; pub type TariTransactionSendStatus = c_void; pub type TariContactsLivenessData = c_void; +pub type TariRangeProof = c_void; pub type TariBalance = c_void; pub type TariWallet = c_void; pub type TariWalletAddress = c_void; pub type ByteVector = c_void; -#[allow(dead_code)] pub type TariFeePerGramStat = c_void; -#[allow(dead_code)] pub type TariTypeTag = c_void; pub type TariVector = c_void; pub type TariCoinPreview = c_void; pub type TariTransactionKernel = c_void; pub type TariPublicKey = c_void; -#[allow(dead_code)] pub type TariPublicKeys = c_void; -#[allow(dead_code)] pub type TariPrivateKey = c_void; -#[allow(dead_code)] pub type TariComAndPubSignature = c_void; -#[allow(dead_code)] pub type TariOutputFeatures = c_void; -#[allow(dead_code)] pub type TariCovenant = c_void; -#[allow(dead_code)] pub type TariEncryptedOpenings = c_void; -#[allow(dead_code)] pub type TariUnblindedOutput = c_void; -#[allow(dead_code)] pub type TariUnblindedOutputs = c_void; pub type TariContact = c_void; pub type TariContacts = c_void; @@ -63,13 +54,11 @@ pub type TariCompletedTransactions = c_void; pub type TariPendingOutboundTransactions = c_void; pub type TariPendingOutboundTransaction = c_void; pub type TariPendingInboundTransactions = c_void; -#[allow(dead_code)] pub type TariUtxoSort = c_void; -#[allow(dead_code)] pub type EmojiSet = c_void; -#[allow(dead_code)] pub type TariFeePerGramStats = c_void; pub type TariBaseNodeState = c_void; +pub type ContactsServiceHandle = c_void; #[cfg_attr(windows, link(name = "minotari_wallet_ffi.dll"))] #[cfg_attr(not(windows), link(name = "minotari_wallet_ffi"))] @@ -107,11 +96,6 @@ extern "C" { pub fn tari_address_create(bytes: *mut ByteVector, error_out: *mut c_int) -> *mut TariWalletAddress; pub fn tari_address_destroy(address: *mut TariWalletAddress); pub fn tari_address_get_bytes(address: *mut TariWalletAddress, error_out: *mut c_int) -> *mut ByteVector; - pub fn tari_address_from_private_key( - secret_key: *mut TariPrivateKey, - network: c_uint, - error_out: *mut c_int, - ) -> *mut TariWalletAddress; pub fn tari_address_from_base58(address: *const c_char, error_out: *mut c_int) -> *mut TariWalletAddress; pub fn tari_address_to_emoji_id(address: *mut TariWalletAddress, error_out: *mut c_int) -> *mut c_char; pub fn emoji_id_to_tari_address(emoji: *const c_char, error_out: *mut c_int) -> *mut TariWalletAddress; @@ -137,6 +121,7 @@ extern "C" { encrypted_data: *mut TariEncryptedOpenings, minimum_value_promise: c_ulonglong, script_lock_height: c_ulonglong, + range_proof: *mut TariRangeProof, error_out: *mut c_int, ) -> *mut TariUnblindedOutput; pub fn tari_unblinded_output_destroy(output: *mut TariUnblindedOutput); @@ -176,6 +161,7 @@ extern "C" { output_type: c_ushort, maturity: c_ulonglong, metadata: *const ByteVector, + range_proof_type: c_ushort, error_out: *mut c_int, ) -> *mut TariOutputFeatures; pub fn output_features_destroy(output_features: *mut TariOutputFeatures); @@ -186,7 +172,12 @@ extern "C" { ) -> *mut TariSeedWords; pub fn seed_words_get_length(seed_words: *const TariSeedWords, error_out: *mut c_int) -> c_uint; pub fn seed_words_get_at(seed_words: *mut TariSeedWords, position: c_uint, error_out: *mut c_int) -> *mut c_char; - pub fn seed_words_push_word(seed_words: *mut TariSeedWords, word: *const c_char, error_out: *mut c_int) -> c_uchar; + pub fn seed_words_push_word( + seed_words: *mut TariSeedWords, + word: *const c_char, + passphrase: *const c_char, + error_out: *mut c_int, + ) -> c_uchar; pub fn seed_words_destroy(seed_words: *mut TariSeedWords); pub fn contact_create( alias: *const c_char, @@ -204,7 +195,7 @@ extern "C" { liveness_data: *mut TariContactsLivenessData, error_out: *mut c_int, ) -> *mut TariWalletAddress; - pub fn liveness_data_get_latency(liveness_data: *mut TariContactsLivenessData, error_out: *mut c_int) -> c_int; + pub fn liveness_data_get_latency(liveness_data: *mut TariContactsLivenessData, error_out: *mut c_int) -> c_uint; pub fn liveness_data_get_last_seen( liveness_data: *mut TariContactsLivenessData, error_out: *mut c_int, @@ -381,7 +372,7 @@ extern "C" { database_name: *const c_char, datastore_path: *const c_char, log_path: *const c_char, - log_level: c_int, + log_verbosity: c_int, num_rolling_log_files: c_uint, size_per_log_file_bytes: c_uint, passphrase: *const c_char, @@ -430,7 +421,7 @@ extern "C" { callback_saf_messages_received: unsafe extern "C" fn(context: *mut c_void), callback_connectivity_status: unsafe extern "C" fn(context: *mut c_void, u64), callback_wallet_scanned_height: unsafe extern "C" fn(context: *mut c_void, u64), - callback_base_node_state_updated: unsafe extern "C" fn(context: *mut c_void, *mut TariBaseNodeState), + callback_base_node_state: unsafe extern "C" fn(context: *mut c_void, *mut TariBaseNodeState), recovery_in_progress: *mut bool, error_out: *mut c_int, ) -> *mut TariWallet; @@ -508,8 +499,8 @@ extern "C" { amount: c_ulonglong, commitments: *mut TariVector, fee_per_gram: c_ulonglong, - num_kernels: c_ulonglong, - num_outputs: c_ulonglong, + num_kernels: c_uint, + num_outputs: c_uint, error_out: *mut c_int, ) -> c_ulonglong; pub fn wallet_get_num_confirmations_required(wallet: *mut TariWallet, error_out: *mut c_int) -> c_ulonglong; @@ -624,5 +615,5 @@ extern "C" { error_out: *mut c_int, ) -> c_ulonglong; pub fn fee_per_gram_stat_destroy(fee_per_gram_stat: *mut TariFeePerGramStat); - pub fn contacts_handle(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut c_void; + pub fn contacts_handle(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut ContactsServiceHandle; } diff --git a/integration_tests/src/ffi/mod.rs b/integration_tests/src/ffi/mod.rs index 19c0b0f914..cda7b4306e 100644 --- a/integration_tests/src/ffi/mod.rs +++ b/integration_tests/src/ffi/mod.rs @@ -72,3 +72,251 @@ mod fee_per_gram_stats; pub use fee_per_gram_stats::FeePerGramStats; mod fee_per_gram_stat; pub use fee_per_gram_stat::FeePerGramStat; + +#[cfg(test)] +mod tests { + use std::{collections::HashMap, fs}; + + use regex::Regex; + + #[derive(Debug)] + struct FnMap { + fn_name: String, + fn_return_type: String, + fn_args: String, + } + + fn trim_whitespace(s: &str) -> String { + let mut value = s.trim().to_string().replace("\r\n", " ").replace("\n", " "); + while value.contains(" ") { + value = value.replace(" ", " "); + } + value + } + + fn clean_lib_content(content: &str) -> (usize, String) { + let mut parsed = String::new(); + + let fn_lines = content + .lines() + .enumerate() + .filter(|(_i, line)| line.starts_with("pub unsafe extern \"C\" fn")) + .map(|(i, _line)| i) + .collect::>(); + let final_fn_line = fn_lines[fn_lines.len() - 1]; + + for (count, line) in content.lines().enumerate().skip(fn_lines[0]) { + if line.contains("mod test {") && count > final_fn_line - (fn_lines[0]) { + break; + } + let trimmed = line.trim(); + if trimmed.is_empty() || + trimmed.starts_with("#[") || + trimmed.starts_with("///") || + trimmed.starts_with("//") + { + continue; + } + parsed.push_str(trimmed); + parsed.push('\n'); + } + + let mut result = String::new(); + let mut signature_definition = false; + let mut definition_end = false; + let mut fn_count = 0; + for line in parsed.lines() { + if line.contains("pub unsafe extern") { + fn_count += 1; + signature_definition = true; + } + if signature_definition && (line.contains(") {") || line.contains(") -> ")) { + definition_end = true; + } + if signature_definition { + // Replace the '"C"' in the signature with '~C~' to make the regex easier to parse + let replaced = line + .to_string() + .replace("pub unsafe extern \"C\" fn", "pub unsafe extern ~C~ fn"); + result.push_str(replaced.as_str()); + if definition_end { + result.push('\n'); + signature_definition = false; + definition_end = false; + } else { + result.push(' '); + } + } + } + + (fn_count, result) + } + + fn clean_ffi_import_content(content: &str) -> (usize, String) { + let mut parsed = String::new(); + + let fn_lines = content + .lines() + .enumerate() + .filter(|(_i, line)| line.contains("pub fn ")) + .map(|(i, _line)| i) + .collect::>(); + + for line in content.lines().skip(fn_lines[0]) { + let trimmed = line.trim(); + if trimmed.is_empty() || + trimmed.starts_with("#[") || + trimmed.starts_with("///") || + trimmed.starts_with("//") + { + continue; + } + parsed.push_str(trimmed); + parsed.push('\n'); + } + + let mut result = String::new(); + let mut signature_definition = false; + let mut definition_end = false; + let mut fn_count = 0; + for line in parsed.lines() { + if line.contains("pub fn ") { + fn_count += 1; + signature_definition = true; + } + if signature_definition && line.contains(";") { + definition_end = true; + } + if signature_definition { + // Replace the ";" at the end of the signature with " {" to make the regex easier to parse + result.push_str(line.to_string().replace(";", " {").as_str()); + if definition_end { + result.push('\n'); + signature_definition = false; + definition_end = false; + } else { + result.push(' '); + } + } + } + + (fn_count, result) + } + + fn parse_function_signatures(content: &str, re: Regex) -> HashMap { + let mut fn_maps = HashMap::new(); + + for cap in re.captures_iter(content) { + let mut fn_map = FnMap { + fn_name: trim_whitespace(&cap["fn_name"]), + fn_return_type: trim_whitespace(cap.name("fn_return_type").map_or("", |m| m.as_str())), + fn_args: trim_whitespace(&cap["fn_args"]), + }; + if fn_map.fn_args.ends_with(",") { + fn_map.fn_args.pop(); + } + fn_map.fn_args = fn_map.fn_args.replace("( ", "("); + fn_map.fn_args = fn_map.fn_args.replace(" )", ")"); + fn_map.fn_args = fn_map.fn_args.replace(",)", ")"); + assert!(fn_maps.insert(fn_map.fn_name.clone(), fn_map).is_none()); + } + + fn_maps + } + + #[test] + fn test_ffi_import_fn_signatures() { + let ffi_lib_content = fs::read_to_string("../base_layer/wallet_ffi/src/lib.rs").unwrap(); + let (fn_count, cleaned_ffi_lib_content) = clean_lib_content(&ffi_lib_content); + // 'cleaned_ffi_lib_content' looks like: + // ------------------------------------- + // pub unsafe extern ~C~ fn create_tari_vector(tag: TariTypeTag) -> *mut TariVector { + // pub unsafe extern ~C~ fn destroy_tari_vector(v: *mut TariVector) { + // pub unsafe extern ~C~ fn destroy_tari_coin_preview(p: *mut TariCoinPreview) { + let re = Regex::new( + r"(?m)^\s*pub\s+unsafe\s+extern\s+~C~\s+fn\s+(?P\w+)\s*\((?P.*)\)\s*(->\s*(?P[^;{]+))?", + ) + .unwrap(); + let ffi_lib_fn_maps = parse_function_signatures(&cleaned_ffi_lib_content, re); + assert_eq!(fn_count, ffi_lib_fn_maps.len()); + + let ffi_import_content = fs::read_to_string("src/ffi/ffi_import.rs").unwrap(); + let (fn_count, cleaned_ffi_import_content) = clean_ffi_import_content(&ffi_import_content); + // 'cleaned_ffi_import_content' looks like: + // ---------------------------------------- + // pub fn create_tari_vector(tag: TariTypeTag) -> *mut TariVector { + // pub fn destroy_tari_vector(v: *mut TariVector) { + // pub fn destroy_tari_coin_preview(p: *mut TariCoinPreview) { + let re = Regex::new( + r"(?m)^\s*pub\s+fn\s+(?P\w+)\s*\((?P.*)\)\s*(->\s*(?P[^;{]+))?", + ) + .unwrap(); + let ffi_import_fn_maps = parse_function_signatures(&cleaned_ffi_import_content, re); + assert_eq!(fn_count, ffi_import_fn_maps.len()); + + let mut mismatches = Vec::new(); + for (fn_name, ffi_import_fn_map) in &ffi_import_fn_maps { + let ffi_lib_fn_map = match ffi_lib_fn_maps.get(fn_name) { + Some(fn_map) => fn_map, + None => { + mismatches.push(format!("Function '{}' not found in ffi_lib", fn_name)); + continue; + }, + }; + if ffi_import_fn_map.fn_return_type != ffi_lib_fn_map.fn_return_type { + mismatches.push(format!( + "Function '{}' return type mismatch:\n import: '{}'\n lib: '{}'\n", + fn_name, ffi_import_fn_map.fn_return_type, ffi_lib_fn_map.fn_return_type + )); + } + if ffi_import_fn_map.fn_args != ffi_lib_fn_map.fn_args { + mismatches.push(format!( + "Function '{}' arguments mismatch:\n import: '{}'\n lib: '{}'\n", + fn_name, ffi_import_fn_map.fn_args, ffi_lib_fn_map.fn_args + )); + } + } + + if !mismatches.is_empty() { + println!(); + for mismatch in mismatches { + println!("{}\n", mismatch); + } + println!(); + panic!("Mismatched function signatures found"); + } + + // Also verify that parsing a complex function is working correctly + let test_1 = ffi_lib_fn_maps.get("wallet_create").unwrap(); + assert_eq!(test_1.fn_return_type, "*mut TariWallet".to_string()); + assert_eq!( + test_1.fn_args, + "context: *mut c_void, config: *mut TariCommsConfig, database_name: *const c_char, datastore_path: *const \ + c_char, log_path: *const c_char, log_verbosity: c_int, num_rolling_log_files: c_uint, \ + size_per_log_file_bytes: c_uint, passphrase: *const c_char, seed_passphrase: *const c_char, seed_words: \ + *const TariSeedWords, network_str: *const c_char, dns_seeds_str: *const c_char, \ + dns_seed_name_servers_str: *const c_char, use_dns_sec: bool, callback_received_transaction: unsafe \ + extern \"C\" fn(context: *mut c_void, *mut TariPendingInboundTransaction), \ + callback_received_transaction_reply: unsafe extern \"C\" fn(context: *mut c_void, *mut \ + TariCompletedTransaction), callback_received_finalized_transaction: unsafe extern \"C\" fn(context: *mut \ + c_void, *mut TariCompletedTransaction), callback_transaction_broadcast: unsafe extern \"C\" fn(context: \ + *mut c_void, *mut TariCompletedTransaction), callback_transaction_mined: unsafe extern \"C\" fn(context: \ + *mut c_void, *mut TariCompletedTransaction), callback_transaction_mined_unconfirmed: unsafe extern \"C\" \ + fn(context: *mut c_void, *mut TariCompletedTransaction, u64), callback_faux_transaction_confirmed: \ + unsafe extern \"C\" fn(context: *mut c_void, *mut TariCompletedTransaction), \ + callback_faux_transaction_unconfirmed: unsafe extern \"C\" fn(context: *mut c_void, *mut \ + TariCompletedTransaction, u64), callback_transaction_send_result: unsafe extern \"C\" fn(context: *mut \ + c_void, c_ulonglong, *mut TariTransactionSendStatus), callback_transaction_cancellation: unsafe extern \ + \"C\" fn(context: *mut c_void, *mut TariCompletedTransaction, u64), callback_txo_validation_complete: \ + unsafe extern \"C\" fn(context: *mut c_void, u64, u64), callback_contacts_liveness_data_updated: unsafe \ + extern \"C\" fn(context: *mut c_void, *mut TariContactsLivenessData), callback_balance_updated: unsafe \ + extern \"C\" fn(context: *mut c_void, *mut TariBalance), callback_transaction_validation_complete: \ + unsafe extern \"C\" fn(context: *mut c_void, u64, u64), callback_saf_messages_received: unsafe extern \ + \"C\" fn(context: *mut c_void), callback_connectivity_status: unsafe extern \"C\" fn(context: *mut \ + c_void, u64), callback_wallet_scanned_height: unsafe extern \"C\" fn(context: *mut c_void, u64), \ + callback_base_node_state: unsafe extern \"C\" fn(context: *mut c_void, *mut TariBaseNodeState), \ + recovery_in_progress: *mut bool, error_out: *mut c_int" + .to_string() + ); + } +} diff --git a/integration_tests/src/ffi/seed_words.rs b/integration_tests/src/ffi/seed_words.rs index 883f29cbe0..ee4143b3a8 100644 --- a/integration_tests/src/ffi/seed_words.rs +++ b/integration_tests/src/ffi/seed_words.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{ffi::CString, ptr::null_mut}; +use std::{ffi::CString, ptr, ptr::null_mut}; use libc::c_void; @@ -96,7 +96,12 @@ impl SeedWords { let mut error = 0; let result; unsafe { - result = ffi_import::seed_words_push_word(self.ptr, CString::new(word).unwrap().into_raw(), &mut error); + result = ffi_import::seed_words_push_word( + self.ptr, + CString::new(word).unwrap().into_raw(), + ptr::null(), + &mut error, + ); if error > 0 { println!("seed_words_push_word error {}", error); panic!("seed_words_push_word error"); diff --git a/integration_tests/src/merge_mining_proxy.rs b/integration_tests/src/merge_mining_proxy.rs index 44d79535cd..a6c0951a4e 100644 --- a/integration_tests/src/merge_mining_proxy.rs +++ b/integration_tests/src/merge_mining_proxy.rs @@ -22,6 +22,7 @@ use std::{convert::TryInto, thread}; +use log::*; use minotari_app_utilities::common_cli_args::CommonCliArgs; use minotari_merge_mining_proxy::{merge_miner, Cli}; use minotari_wallet_grpc_client::{grpc, WalletGrpcClient}; @@ -35,6 +36,8 @@ use tonic::transport::Channel; use super::get_port; use crate::TariWorld; +const LOG_TARGET: &str = "cucumber_detail::merge_mining_process"; + #[derive(Clone, Debug)] pub struct MergeMiningProxyProcess { pub name: String, @@ -104,20 +107,6 @@ impl MergeMiningProxyProcess { "merge_mining_proxy.base_node_grpc_address".to_string(), format!("/ip4/127.0.0.1/tcp/{}", base_node_grpc_port), ), - ( - "merge_mining_proxy.monerod_url".to_string(), - [ - "http://stagenet.xmr-tw.org:38081", - "http://node.monerodevs.org:38089", - "http://node3.monerodevs.org:38089", - "http://xmr-lux.boldsuck.org:38081", - "http://singapore.node.xmr.pm:38081", - ] - .join(","), - ), - ("merge_mining_proxy.monerod_use_auth".to_string(), "false".to_string()), - ("merge_mining_proxy.monerod_username".to_string(), "".to_string()), - ("merge_mining_proxy.monerod_password".to_string(), "".to_string()), ( "merge_mining_proxy.wait_for_initial_sync_at_startup".to_string(), "false".to_string(), @@ -138,9 +127,10 @@ impl MergeMiningProxyProcess { }, non_interactive_mode: false, }; + debug!(target: LOG_TARGET, "{:?}", cli); let rt = runtime::Builder::new_multi_thread().enable_all().build().unwrap(); if let Err(e) = rt.block_on(merge_miner(cli)) { - println!("Error running merge mining proxy : {:?}", e); + println!("Error running merge mining proxy : {}", e); panic!("Error running merge mining proxy"); } }); @@ -148,12 +138,19 @@ impl MergeMiningProxyProcess { async fn get_response(&self, path: &str) -> Value { let full_address = format!("http://127.0.0.1:{}", self.port); - reqwest::get(format!("{}/{}", full_address, path)) - .await - .unwrap() - .json::() - .await - .unwrap() + match reqwest::get(format!("{}/{}", full_address, path)).await { + Ok(response) => match response.json::().await { + Ok(value) => value, + Err(err) => { + error!(target: LOG_TARGET, "response.json ({})", err); + panic!("Error parsing response ({})", err); + }, + }, + Err(err) => { + error!(target: LOG_TARGET, "reqwest::get ({})", err); + panic!("Error reqwest::get ({})", err); + }, + } } async fn json_rpc_call(&mut self, method_name: &str, params: &Value) -> Value { @@ -164,19 +161,24 @@ impl MergeMiningProxyProcess { "params": params, "id":self.id} ); - println!("json_rpc_call {}", method_name); - println!("json payload {}", json); + debug!(target: LOG_TARGET, "json_rpc_call {}", method_name); + debug!(target: LOG_TARGET, "json payload {}", json); self.id += 1; let full_address = format!("http://127.0.0.1:{}/json_rpc", self.port); - client - .post(full_address) - .json(&json) - .send() - .await - .unwrap() - .json() - .await - .unwrap() + + match client.post(full_address).json(&json).send().await { + Ok(response) => match response.json().await { + Ok(value) => value, + Err(err) => { + error!(target: LOG_TARGET, "response.json() {}", err); + panic!("Error parsing response ({})", err); + }, + }, + Err(err) => { + error!(target: LOG_TARGET, "client.post ({})", err); + panic!("Error client.post ({})", err); + }, + } } pub async fn get_height(&self) -> Value { @@ -185,8 +187,7 @@ impl MergeMiningProxyProcess { pub async fn get_block_template(&mut self) -> Value { let params = json!({ - "wallet_address":"5AUoj81i63cBUbiKY5jybsZXRDYb9CppmSjiZXC8ZYT6HZH6ebsQvBecYfRKDYoyzKF2uML9FKkTAc7nJvHKdoDYQEeteRW", - "reserve_size":60 + "wallet_address": "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A" }); self.json_rpc_call("getblocktemplate", ¶ms).await } diff --git a/integration_tests/tests/cucumber.rs b/integration_tests/tests/cucumber.rs index 632b53e930..a9bc36f181 100644 --- a/integration_tests/tests/cucumber.rs +++ b/integration_tests/tests/cucumber.rs @@ -68,6 +68,7 @@ fn main() { let world = TariWorld::cucumber() .repeat_failed() // following config needed to use eprint statements in the tests + .max_concurrent_scenarios(1) // If >1 the .after(move |_feature, _rule, scenario, ev, maybe_world| { let stdout_buffer = stdout_buffer_clone.clone(); Box::pin(async move { diff --git a/integration_tests/tests/features/Mempool.feature b/integration_tests/tests/features/Mempool.feature index 6cd2216420..e8d95d7719 100644 --- a/integration_tests/tests/features/Mempool.feature +++ b/integration_tests/tests/features/Mempool.feature @@ -21,7 +21,6 @@ Feature: Mempool Then SENDER has TX1 in MEMPOOL state Then TX1 is in the MEMPOOL of all nodes, where 1% can fail - @broken Scenario: Transactions are synced Given I have 2 seed nodes When I have a base node SENDER connected to all seed nodes @@ -34,7 +33,6 @@ Feature: Mempool Then SENDER has TX1 in MEMPOOL state Then TX1 is in the MEMPOOL of all nodes When I have a base node NODE1 connected to all seed nodes - # Keeps returning not stored. Maybe initial sync ins't receiving it. # mempool needs to sync more than 5 blocks before it starts syncing Then NODE1 has TX1 in MEMPOOL state When I mine 1 blocks on SENDER diff --git a/integration_tests/tests/features/WalletFFI.feature b/integration_tests/tests/features/WalletFFI.feature index e63cf3f14d..bba667873a 100644 --- a/integration_tests/tests/features/WalletFFI.feature +++ b/integration_tests/tests/features/WalletFFI.feature @@ -10,8 +10,7 @@ Feature: Wallet FFI And I want to get emoji id of ffi wallet FFI_WALLET And I stop ffi wallet FFI_WALLET - # Failing with: (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION) - @broken + @critical Scenario: As a client I want to be able to restore my ffi wallet from seed words Given I have a base node BASE When I have wallet SPECTATOR connected to base node BASE @@ -284,3 +283,4 @@ Feature: Wallet FFI And The fee per gram stats for FFI_WALLET are 18, 37, 56 When mining node MINER mines 1 blocks And The fee per gram stats for FFI_WALLET are 1, 1, 1 + And I stop ffi wallet FFI_WALLET diff --git a/integration_tests/tests/steps/wallet_ffi_steps.rs b/integration_tests/tests/steps/wallet_ffi_steps.rs index c0dd2d1f9c..97fadda7bc 100644 --- a/integration_tests/tests/steps/wallet_ffi_steps.rs +++ b/integration_tests/tests/steps/wallet_ffi_steps.rs @@ -516,10 +516,13 @@ async fn ffi_fee_per_gram_stats(world: &mut TariWorld, wallet: String, min: u64, let fee_per_gram_stats = ffi_wallet.get_fee_per_gram_stats(5); for i in 0..fee_per_gram_stats.get_length() { let fee_per_gram_stat = fee_per_gram_stats.get_at(i); - println!("order {}", fee_per_gram_stat.get_order()); - println!("min {}", fee_per_gram_stat.get_min_fee_per_gram()); - println!("avg {}", fee_per_gram_stat.get_avg_fee_per_gram()); - println!("max {}", fee_per_gram_stat.get_max_fee_per_gram()); + println!( + "order {}, min {}, avg {}, max {}", + fee_per_gram_stat.get_order(), + fee_per_gram_stat.get_min_fee_per_gram(), + fee_per_gram_stat.get_avg_fee_per_gram(), + fee_per_gram_stat.get_max_fee_per_gram() + ); assert_eq!(fee_per_gram_stat.get_min_fee_per_gram(), min); assert_eq!(fee_per_gram_stat.get_avg_fee_per_gram(), avg); assert_eq!(fee_per_gram_stat.get_max_fee_per_gram(), max); diff --git a/integration_tests/tests/steps/wallet_steps.rs b/integration_tests/tests/steps/wallet_steps.rs index a14fa10348..4688592e4f 100644 --- a/integration_tests/tests/steps/wallet_steps.rs +++ b/integration_tests/tests/steps/wallet_steps.rs @@ -77,7 +77,7 @@ use tari_utilities::hex::Hex; use crate::steps::{mining_steps::create_miner, CONFIRMATION_PERIOD, HALF_SECOND, TWO_MINUTES_WITH_HALF_SECOND_SLEEP}; -const LOG_TARGET: &str = "cucumber::wallet_steps"; +const LOG_TARGET: &str = "cucumber_detail::wallet_steps"; #[given(expr = "a wallet {word} connected to base node {word}")] async fn start_wallet(world: &mut TariWorld, wallet_name: String, node_name: String) {