diff --git a/Cargo.toml b/Cargo.toml index f4d01ca..0aa5d5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,9 @@ name = "zmigrate" path = "src/main.rs" [dependencies] -zewif = { path = "../zewif" } -zewif-zcashd = { path = "../zewif-zcashd" } -zewif-zingo = { path = "../zewif-zingo" } +zewif = "0.1.0" +zewif-zcashd = { version = "0.1.0", optional = true } +zewif-zingo = { version = "0.1.0", optional = true } anyhow = "1.0.95" hex = "0.4.3" @@ -25,5 +25,16 @@ anstyle = "^1.0.1" regex = "1.10.2" [features] -default = [] +default = ["zcashd"] +zcashd = ["dep:zewif-zcashd"] +zingo = ["dep:zewif-zingo"] with-context = [] + +[patch.crates-io] +zewif = { path = "../zewif/" } +zewif-zcashd = { path = "../zewif-zcashd/" } +zewif-zingo = { path = "../zewif-zingo/" } + +#zewif = { git = "https://github.com/nuttycom/zewif", rev = "61c988fbfb6c3e74c5b98ba3da1cbc521b3967bd" } +#zewif-zcashd = { git = "https://github.com/nuttycom/zewif-zcashd", rev = "dc7825b03e413596fcf301d9918df24dc012c22c", optional = true } +#zewif-zingo = { git = "https://github.com/nuttycom/zewif-zingo", rev = "9e647c9e5bfc9b795fdd6421d02a522f86093420", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 5f7d4ec..56f5659 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ -pub mod zcashd_cmd; -pub mod zingo_cmd; pub mod exec; pub mod file_args; +pub mod zcashd_cmd; +#[cfg(feature = "zingo")] +pub mod zingo_cmd; diff --git a/src/main.rs b/src/main.rs index eff2388..6400110 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,11 @@ - mod styles; use clap::{Parser as ClapParser, Subcommand}; -use zmigrate::{exec::Exec, zcashd_cmd, zingo_cmd}; +use zmigrate::exec::Exec; +#[cfg(feature = "zcashd")] +use zmigrate::zcashd_cmd; +#[cfg(feature = "zingo")] +use zmigrate::zingo_cmd; /// A tool for migrating Zcash wallets #[derive(Debug, clap::Parser)] @@ -18,7 +21,9 @@ struct Cli { #[derive(Debug, Subcommand)] #[doc(hidden)] enum MainCommands { + #[cfg(feature = "zcashd")] Zcashd(zcashd_cmd::CommandArgs), + #[cfg(feature = "zingo")] Zingo(zingo_cmd::CommandArgs), } @@ -40,7 +45,9 @@ fn inner_main() -> anyhow::Result<()> { let cli = Cli::parse(); let output = match cli.command { + #[cfg(feature = "zcashd")] MainCommands::Zcashd(args) => args.exec(), + #[cfg(feature = "zingo")] MainCommands::Zingo(args) => args.exec(), }; let output = output?; diff --git a/src/zcashd_cmd.rs b/src/zcashd_cmd.rs index f3637e6..02591f9 100644 --- a/src/zcashd_cmd.rs +++ b/src/zcashd_cmd.rs @@ -6,7 +6,7 @@ use std::fmt::Write; use crate::file_args::{FileArgs, FileArgsLike}; -use zewif_zcashd::{BDBDump, ZcashdDump, ZcashdParser}; +use zewif_zcashd::{BDBDump, ZcashdDump, ZcashdParser, migrate::migrate_to_zewif}; /// Process a zcashd wallet file #[derive(Debug, Args)] @@ -14,6 +14,13 @@ use zewif_zcashd::{BDBDump, ZcashdDump, ZcashdParser}; pub struct CommandArgs { #[command(flatten)] file_args: FileArgs, + + /// Flag indicating whether lenient parsing is allowed. + /// + /// Defaults to `false`. If set to `true`, parse errors will be reported to `stderr` but will + /// not halt execution. + #[arg(long, default_value = "false")] + lenient: bool } impl FileArgsLike for CommandArgs { @@ -24,17 +31,17 @@ impl FileArgsLike for CommandArgs { impl crate::exec::Exec for CommandArgs { fn exec(&self) -> Result { - dump_wallet(self.file()) + dump_wallet(self.file(), !self.lenient) } } -pub fn dump_wallet(file: &Path) -> Result { +pub fn dump_wallet(file: &Path, strict: bool) -> Result { let db_dump = BDBDump::from_file(file).context("Parsing BerkeleyDB file")?; - let zcashd_dump = ZcashdDump::from_bdb_dump(&db_dump).context("Parsing Zcashd dump")?; + let zcashd_dump = ZcashdDump::from_bdb_dump(&db_dump, strict).context("Parsing Zcashd dump")?; let (zcashd_wallet, unparsed_keys) = - ZcashdParser::parse_dump(&zcashd_dump).context("Parsing Zcashd dump")?; + ZcashdParser::parse_dump(&zcashd_dump, strict).context("Parsing Zcashd wallet")?; let mut output = String::new(); @@ -63,8 +70,7 @@ pub fn dump_wallet(file: &Path) -> Result { return Ok(output); } - let zewif_wallet = zewif_zcashd::migrate_to_zewif(&zcashd_wallet) - .context("Migrating to Zewif")?; + let zewif_wallet = migrate_to_zewif(&zcashd_wallet).context("Migrating to Zewif")?; writeln!(output, "---")?; writeln!(output, "Migrated wallet:\n{:#?}", zewif_wallet)?; @@ -79,18 +85,27 @@ pub fn dump_wallet(file: &Path) -> Result { let zcashd_address_count = zcashd_wallet.address_names().len(); // Count addresses in zewif wallet - all accounts combined - let zewif_address_count = zewif_wallet.wallets() + let zewif_address_count = zewif_wallet + .wallets() .values() .flat_map(|w| w.accounts().values()) .flat_map(|a| a.addresses()) .count(); - writeln!(report, "- Addresses: {}/{} preserved", zewif_address_count, zcashd_address_count)?; + writeln!( + report, + "- Addresses: {}/{} preserved", + zewif_address_count, zcashd_address_count + )?; // Check transaction preservation let zcashd_tx_count = zcashd_wallet.transactions().len(); let zewif_tx_count = zewif_wallet.transactions().len(); - writeln!(report, "- Transactions: {}/{} preserved", zewif_tx_count, zcashd_tx_count)?; + writeln!( + report, + "- Transactions: {}/{} preserved", + zewif_tx_count, zcashd_tx_count + )?; // Add the report to the output writeln!(output, "{}", report)?; diff --git a/tests/test_dump.rs b/tests/test_dump.rs index 4653458..375487f 100644 --- a/tests/test_dump.rs +++ b/tests/test_dump.rs @@ -1,9 +1,13 @@ use anyhow::{Result, bail}; -use zmigrate::{zcashd_cmd, zingo_cmd}; -use std::path::PathBuf; -use std::fmt::Write; use regex::Regex; +use std::fmt::Write; +use std::path::PathBuf; + +#[cfg(feature = "zcashd")] +use zmigrate::zcashd_cmd; +#[cfg(feature = "zingo")] +use zmigrate::zingo_cmd; fn fixtures_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) @@ -13,12 +17,14 @@ fn fixtures_dir() -> PathBuf { fn dump_wallet(path_elements: &[&str]) -> Result { let path = fixtures_dir().join(path_elements.iter().collect::()); - if path_elements[0] == "zcashd" { - zcashd_cmd::dump_wallet(&path) - } else if path_elements[0] == "zingo" { - zingo_cmd::dump_wallet(&path) - } else { - bail!("Unknown command: {}", path_elements[0]); + match path_elements[0] { + #[cfg(feature = "zcashd")] + "zcashd" => zcashd_cmd::dump_wallet(&path, true), + #[cfg(feature = "zingo")] + "zingo" => zingo_cmd::dump_wallet(&path), + _ => { + bail!("Unknown command: {}", path_elements[0]) + } } } @@ -47,12 +53,20 @@ fn test_migration_quality(path_elements: &[&str]) -> Result { // Check address preservation let zcashd_address_count = zcashd_section.matches("Address").count(); let zewif_address_count = zewif_section.matches("Address").count(); - writeln!(report, "- Addresses: {}/{} preserved", zewif_address_count, zcashd_address_count)?; + writeln!( + report, + "- Addresses: {}/{} preserved", + zewif_address_count, zcashd_address_count + )?; // Check transaction preservation let zcashd_tx_count = zcashd_section.matches("TxId").count(); let zewif_tx_count = zewif_section.matches("TxId").count(); - writeln!(report, "- Transactions: {}/{} preserved", zewif_tx_count, zcashd_tx_count)?; + writeln!( + report, + "- Transactions: {}/{} preserved", + zewif_tx_count, zcashd_tx_count + )?; // Check position information let zero_positions_count = zewif_section.matches("Position(0)").count(); @@ -61,8 +75,11 @@ fn test_migration_quality(path_elements: &[&str]) -> Result { if total_positions > 0 { let preservation_rate = (nonzero_positions_count as f64 / total_positions as f64) * 100.0; - writeln!(report, "- Positions: {}/{} preserved ({:.1}%)", - nonzero_positions_count, total_positions, preservation_rate)?; + writeln!( + report, + "- Positions: {}/{} preserved ({:.1}%)", + nonzero_positions_count, total_positions, preservation_rate + )?; } else { writeln!(report, "- Positions: No position data found")?; } @@ -94,12 +111,34 @@ fn test_migration_quality(path_elements: &[&str]) -> Result { // Check for account handling let zcashd_accounts = count_pattern(zcashd_section, r"Account\s*\{"); let zewif_accounts = count_pattern(zewif_section, r"Account\s*\{"); - writeln!(report, "- Accounts: {}/{} preserved", zewif_accounts, zcashd_accounts)?; + writeln!( + report, + "- Accounts: {}/{} preserved", + zewif_accounts, zcashd_accounts + )?; // Check specific ZCash features - check_feature_presence(&mut report, zcashd_section, zewif_section, "Unified", "Unified Address Support")?; - check_feature_presence(&mut report, zcashd_section, zewif_section, "SeedMaterial", "Seed Material")?; - check_feature_presence(&mut report, zcashd_section, zewif_section, "Network", "Network Information")?; + check_feature_presence( + &mut report, + zcashd_section, + zewif_section, + "Unified", + "Unified Address Support", + )?; + check_feature_presence( + &mut report, + zcashd_section, + zewif_section, + "SeedMaterial", + "Seed Material", + )?; + check_feature_presence( + &mut report, + zcashd_section, + zewif_section, + "Network", + "Network Information", + )?; Ok(report) } @@ -126,7 +165,13 @@ fn count_pattern(text: &str, pattern: &str) -> usize { re.find_iter(text).count() } -fn check_feature_presence(report: &mut String, source: &str, dest: &str, key_word: &str, feature_name: &str) -> Result<()> { +fn check_feature_presence( + report: &mut String, + source: &str, + dest: &str, + key_word: &str, + feature_name: &str, +) -> Result<()> { let in_source = source.contains(key_word); let in_dest = dest.contains(key_word); @@ -141,14 +186,22 @@ fn check_feature_presence(report: &mut String, source: &str, dest: &str, key_wor Ok(()) } -fn report_key_preservation(report: &mut String, source: &str, dest: &str, key_type: &str) -> Result<()> { +fn report_key_preservation( + report: &mut String, + source: &str, + dest: &str, + key_type: &str, +) -> Result<()> { let source_count = source.matches(key_type).count(); let dest_count = dest.matches(key_type).count(); if source_count > 0 { let preservation_rate = (dest_count as f64 / source_count as f64) * 100.0; - writeln!(report, " * {} keys: {}/{} preserved ({:.1}%)", - key_type, dest_count, source_count, preservation_rate)?; + writeln!( + report, + " * {} keys: {}/{} preserved ({:.1}%)", + key_type, dest_count, source_count, preservation_rate + )?; } else { writeln!(report, " * {} keys: None found in source", key_type)?; } @@ -163,17 +216,14 @@ fn test_zcashd() { vec!["zcashd", "golden-v5.6.0", "node1_wallet.dat"], vec!["zcashd", "golden-v5.6.0", "node2_wallet.dat"], vec!["zcashd", "golden-v5.6.0", "node3_wallet.dat"], - vec!["zcashd", "tarnished-v5.6.0", "node0_wallet.dat"], vec!["zcashd", "tarnished-v5.6.0", "node1_wallet.dat"], vec!["zcashd", "tarnished-v5.6.0", "node2_wallet.dat"], vec!["zcashd", "tarnished-v5.6.0", "node3_wallet.dat"], - vec!["zcashd", "sprout", "node0_wallet.dat"], vec!["zcashd", "sprout", "node1_wallet.dat"], vec!["zcashd", "sprout", "node2_wallet.dat"], vec!["zcashd", "sprout", "node3_wallet.dat"], - vec!["zcashd", "wallet0.dat"], vec!["zcashd", "wallet1.dat"], vec!["zcashd", "wallet2.dat"], @@ -195,13 +245,10 @@ fn test_migration_quality_report() { // Golden reference wallets (expected to be fully working) vec!["zcashd", "golden-v5.6.0", "node0_wallet.dat"], vec!["zcashd", "golden-v5.6.0", "node2_wallet.dat"], // May have more shielded data - // Tarnished wallets (may have issues) vec!["zcashd", "tarnished-v5.6.0", "node0_wallet.dat"], - // Sprout wallets (older format) vec!["zcashd", "sprout", "node0_wallet.dat"], - // Standard wallets vec!["zcashd", "wallet0.dat"], // Test standard wallet vec!["zcashd", "wallet5.dat"], // Test wallet likely with Orchard data @@ -210,8 +257,18 @@ fn test_migration_quality_report() { // Create a summary table of all wallet reports let mut summary = String::new(); writeln!(summary, "=== MIGRATION QUALITY SUMMARY ===").unwrap(); - writeln!(summary, "{:<40} | {:<15} | {:<15} | {:<15}", "Wallet", "Addresses", "Transactions", "Positions").unwrap(); - writeln!(summary, "{:-<40}-+-{:-<15}-+-{:-<15}-+-{:-<15}", "", "", "", "").unwrap(); + writeln!( + summary, + "{:<40} | {:<15} | {:<15} | {:<15}", + "Wallet", "Addresses", "Transactions", "Positions" + ) + .unwrap(); + writeln!( + summary, + "{:-<40}-+-{:-<15}-+-{:-<15}-+-{:-<15}", + "", "", "", "" + ) + .unwrap(); // Process each wallet and collect stats for path in &test_paths { @@ -236,8 +293,12 @@ fn test_migration_quality_report() { "N/A".to_string() }; - writeln!(summary, "{:<40} | {:<15} | {:<15} | {:<15}", - wallet_name, addr_stats, tx_stats, pos_stats).unwrap(); + writeln!( + summary, + "{:<40} | {:<15} | {:<15} | {:<15}", + wallet_name, addr_stats, tx_stats, pos_stats + ) + .unwrap(); } // Print the summary table @@ -257,15 +318,21 @@ fn extract_stat(report: &str, label: &str) -> String { #[test] fn test_zingo() { let paths = vec![ - vec!["zingo", "mainnet", "hhcclaltpcckcsslpcnetblr-gf0aaf9347.dat"], + vec![ + "zingo", + "mainnet", + "hhcclaltpcckcsslpcnetblr-gf0aaf9347.dat", + ], vec!["zingo", "mainnet", "hhcclaltpcckcsslpcnetblr-latest.dat"], // vec!["zingo", "mainnet", "vtfcorfbcbpctcfupmegmwbp-v28.dat"], // long - vec!["zingo", "regtest", "hmvasmuvwmssvichcarbpoct-v27.dat"], vec!["zingo", "regtest", "aadaalacaadaalacaadaalac-orch-only.dat"], - vec!["zingo", "regtest", "aadaalacaadaalacaadaalac-orch-and-sapling.dat"], + vec![ + "zingo", + "regtest", + "aadaalacaadaalacaadaalac-orch-and-sapling.dat", + ], vec!["zingo", "regtest", "aaaaaaaaaaaaaaaaaaaaaaaa-v26.dat"], - vec!["zingo", "testnet", "cbbhrwiilgbrababsshsmtpr-latest.dat"], vec!["zingo", "testnet", "G93738061a.dat"], vec!["zingo", "testnet", "Gab72a38b.dat"], diff --git a/tests/test_transaction_assignment.rs b/tests/test_transaction_assignment.rs index 346e901..5e563cd 100644 --- a/tests/test_transaction_assignment.rs +++ b/tests/test_transaction_assignment.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; use std::collections::{HashMap, HashSet}; -use std::path::PathBuf; use std::fmt::Write; +use std::path::PathBuf; use zewif_zcashd::{BDBDump, ZcashdDump, ZcashdParser, ZcashdWallet}; @@ -29,7 +29,7 @@ fn load_zcashd_wallet(path_elements: &[&str]) -> Result { let db_dump = BDBDump::from_file(&path).context("Parsing BerkeleyDB file")?; // Parse to ZcashdDump - let zcashd_dump = ZcashdDump::from_bdb_dump(&db_dump).context("Parsing Zcashd dump")?; + let zcashd_dump = ZcashdDump::from_bdb_dump(&db_dump, true).context("Parsing Zcashd dump")?; // Parse into wallet structure let (zcashd_wallet, _unparsed_keys) = @@ -61,8 +61,8 @@ fn test_transaction_assignment_coverage(path_elements: &[&str]) -> Result Result = account.relevant_transactions() + let tx_ids: HashSet = account + .relevant_transactions() .iter() .map(|tx_id| format!("{:?}", tx_id)) .collect(); @@ -103,7 +104,11 @@ fn test_transaction_assignment_coverage(path_elements: &[&str]) -> Result Result Result Result> = HashMap::new(); @@ -188,9 +202,21 @@ fn test_transaction_duplicate_assignments(path_elements: &[&str]) -> Result Result 5 { - writeln!(report, "- ... and {} more multi-assigned transactions", multi_assigned_txs.len() - 5)?; + writeln!( + report, + "- ... and {} more multi-assigned transactions", + multi_assigned_txs.len() - 5 + )?; } } @@ -240,8 +279,8 @@ fn test_change_detection(path_elements: &[&str]) -> Result { let change_addresses = extract_change_addresses(&wallet); // Migrate to ZeWIF - let zewif_wallet = zewif_zcashd::migrate_to_zewif(&wallet) - .context("Migrating to ZeWIF")?; + let zewif_wallet = + zewif_zcashd::migrate::migrate_to_zewif(&wallet).context("Migrating to ZeWIF")?; // Find transactions that might involve change let mut potential_change_txs = 0; @@ -259,7 +298,8 @@ fn test_change_detection(path_elements: &[&str]) -> Result { // Count assigned accounts for this transaction (placeholder for now) // In a complete implementation, we would check that this transaction // is assigned to the same account that sent the funds - let assigned_account_count = count_accounts_for_transaction(&zewif_wallet, &transaction.txid()); + let assigned_account_count = + count_accounts_for_transaction(&zewif_wallet, &transaction.txid()); // If assigned to exactly one account, consider it properly assigned if assigned_account_count == 1 { @@ -270,10 +310,26 @@ fn test_change_detection(path_elements: &[&str]) -> Result { // Generate report let mut report = String::new(); - writeln!(report, "Change Transaction Assignment Report for {:?}", path_elements)?; - writeln!(report, "- Total Transactions: {}", zewif_wallet.transactions().len())?; - writeln!(report, "- Potential Change Transactions: {}", potential_change_txs)?; - writeln!(report, "- Change Addresses Found: {}", change_addresses.len())?; + writeln!( + report, + "Change Transaction Assignment Report for {:?}", + path_elements + )?; + writeln!( + report, + "- Total Transactions: {}", + zewif_wallet.transactions().len() + )?; + writeln!( + report, + "- Potential Change Transactions: {}", + potential_change_txs + )?; + writeln!( + report, + "- Change Addresses Found: {}", + change_addresses.len() + )?; let properly_assigned_percentage = if potential_change_txs > 0 { (properly_assigned_change_txs as f64 / potential_change_txs as f64) * 100.0 @@ -281,8 +337,11 @@ fn test_change_detection(path_elements: &[&str]) -> Result { 0.0 }; - writeln!(report, "- Properly Assigned Change Transactions: {}/{} ({:.1}%)", - properly_assigned_change_txs, potential_change_txs, properly_assigned_percentage)?; + writeln!( + report, + "- Properly Assigned Change Transactions: {}/{} ({:.1}%)", + properly_assigned_change_txs, potential_change_txs, properly_assigned_percentage + )?; // List some change addresses if found if !change_addresses.is_empty() { @@ -291,7 +350,11 @@ fn test_change_detection(path_elements: &[&str]) -> Result { writeln!(report, "- {}", addr)?; } if change_addresses.len() > 5 { - writeln!(report, "- ... and {} more change addresses", change_addresses.len() - 5)?; + writeln!( + report, + "- ... and {} more change addresses", + change_addresses.len() - 5 + )?; } } @@ -357,13 +420,10 @@ fn test_transaction_assignment_across_wallets() -> Result<()> { // Golden wallets (expected to have full transaction history) vec!["zcashd", "golden-v5.6.0", "node0_wallet.dat"], vec!["zcashd", "golden-v5.6.0", "node2_wallet.dat"], // Test a different node wallet - // Tarnished wallets (may have corruption or issues) vec!["zcashd", "tarnished-v5.6.0", "node0_wallet.dat"], - // Sprout wallets (older format) vec!["zcashd", "sprout", "node0_wallet.dat"], - // Standard wallets vec!["zcashd", "wallet0.dat"], // Basic wallet vec!["zcashd", "wallet5.dat"], // Wallet with likely Orchard data @@ -372,9 +432,16 @@ fn test_transaction_assignment_across_wallets() -> Result<()> { // Create a summary table let mut summary = String::new(); writeln!(summary, "=== TRANSACTION ASSIGNMENT SUMMARY ===")?; - writeln!(summary, "{:<40} | {:<15} | {:<15} | {:<15}", - "Wallet", "Total Txs", "Assigned (%)", "Multi-Assigned (%)")?; - writeln!(summary, "{:-<40}-+-{:-<15}-+-{:-<15}-+-{:-<15}", "", "", "", "")?; + writeln!( + summary, + "{:<40} | {:<15} | {:<15} | {:<15}", + "Wallet", "Total Txs", "Assigned (%)", "Multi-Assigned (%)" + )?; + writeln!( + summary, + "{:-<40}-+-{:-<15}-+-{:-<15}-+-{:-<15}", + "", "", "", "" + )?; for path in &test_paths { // Run all tests for this wallet @@ -392,10 +459,14 @@ fn test_transaction_assignment_across_wallets() -> Result<()> { let wallet_name = path.join("/"); let tx_count = extract_stat(&coverage_report, "- Total Transactions:"); let assigned_percentage = extract_percentage(&coverage_report, "- Assigned Transactions:"); - let multi_assigned_percentage = extract_percentage(&duplicate_report, "- Multi-Account Assignment Rate:"); - - writeln!(summary, "{:<40} | {:<15} | {:<15} | {:<15}", - wallet_name, tx_count, assigned_percentage, multi_assigned_percentage)?; + let multi_assigned_percentage = + extract_percentage(&duplicate_report, "- Multi-Account Assignment Rate:"); + + writeln!( + summary, + "{:<40} | {:<15} | {:<15} | {:<15}", + wallet_name, tx_count, assigned_percentage, multi_assigned_percentage + )?; } // Print final summary @@ -445,7 +516,7 @@ fn extract_percentage(report: &str, label: &str) -> String { if let Some(pct_idx) = line.find('%') { if let Some(paren_idx) = line.find('(') { if paren_idx < pct_idx { - let percentage = &line[paren_idx+1..pct_idx]; + let percentage = &line[paren_idx + 1..pct_idx]; return percentage.trim().to_string(); } } @@ -453,7 +524,10 @@ fn extract_percentage(report: &str, label: &str) -> String { // Try to extract just the number before the % sign let start_idx = pct_idx; let mut current_idx = start_idx; - while current_idx > 0 && (line.chars().nth(current_idx-1).unwrap().is_ascii_digit() || line.chars().nth(current_idx-1).unwrap() == '.') { + while current_idx > 0 + && (line.chars().nth(current_idx - 1).unwrap().is_ascii_digit() + || line.chars().nth(current_idx - 1).unwrap() == '.') + { current_idx -= 1; } if current_idx < start_idx { @@ -485,11 +559,12 @@ fn test_transaction_address_registry_correlation() -> Result<()> { let tx_count = wallet.transactions().len(); // Migrate to ZeWIF - let zewif_wallet = zewif_zcashd::migrate_to_zewif(&wallet) - .context("Migrating to ZeWIF")?; + let zewif_wallet = + zewif_zcashd::migrate::migrate_to_zewif(&wallet).context("Migrating to ZeWIF")?; // Count addresses in the ZeWIF wallet - let zewif_address_count = zewif_wallet.wallets() + let zewif_address_count = zewif_wallet + .wallets() .values() .flat_map(|w| w.accounts().values()) .flat_map(|a| a.addresses()) @@ -499,7 +574,8 @@ fn test_transaction_address_registry_correlation() -> Result<()> { let zewif_tx_count = zewif_wallet.transactions().len(); // Count transactions assigned to accounts - let assigned_txs: HashSet<_> = zewif_wallet.wallets() + let assigned_txs: HashSet<_> = zewif_wallet + .wallets() .values() .flat_map(|w| w.accounts().values()) .flat_map(|a| a.relevant_transactions()) @@ -515,35 +591,55 @@ fn test_transaction_address_registry_correlation() -> Result<()> { println!("- Transactions Assigned to Accounts: {}", assigned_tx_count); // Basic assertions - assert!(zewif_address_count > 0, "No addresses found in ZeWIF wallet"); + assert!( + zewif_address_count > 0, + "No addresses found in ZeWIF wallet" + ); assert!(zewif_tx_count > 0, "No transactions found in ZeWIF wallet"); - assert!(assigned_tx_count > 0, "No transactions assigned to accounts"); + assert!( + assigned_tx_count > 0, + "No transactions assigned to accounts" + ); // Check address preservation - The counts might not be exactly equal due to // potential differences in how addresses are represented between formats - println!("- Note: ZeWIF address count may differ due to unified addresses or different representation formats"); - assert!(zewif_address_count > 0, "No addresses in ZeWIF format after migration"); + println!( + "- Note: ZeWIF address count may differ due to unified addresses or different representation formats" + ); + assert!( + zewif_address_count > 0, + "No addresses in ZeWIF format after migration" + ); // Check transaction preservation - assert_eq!(zewif_tx_count, tx_count, + assert_eq!( + zewif_tx_count, tx_count, "Transaction count mismatch: ZeWIF has {} transactions, source has {}", - zewif_tx_count, tx_count); + zewif_tx_count, tx_count + ); // Check that at least some transactions are assigned (ideally all) // This checks that we're not defaulting to zero assigned transactions - assert!(assigned_tx_count > 0, - "No transactions assigned to accounts after migration"); + assert!( + assigned_tx_count > 0, + "No transactions assigned to accounts after migration" + ); // In a perfect world, all transactions would be assigned // but we might need to relax this assertion depending on the wallet data let assignment_percentage = (assigned_tx_count as f64 / zewif_tx_count as f64) * 100.0; - println!("- Transaction Assignment Rate: {:.1}%", assignment_percentage); + println!( + "- Transaction Assignment Rate: {:.1}%", + assignment_percentage + ); // We'll check for a reasonable percentage of transaction assignment // 70% is a more reasonable threshold than 90% for initial testing - assert!(assignment_percentage >= 70.0, + assert!( + assignment_percentage >= 70.0, "Only {:.1}% of transactions assigned to accounts (should be >=70%)", - assignment_percentage); + assignment_percentage + ); Ok(()) } @@ -569,12 +665,13 @@ fn test_address_registry_initialization() -> Result<()> { let sapling_count = wallet.sapling_z_addresses().len(); // Migrate to ZeWIF - let zewif_wallet = zewif_zcashd::migrate_to_zewif(&wallet) - .context("Migrating to ZeWIF")?; + let zewif_wallet = + zewif_zcashd::migrate::migrate_to_zewif(&wallet).context("Migrating to ZeWIF")?; // Count addresses in the ZeWIF wallet // Note: This is a simplified check as we don't have direct access to address types - let zewif_address_count = zewif_wallet.wallets() + let zewif_address_count = zewif_wallet + .wallets() .values() .flat_map(|w| w.accounts().values()) .flat_map(|a| a.addresses()) @@ -588,25 +685,36 @@ fn test_address_registry_initialization() -> Result<()> { // Check if addresses were preserved during migration // We expect at least the transparent and sapling addresses to be preserved - assert!(zewif_address_count >= (transparent_count + sapling_count), + assert!( + zewif_address_count >= (transparent_count + sapling_count), "Not all addresses were migrated: source has {} transparent and {} sapling, ZeWIF has {} total", - transparent_count, sapling_count, zewif_address_count); + transparent_count, + sapling_count, + zewif_address_count + ); // Count transactions assigned to at least one account - let assigned_txs: HashSet<_> = zewif_wallet.wallets() + let assigned_txs: HashSet<_> = zewif_wallet + .wallets() .values() .flat_map(|w| w.accounts().values()) .flat_map(|a| a.relevant_transactions()) .collect(); // Check that most transactions are assigned - let assignment_percentage = (assigned_txs.len() as f64 / zewif_wallet.transactions().len() as f64) * 100.0; - println!("- Transaction Assignment Rate: {:.1}%", assignment_percentage); + let assignment_percentage = + (assigned_txs.len() as f64 / zewif_wallet.transactions().len() as f64) * 100.0; + println!( + "- Transaction Assignment Rate: {:.1}%", + assignment_percentage + ); // Check that a significant percentage are assigned - assert!(assignment_percentage >= 70.0, + assert!( + assignment_percentage >= 70.0, "Transaction assignment rate too low: {:.1}% (should be >=70%)", - assignment_percentage); + assignment_percentage + ); Ok(()) } @@ -627,15 +735,15 @@ fn test_multi_account_transactions() -> Result<()> { // We'll focus on testing specific wallets that might have multi-account transactions let multi_account_wallet_paths = vec![ vec!["zcashd", "golden-v5.6.0", "node0_wallet.dat"], // Primary test wallet - vec!["zcashd", "wallet0.dat"], // Additional test wallet + vec!["zcashd", "wallet0.dat"], // Additional test wallet ]; for path in &multi_account_wallet_paths { let wallet = load_zcashd_wallet(path)?; // Migrate to ZeWIF - let zewif_wallet = zewif_zcashd::migrate_to_zewif(&wallet) - .context("Migrating to ZeWIF")?; + let zewif_wallet = + zewif_zcashd::migrate::migrate_to_zewif(&wallet).context("Migrating to ZeWIF")?; // Find which transactions are assigned to multiple accounts let mut tx_to_accounts: HashMap> = HashMap::new(); @@ -672,38 +780,51 @@ fn test_multi_account_transactions() -> Result<()> { // Generate report println!("Multi-Account Transaction Report for {:?}", path); println!("- Total Transactions: {}", total_tx_count); - println!("- Multi-Account Transactions: {} ({:.1}%)", - multi_account_count, multi_account_percentage); + println!( + "- Multi-Account Transactions: {} ({:.1}%)", + multi_account_count, multi_account_percentage + ); // Display some examples of multi-account transactions if !multi_account_txs.is_empty() { println!("\nExamples of Multi-Account Transactions:"); for (i, (tx_id, accounts)) in multi_account_txs.iter().enumerate().take(3) { - println!("{}. Transaction {} is assigned to {} accounts:", - i+1, tx_id, accounts.len()); + println!( + "{}. Transaction {} is assigned to {} accounts:", + i + 1, + tx_id, + accounts.len() + ); for account in accounts.iter() { println!(" - {}", account); } } if multi_account_txs.len() > 3 { - println!(" ... and {} more multi-account transactions", multi_account_txs.len() - 3); + println!( + " ... and {} more multi-account transactions", + multi_account_txs.len() - 3 + ); } } // Actual test: we can't know exactly how many multi-account transactions there should be, // but we want to avoid having too many (which would indicate indiscriminate assignment) - assert!(multi_account_percentage < 80.0, + assert!( + multi_account_percentage < 80.0, "Too many multi-account transactions: {:.1}% (should be < 80%)", - multi_account_percentage); + multi_account_percentage + ); // We also don't want zero multi-account transactions in most wallets, as some // transactions legitimately involve multiple accounts // However, this test might fail for some wallets with only a single account // or with no cross-account transactions, so we'll make it a soft check if total_tx_count > 10 { - println!("Note: Found {} multi-account transactions ({:.1}%)", - multi_account_count, multi_account_percentage); + println!( + "Note: Found {} multi-account transactions ({:.1}%)", + multi_account_count, multi_account_percentage + ); } } diff --git a/tests/test_tree_size.rs b/tests/test_tree_size.rs index 6381f93..bb5173b 100644 --- a/tests/test_tree_size.rs +++ b/tests/test_tree_size.rs @@ -17,7 +17,7 @@ fn extract_tree_data(wallet_path: &[&str]) -> Result<()> { let db_dump = BDBDump::from_file(&file_path).context("Parsing BerkeleyDB file")?; // Parse to ZcashdDump - let zcashd_dump = ZcashdDump::from_bdb_dump(&db_dump).context("Parsing Zcashd dump")?; + let zcashd_dump = ZcashdDump::from_bdb_dump(&db_dump, true).context("Parsing Zcashd dump")?; // Parse into wallet structure let (zcashd_wallet, _unparsed_keys) = @@ -37,11 +37,12 @@ fn extract_tree_data(wallet_path: &[&str]) -> Result<()> { println!("Unparsed data size: {}", tree.unparsed_data().len()); // Migrate to ZeWIF - let zewif_wallet = zewif_zcashd::migrate_to_zewif(&zcashd_wallet) - .context("Migrating to ZeWIF")?; + let zewif_wallet = + zewif_zcashd::migrate::migrate_to_zewif(&zcashd_wallet).context("Migrating to ZeWIF")?; // Count total addresses and transactions after migration - let address_count = zewif_wallet.wallets() + let address_count = zewif_wallet + .wallets() .values() .flat_map(|w| w.accounts().values()) .flat_map(|a| a.addresses()) diff --git a/tests/test_witness_data.rs b/tests/test_witness_data.rs index 3ae1f1a..368a579 100644 --- a/tests/test_witness_data.rs +++ b/tests/test_witness_data.rs @@ -1,7 +1,11 @@ -use anyhow::Result; -use zmigrate::{zcashd_cmd, zingo_cmd}; -use std::path::PathBuf; +use anyhow::{Result, bail}; use regex::Regex; +use std::path::PathBuf; + +#[cfg(feature = "zcashd")] +use zmigrate::zcashd_cmd; +#[cfg(feature = "zingo")] +use zmigrate::zingo_cmd; fn fixtures_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) @@ -11,12 +15,14 @@ fn fixtures_dir() -> PathBuf { fn dump_wallet(path_elements: &[&str]) -> Result { let path = fixtures_dir().join(path_elements.iter().collect::()); - if path_elements[0] == "zcashd" { - zcashd_cmd::dump_wallet(&path) - } else if path_elements[0] == "zingo" { - zingo_cmd::dump_wallet(&path) - } else { - Err(anyhow::anyhow!("Unknown command: {}", path_elements[0])) + match path_elements[0] { + #[cfg(feature = "zcashd")] + "zcashd" => zcashd_cmd::dump_wallet(&path, true), + #[cfg(feature = "zingo")] + "zingo" => zingo_cmd::dump_wallet(&path), + _ => { + bail!("Unknown command: {}", path_elements[0]) + } } } @@ -28,19 +34,17 @@ fn test_witness_data_migration() { // Golden reference wallets (expected to be fully working) vec!["zcashd", "golden-v5.6.0", "node0_wallet.dat"], vec!["zcashd", "golden-v5.6.0", "node2_wallet.dat"], - // Tarnished wallets (may have issues) vec!["zcashd", "tarnished-v5.6.0", "node0_wallet.dat"], - - // Standard wallets + // Standard wallets vec!["zcashd", "wallet0.dat"], vec!["zcashd", "wallet5.dat"], ]; // Process each wallet and check witness data migration for path in &test_paths { - let output = dump_wallet(path) - .unwrap_or_else(|e| panic!("Error dumping wallet {:?}: {}", path, e)); + let output = + dump_wallet(path).unwrap_or_else(|e| panic!("Error dumping wallet {:?}: {}", path, e)); // Split the output into ZcashdWallet and ZewifTop sections let sections: Vec<&str> = output.split("---").collect(); @@ -58,21 +62,24 @@ fn test_witness_data_migration() { println!("\nWitness Data & Memo Migration for {:?}:", path); println!("- Source has witness data: {}", has_witness_in_source); println!("- Destination witness entries: {}", witness_count_in_dest); - + // Check for memo field entries in the output let memo_count_in_dest = count_memo_entries(zewif_section); println!("- Destination memo entries: {}", memo_count_in_dest); // Note: Transaction time is noted in the code but not yet stored // This will be implemented in the "Extract Transaction Metadata" subtask - + // We don't want to strictly assert witness data exists because some wallets // may legitimately not have any. Instead, we just log the information. - + // But we can check that memo field support is working by verifying that we have some memo entries // Only assert if we have sapling outputs, which should have memo fields if zewif_section.contains("SaplingOutputDescription") { - assert!(memo_count_in_dest > 0, "Memo fields should be present in Sapling outputs"); + assert!( + memo_count_in_dest > 0, + "Memo fields should be present in Sapling outputs" + ); } } } @@ -81,8 +88,8 @@ fn test_witness_data_migration() { fn has_witness_data(wallet_section: &str) -> bool { // Look for evidence of witness data in the wallet // This could be in either witnesses field or witness fields - wallet_section.contains("witnesses: [") || - (wallet_section.contains("witness:") && !wallet_section.contains("witness: None")) + wallet_section.contains("witnesses: [") + || (wallet_section.contains("witness:") && !wallet_section.contains("witness: None")) } /// Count witness entries in the destination ZeWIF format @@ -99,4 +106,4 @@ fn count_memo_entries(zewif_section: &str) -> usize { let memo_pattern = r"memo:\s*Some\("; let re = Regex::new(memo_pattern).unwrap(); re.find_iter(zewif_section).count() -} \ No newline at end of file +}