diff --git a/Cargo.lock b/Cargo.lock index ba46b41e0..e653231a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4024,12 +4024,14 @@ dependencies = [ "incrementalmerkletree", "json", "log", + "orchard", "portpicker", "serde", "serde_json", "tempdir", "tokio", "tracing", + "zcash_address", "zcash_primitives", "zingoconfig", "zingolib", diff --git a/zingo-testutils/Cargo.toml b/zingo-testutils/Cargo.toml index aa8800b94..e0a2895c6 100644 --- a/zingo-testutils/Cargo.toml +++ b/zingo-testutils/Cargo.toml @@ -9,6 +9,8 @@ edition = "2021" zingoconfig = { path = "../zingoconfig" } zingolib = { path = "../zingolib" } zcash_primitives = { workspace = true } +zcash_address = { workspace = true } +orchard = { workspace = true } portpicker = { workspace = true} tempdir = { workspace = true } json = "0.12.4" diff --git a/zingo-testutils/src/lib.rs b/zingo-testutils/src/lib.rs index ad98ebfe9..32ffb7571 100644 --- a/zingo-testutils/src/lib.rs +++ b/zingo-testutils/src/lib.rs @@ -1,5 +1,8 @@ pub mod data; pub use incrementalmerkletree; +use zcash_address::unified::{Fvk, Ufvk}; +use zingolib::wallet::keys::unified::WalletCapability; +use zingolib::wallet::WalletBase; pub mod regtest; use std::fs::OpenOptions; use std::io::Read; @@ -17,12 +20,53 @@ use zingolib::lightclient::LightClient; use crate::scenarios::setup::TestEnvironmentGenerator; +pub fn build_fvks_from_wallet_capability(wallet_capability: &WalletCapability) -> [Fvk; 3] { + let o_fvk = Fvk::Orchard( + orchard::keys::FullViewingKey::try_from(wallet_capability) + .unwrap() + .to_bytes(), + ); + let s_fvk = Fvk::Sapling( + zcash_primitives::zip32::sapling::DiversifiableFullViewingKey::try_from(wallet_capability) + .unwrap() + .to_bytes(), + ); + let mut t_fvk_bytes = [0u8; 65]; + let t_ext_pk: zingolib::wallet::keys::extended_transparent::ExtendedPubKey = + (wallet_capability).try_into().unwrap(); + t_fvk_bytes[0..32].copy_from_slice(&t_ext_pk.chain_code[..]); + t_fvk_bytes[32..65].copy_from_slice(&t_ext_pk.public_key.serialize()[..]); + let t_fvk = Fvk::P2pkh(t_fvk_bytes); + [o_fvk, s_fvk, t_fvk] +} +pub async fn build_fvk_client_and_capability( + fvks: &[&Fvk], + zingoconfig: &ZingoConfig, +) -> (LightClient, WalletCapability) { + let ufvk = zcash_address::unified::Encoding::encode( + &::try_from_items( + fvks.iter().copied().cloned().collect(), + ) + .unwrap(), + &zcash_address::Network::Regtest, + ); + let viewkey_client = + LightClient::create_unconnected(zingoconfig, WalletBase::Ufvk(ufvk), 0).unwrap(); + let watch_wc = viewkey_client + .wallet + .wallet_capability() + .read() + .await + .clone(); + (viewkey_client, watch_wc) +} + #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct DurationAnnotation { - timestamp: u64, - git_description: String, - test_name: String, - duration: Duration, + pub timestamp: u64, + pub git_description: String, + pub test_name: String, + pub duration: Duration, } impl DurationAnnotation { pub fn new(test_name: String, duration: Duration) -> Self { @@ -66,8 +110,7 @@ fn timestamp() -> u64 { .unwrap() .as_secs() } -fn path_to_times(basename: String) -> PathBuf { - let file_name = PathBuf::from(basename); +fn path_to_times(file_name: PathBuf) -> PathBuf { let timing_dir = PathBuf::from( std::env::var("CARGO_MANIFEST_DIR").expect("To be inside a manifested space."), ) @@ -82,18 +125,20 @@ pub fn read_duration_annotation_file(target: PathBuf) -> Vec }; data_set } +pub fn get_duration_annotations(storage_file: PathBuf) -> Vec { + read_duration_annotation_file(storage_file) +} pub fn record_time(annotation: &DurationAnnotation) { - let basename = format!("{}.json", annotation.test_name); - let data_store = path_to_times(basename); - - let mut data_set = read_duration_annotation_file(data_store.clone()); + let storage_location = + path_to_times(PathBuf::from("sync_duration_annotation.json".to_string())); + let mut data_set = get_duration_annotations(storage_location.clone()); data_set.push(annotation.clone()); //let json_dataset = array!(data_set); let mut time_file = OpenOptions::new() .create(true) .write(true) - .open(data_store) + .open(storage_location) .expect("to access a data_store file"); std::io::Write::write_all( &mut time_file, @@ -269,7 +314,7 @@ pub mod scenarios { use super::regtest::{ChildProcessHandler, RegtestManager}; use zingolib::{get_base_address, lightclient::LightClient}; - use self::setup::ClientManager; + use self::setup::ClientBuilder; use super::increase_height_and_sync_client; pub mod setup { @@ -282,7 +327,7 @@ pub mod scenarios { pub struct ScenarioBuilder { pub test_env: TestEnvironmentGenerator, pub regtest_manager: RegtestManager, - pub client_builder: ClientManager, + pub client_builder: ClientBuilder, pub child_process_handler: Option, } impl ScenarioBuilder { @@ -300,7 +345,7 @@ pub mod scenarios { } else { regtest_manager.zingo_datadir.clone() }; - let client_builder = ClientManager::new( + let client_builder = ClientBuilder::new( test_env.get_lightwalletd_uri(), data_dir, data::seeds::ABANDON_ART_SEED, @@ -371,17 +416,17 @@ pub mod scenarios { /// Internally (and perhaps in wider scopes) we say "Sprout" to mean /// take a seed, and generate a client from the seed (planted in the chain). - pub struct ClientManager { + pub struct ClientBuilder { pub server_id: http::Uri, pub zingo_datadir: PathBuf, seed: String, client_number: u8, } - impl ClientManager { + impl ClientBuilder { pub fn new(server_id: http::Uri, zingo_datadir: PathBuf, seed: &str) -> Self { let seed = seed.to_string(); let client_number = 0; - ClientManager { + ClientBuilder { server_id, zingo_datadir, seed, @@ -416,7 +461,7 @@ pub mod scenarios { ) -> LightClient { //! A "faucet" is a lightclient that receives mining rewards let zingo_config = self.make_unique_data_dir_and_load_config(); - LightClient::new_from_wallet_base_async( + LightClient::create_from_wallet_base_async( WalletBase::MnemonicPhrase(self.seed.clone()), &zingo_config, birthday, @@ -432,7 +477,7 @@ pub mod scenarios { overwrite: bool, ) -> LightClient { let zingo_config = self.make_unique_data_dir_and_load_config(); - LightClient::new_from_wallet_base_async( + LightClient::create_from_wallet_base_async( WalletBase::MnemonicPhrase(mnemonic_phrase), &zingo_config, birthday, @@ -515,7 +560,7 @@ pub mod scenarios { } } } - pub fn custom_clients() -> (RegtestManager, ChildProcessHandler, ClientManager) { + pub fn custom_clients() -> (RegtestManager, ChildProcessHandler, ClientBuilder) { let sb = setup::ScenarioBuilder::build_configure_launch( Some(REGSAP_ADDR_FROM_ABANDONART.to_string()), None, @@ -620,6 +665,8 @@ pub mod scenarios { ) } pub mod chainload { + use crate::{build_fvk_client_and_capability, build_fvks_from_wallet_capability}; + use super::*; pub async fn unsynced_basic() -> ChildProcessHandler { @@ -627,6 +674,36 @@ pub mod scenarios { .child_process_handler .unwrap() } + pub async fn unsynced_viewonlyclient_1153( + ) -> (RegtestManager, ChildProcessHandler, LightClient) { + let mut sb = setup::ScenarioBuilder::new_load_1153_saplingcb_regtest_chain(); + let zingo_config = zingolib::load_clientconfig( + sb.client_builder.server_id.clone(), + Some(sb.client_builder.zingo_datadir.clone()), + zingoconfig::ChainType::Regtest, + ) + .unwrap(); + // Create a lightclient to extract a capability from. + let original_recipient = sb.client_builder.build_new_faucet(0, false).await; + // Extract viewing keys + let wallet_capability = original_recipient + .wallet + .wallet_capability() + .read() + .await + .clone(); + // Delete the client after getting the capability. + drop(original_recipient); + // Extract the orchard fvk + let [o_fvk, s_fvk, t_fvk] = build_fvks_from_wallet_capability(&wallet_capability); + let (viewing_client, _) = + build_fvk_client_and_capability(&[&o_fvk, &s_fvk, &t_fvk], &zingo_config).await; + ( + sb.regtest_manager, + sb.child_process_handler.unwrap(), + viewing_client, + ) + } pub async fn faucet_recipient_1153() -> ( RegtestManager, ChildProcessHandler, diff --git a/zingocli/Cargo.toml b/zingocli/Cargo.toml index 1a5a7261c..29fce5bfa 100644 --- a/zingocli/Cargo.toml +++ b/zingocli/Cargo.toml @@ -33,7 +33,7 @@ zcash_primitives = { workspace = true } tempdir = { workspace = true } portpicker = { workspace = true } env_logger = "0.10.0" -zingolib = { path = "../zingolib/", features = ["integration_test"] } +zingolib = { path = "../zingolib/" } bech32 = "0.9.0" rand = "0.8.5" hex = "0.3" diff --git a/zingocli/src/lib.rs b/zingocli/src/lib.rs index 13f60f589..0531f5a4b 100644 --- a/zingocli/src/lib.rs +++ b/zingocli/src/lib.rs @@ -420,13 +420,13 @@ pub fn startup( "Cannot initiate a wallet with a seed and a viewing key simultaneously", )); } - (Some(phrase), None) => Arc::new(LightClient::new_from_wallet_base( + (Some(phrase), None) => Arc::new(LightClient::create_from_wallet_base( WalletBase::MnemonicPhrase(phrase), &config, filled_template.birthday, false, )?), - (None, Some(ufvk)) => Arc::new(LightClient::new_from_wallet_base( + (None, Some(ufvk)) => Arc::new(LightClient::create_from_wallet_base( WalletBase::Ufvk(ufvk), &config, filled_template.birthday, diff --git a/zingocli/tests/darkside/tests.rs b/zingocli/tests/darkside/tests.rs index 38efa842c..14119172a 100644 --- a/zingocli/tests/darkside/tests.rs +++ b/zingocli/tests/darkside/tests.rs @@ -9,7 +9,7 @@ use crate::darkside::{ use json::JsonValue; use tokio::time::sleep; -use zingo_testutils::scenarios::setup::ClientManager; +use zingo_testutils::scenarios::setup::ClientBuilder; use zingolib::{get_base_address, lightclient::LightClient}; use std::sync::Arc; @@ -216,7 +216,7 @@ async fn simple_sync() { .await .unwrap(); - let light_client = ClientManager::new( + let light_client = ClientBuilder::new( server_id, darkside_handler.darkside_dir.clone(), DARKSIDE_SEED, @@ -267,7 +267,7 @@ async fn reorg_away_receipt() { .await .unwrap(); - let light_client = ClientManager::new( + let light_client = ClientBuilder::new( server_id.clone(), darkside_handler.darkside_dir.clone(), DARKSIDE_SEED, @@ -332,7 +332,7 @@ async fn sent_transaction_reorged_into_mempool() { .await .unwrap(); - let mut client_manager = ClientManager::new( + let mut client_manager = ClientBuilder::new( server_id.clone(), darkside_handler.darkside_dir.clone(), DARKSIDE_SEED, diff --git a/zingocli/tests/integration_tests.rs b/zingocli/tests/integration_tests.rs index a6032d5ec..3670cdfa3 100644 --- a/zingocli/tests/integration_tests.rs +++ b/zingocli/tests/integration_tests.rs @@ -2,7 +2,7 @@ #![cfg(feature = "local_env")] pub mod darkside; use std::{fs::File, path::Path}; -use zingo_testutils::{self, data}; +use zingo_testutils::{self, build_fvk_client_and_capability, data}; use bip0039::Mnemonic; use data::seeds::HOSPITAL_MUSEUM_SEED; @@ -11,7 +11,6 @@ use tokio::time::Instant; use zingo_testutils::scenarios; use tracing_test::traced_test; -use zcash_address::unified::Ufvk; use zcash_client_backend::encoding::encode_payment_address; use zcash_primitives::{ consensus::Parameters, @@ -29,7 +28,7 @@ use zingolib::{ extended_transparent::ExtendedPrivKey, unified::{Capability, WalletCapability}, }, - LightWallet, Pool, WalletBase, + LightWallet, Pool, }, }; @@ -90,7 +89,7 @@ async fn dont_write_unconfirmed() { let (wallet, config) = zingo_testutils::load_wallet(wallet_loc.to_path_buf(), ChainType::Regtest).await; - let loaded_client = LightClient::create_with_wallet(wallet, config); + let loaded_client = LightClient::create_from_extant_wallet(wallet, config); let loaded_balance = loaded_client.do_balance().await; assert_eq!( &loaded_balance["unverified_orchard_balance"] @@ -119,7 +118,7 @@ async fn list_transactions_include_foreign() { let wallet_dir = wallet_path.parent().unwrap(); let (wallet, config) = zingo_testutils::load_wallet(wallet_dir.to_path_buf(), ChainType::Mainnet).await; - let client = LightClient::create_with_wallet(wallet, config); + let client = LightClient::create_from_extant_wallet(wallet, config); let transactions = client.do_list_transactions().await[0].clone(); //env_logger::init(); let expected_consumer_ui_note = r#"{ @@ -270,26 +269,6 @@ fn check_expected_balance_with_fvks( } } } -async fn build_fvk_client_capability( - fvks: &[&Fvk], - zingoconfig: &ZingoConfig, -) -> (LightClient, WalletCapability) { - let ufvk = zcash_address::unified::Encoding::encode( - &::try_from_items( - fvks.iter().copied().cloned().collect(), - ) - .unwrap(), - &zcash_address::Network::Regtest, - ); - let viewkey_client = - LightClient::create_unconnected(zingoconfig, WalletBase::Ufvk(ufvk), 0).unwrap(); - let watch_wc = viewkey_client - .extract_unified_capability() - .read() - .await - .clone(); - (viewkey_client, watch_wc) -} #[allow(clippy::too_many_arguments)] fn check_view_capability_bounds( @@ -404,27 +383,14 @@ async fn test_scanning_in_watch_only_mode() { check_client_balances!(original_recipient, o: sent_o_value s: sent_s_value t: sent_t_value); // Extract viewing keys - let wc = original_recipient - .extract_unified_capability() + let wallet_capability = original_recipient + .wallet + .wallet_capability() .read() .await .clone(); - use zingolib::wallet::keys::extended_transparent::ExtendedPubKey; - let o_fvk = Fvk::Orchard( - orchard::keys::FullViewingKey::try_from(&wc) - .unwrap() - .to_bytes(), - ); - let s_fvk = Fvk::Sapling( - zcash_primitives::zip32::sapling::DiversifiableFullViewingKey::try_from(&wc) - .unwrap() - .to_bytes(), - ); - let mut t_fvk_bytes = [0u8; 65]; - let t_ext_pk: ExtendedPubKey = (&wc).try_into().unwrap(); - t_fvk_bytes[0..32].copy_from_slice(&t_ext_pk.chain_code[..]); - t_fvk_bytes[32..65].copy_from_slice(&t_ext_pk.public_key.serialize()[..]); - let t_fvk = Fvk::P2pkh(t_fvk_bytes); + let [o_fvk, s_fvk, t_fvk] = + zingo_testutils::build_fvks_from_wallet_capability(&wallet_capability); let fvks_sets = vec![ vec![&o_fvk], vec![&s_fvk], @@ -439,7 +405,7 @@ async fn test_scanning_in_watch_only_mode() { log::info!(" sapling fvk: {}", fvks.contains(&&s_fvk)); log::info!(" transparent fvk: {}", fvks.contains(&&t_fvk)); - let (watch_client, watch_wc) = build_fvk_client_capability(fvks, &zingo_config).await; + let (watch_client, watch_wc) = build_fvk_client_and_capability(fvks, &zingo_config).await; // assert empty wallet before rescan let balance = watch_client.do_balance().await; check_expected_balance_with_fvks(fvks, balance, 0, 0, 0); @@ -619,7 +585,7 @@ async fn unspent_notes_are_not_saved() { .unwrap(); // Create client based on config and wallet of faucet - let faucet_copy = LightClient::create_with_wallet(faucet_wallet, zingo_config.clone()); + let faucet_copy = LightClient::create_from_extant_wallet(faucet_wallet, zingo_config.clone()); assert_eq!( &faucet_copy.do_seed_phrase().await.unwrap(), &faucet.do_seed_phrase().await.unwrap() @@ -1725,12 +1691,17 @@ async fn witness_clearing() { #[tokio::test] async fn mempool_clearing() { + async fn do_maybe_recent_txid(lc: &LightClient) -> JsonValue { + json::object! { + "last_txid" => lc.wallet.transactions().read().await.get_some_txid_from_highest_wallet_block().map(|t| t.to_string()) + } + } let value = 100_000; let (regtest_manager, child_process_handler, faucet, recipient, orig_transaction_id) = scenarios::faucet_prefunded_orchard_recipient(value).await; assert_eq!( - recipient.do_maybe_recent_txid().await["last_txid"], + do_maybe_recent_txid(&recipient).await["last_txid"], orig_transaction_id ); // Put some transactions unrelated to the recipient (faucet->faucet) on-chain, to get some clutter @@ -1809,7 +1780,7 @@ async fn mempool_clearing() { // 4. The transaction is not yet sent, it is just sitting in the test GRPC server, so remove it from there to make sure it doesn't get mined. assert_eq!( - recipient.do_maybe_recent_txid().await["last_txid"], + do_maybe_recent_txid(&recipient).await["last_txid"], sent_transaction_id ); @@ -2825,34 +2796,45 @@ mod benchmarks { use zingo_testutils::DurationAnnotation; use super::*; - async fn timing_run(client: &str, print_updates: bool) { - let (_, child_process_handler, keyowning, keyless) = - scenarios::chainload::unsynced_faucet_recipient_1153().await; + async fn timing_run(keyownership: &str, print_updates: bool) { let sync_duration; - match client { + match keyownership { "keyowning" => { + let (_, child_process_handler, keyowning, _keyless) = + scenarios::chainload::unsynced_faucet_recipient_1153().await; let timer_start = Instant::now(); keyowning.do_sync(print_updates).await.unwrap(); let timer_stop = Instant::now(); sync_duration = timer_stop.duration_since(timer_start); + drop(child_process_handler); } "keyless" => { + let (_, child_process_handler, _keyowning, keyless) = + scenarios::chainload::unsynced_faucet_recipient_1153().await; let timer_start = Instant::now(); keyless.do_sync(print_updates).await.unwrap(); let timer_stop = Instant::now(); sync_duration = timer_stop.duration_since(timer_start); + drop(child_process_handler); + } + "fullviewonly" => { + let (_, child_process_handler, view_only_client) = + scenarios::chainload::unsynced_viewonlyclient_1153().await; + let timer_start = Instant::now(); + view_only_client.do_sync(print_updates).await.unwrap(); + let timer_stop = Instant::now(); + sync_duration = timer_stop.duration_since(timer_start); + drop(child_process_handler); } _ => panic!(), } let annotation = DurationAnnotation::new( - format!("{PREFIX}_{client}_client_pu_{print_updates}"), + format!("{PREFIX}_{keyownership}_client_pu_{print_updates}"), sync_duration, ); zingo_testutils::record_time(&annotation); assert!(sync_duration.as_secs() < 1000); - - drop(child_process_handler); } #[tokio::test] async fn keyless_client_pu_true() { @@ -2863,13 +2845,21 @@ mod benchmarks { timing_run("keyless", false).await; } #[tokio::test] - async fn keyowning_client_true() { + async fn keyowning_client_pu_true() { timing_run("keyowning", true).await; } #[tokio::test] - async fn keyowning_client_false() { + async fn keyowning_client_pu_false() { timing_run("keyowning", false).await; } + #[tokio::test] + async fn fullviewonly_client_pu_true() { + timing_run("fullviewonly", true).await; + } + #[tokio::test] + async fn fullviewonly_client_pu_false() { + timing_run("fullviewonly", false).await; + } } } pub const TEST_SEED: &str = "chimney better bulb horror rebuild whisper improve intact letter giraffe brave rib appear bulk aim burst snap salt hill sad merge tennis phrase raise"; diff --git a/zingolib/Cargo.toml b/zingolib/Cargo.toml index f6f9ab462..b99f9940e 100644 --- a/zingolib/Cargo.toml +++ b/zingolib/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" [features] default = ["embed_params"] -integration_test = [] embed_params = [] [dependencies] diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index b6906e31f..2e6e1794f 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -1455,41 +1455,3 @@ pub fn do_user_command(cmd: &str, args: &[&str], lightclient: &LightClient) -> S ), } } - -#[cfg(test)] -pub mod tests { - use super::do_user_command; - use crate::{lightclient::LightClient, wallet::WalletBase}; - use lazy_static::lazy_static; - use tokio::runtime::Runtime; - use zingoconfig::ZingoConfig; - - lazy_static! { - static ref TEST_SEED: String = "youth strong sweet gorilla hammer unhappy congress stamp left stereo riot salute road tag clean toilet artefact fork certain leopard entire civil degree wonder".to_string(); - } - - pub fn test_command_caseinsensitive() { - let lc = Runtime::new() - .unwrap() - .block_on(LightClient::test_new( - &ZingoConfig::create_unconnected(zingoconfig::ChainType::FakeMainnet, None), - WalletBase::MnemonicPhrase(TEST_SEED.to_string()), - 0, - )) - .unwrap(); - - assert_eq!( - do_user_command("addresses", &[], &lc), - do_user_command("AddReSSeS", &[], &lc) - ); - assert_eq!( - do_user_command("addresses", &[], &lc), - do_user_command("Addresses", &[], &lc) - ); - } - - #[test] - pub fn test_nosync_commands() { - // The following commands should run - } -} diff --git a/zingolib/src/lightclient.rs b/zingolib/src/lightclient.rs index 02503c49d..83e6c978c 100644 --- a/zingolib/src/lightclient.rs +++ b/zingolib/src/lightclient.rs @@ -88,144 +88,165 @@ pub struct LightClient { bsync_data: Arc>, interrupt_sync: Arc>, } - -use serde_json::Value; - -enum PriceFetchError { - ReqwestError(String), - NotJson, - NoElements, - PriceReprError(PriceReprError), - NanValue, -} - -impl std::fmt::Display for PriceFetchError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use PriceFetchError::*; - f.write_str(&match self { - ReqwestError(e) => format!("ReqwestError: {}", e), - NotJson => "NotJson".to_string(), - NoElements => "NoElements".to_string(), - PriceReprError(e) => format!("PriceReprError: {}", e), - NanValue => "NanValue".to_string(), - }) +impl LightClient { + pub fn create_from_extant_wallet(wallet: LightWallet, config: ZingoConfig) -> Self { + LightClient { + wallet, + config: config.clone(), + mempool_monitor: std::sync::RwLock::new(None), + sync_lock: Mutex::new(()), + bsync_data: Arc::new(RwLock::new(BlazeSyncData::new(&config))), + interrupt_sync: Arc::new(RwLock::new(false)), + } } -} - -enum PriceReprError { - NoValue, - NoAsStrValue, - NotParseable, -} - -impl std::fmt::Display for PriceReprError { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use PriceReprError::*; - fmt.write_str(match self { - NoValue => "NoValue", - NoAsStrValue => "NoAsStrValue", - NotParseable => "NotParseable", + /// The wallet this fn associates with the lightclient is specifically derived from + /// a spend authority. + pub fn create_from_wallet_base( + wallet_base: WalletBase, + config: &ZingoConfig, + birthday: u64, + overwrite: bool, + ) -> io::Result { + Runtime::new().unwrap().block_on(async move { + LightClient::create_from_wallet_base_async(wallet_base, config, birthday, overwrite) + .await }) } -} -fn repr_price_as_f64(from_gemini: &Value) -> Result { - if let Some(value) = from_gemini.get("price") { - if let Some(stringable) = value.as_str() { - if let Ok(parsed) = stringable.parse::() { - Ok(parsed) - } else { - Err(PriceReprError::NotParseable) + /// The wallet this fn associates with the lightclient is specifically derived from + /// a spend authority. + pub async fn create_from_wallet_base_async( + wallet_base: WalletBase, + config: &ZingoConfig, + birthday: u64, + overwrite: bool, + ) -> io::Result { + #[cfg(not(any(target_os = "ios", target_os = "android")))] + { + if !overwrite && config.wallet_exists() { + return Err(Error::new( + ErrorKind::AlreadyExists, + format!( + "Cannot create a new wallet from seed, because a wallet already exists at:\n{:?}", + config.get_wallet_path().as_os_str() + ), + )); } - } else { - Err(PriceReprError::NoAsStrValue) } - } else { - Err(PriceReprError::NoValue) - } -} - -async fn get_recent_median_price_from_gemini() -> Result { - let httpget = - match reqwest::get("https://api.gemini.com/v1/trades/zecusd?limit_trades=11").await { - Ok(httpresponse) => httpresponse, - Err(e) => { - return Err(PriceFetchError::ReqwestError(e.to_string())); - } + let lightclient = LightClient { + wallet: LightWallet::new(config.clone(), wallet_base, birthday)?, + config: config.clone(), + mempool_monitor: std::sync::RwLock::new(None), + sync_lock: Mutex::new(()), + bsync_data: Arc::new(RwLock::new(BlazeSyncData::new(config))), + interrupt_sync: Arc::new(RwLock::new(false)), }; - let serialized = match httpget.json::().await { - Ok(asjson) => asjson, - Err(_) => { - return Err(PriceFetchError::NotJson); - } - }; - let elements = match serialized.as_array() { - Some(elements) => elements, - None => { - return Err(PriceFetchError::NoElements); - } - }; - let mut trades: Vec = match elements.iter().map(repr_price_as_f64).collect() { - Ok(trades) => trades, - Err(e) => { - return Err(PriceFetchError::PriceReprError(e)); - } - }; - if trades.iter().any(|x| x.is_nan()) { - return Err(PriceFetchError::NanValue); - } - // NOTE: This code will panic if a value is received that: - // 1. was parsed from a string to an f64 - // 2. is not a NaN - // 3. cannot be compared to an f64 - // TODO: Show that this is impossible, or write code to handle - // that case. - trades.sort_by(|a, b| { - a.partial_cmp(b) - .expect("a and b are non-nan f64, I think that makes them comparable") - }); - Ok(trades[5]) -} -#[cfg(any(test, feature = "integration_test"))] -impl LightClient { - /// Method to create a test-only version of the LightClient - pub async fn test_new( + lightclient.set_wallet_initial_state(birthday).await; + lightclient + .do_save() + .await + .map_err(|e| Error::new(ErrorKind::InvalidData, e))?; + + debug!("Created new wallet!"); + + Ok(lightclient) + } + pub fn create_unconnected( config: &ZingoConfig, wallet_base: WalletBase, height: u64, ) -> io::Result { - let l = LightClient::create_unconnected(config, wallet_base, height) - .expect("Unconnected client creation failed!"); - l.set_wallet_initial_state(height).await; + Ok(LightClient { + wallet: LightWallet::new(config.clone(), wallet_base, height)?, + config: config.clone(), + mempool_monitor: std::sync::RwLock::new(None), + bsync_data: Arc::new(RwLock::new(BlazeSyncData::new(config))), + sync_lock: Mutex::new(()), + interrupt_sync: Arc::new(RwLock::new(false)), + }) + } - debug!("Created new wallet!"); - debug!("Created LightClient to {}", &config.get_lightwalletd_uri()); - Ok(l) + fn create_with_new_wallet(config: &ZingoConfig, height: u64) -> io::Result { + Runtime::new().unwrap().block_on(async move { + let l = LightClient::create_unconnected(config, WalletBase::FreshEntropy, height)?; + l.set_wallet_initial_state(height).await; + + debug!("Created new wallet with a new seed!"); + debug!("Created LightClient to {}", &config.get_lightwalletd_uri()); + + // Save + l.do_save() + .await + .map_err(|s| io::Error::new(ErrorKind::PermissionDenied, s))?; + + Ok(l) + }) } - pub async fn do_maybe_recent_txid(&self) -> JsonValue { - object! { - "last_txid" => self.wallet.transactions().read().await.get_some_txid_from_highest_wallet_block().map(|t| t.to_string()) + /// Create a brand new wallet with a new seed phrase. Will fail if a wallet file + /// already exists on disk + pub fn new(config: &ZingoConfig, latest_block: u64) -> io::Result { + #[cfg(not(any(target_os = "ios", target_os = "android")))] + { + if config.wallet_exists() { + return Err(Error::new( + ErrorKind::AlreadyExists, + "Cannot create a new wallet from seed, because a wallet already exists", + )); + } } + + Self::create_with_new_wallet(config, latest_block) + } + /// This constructor depends on a wallet that's read from a buffer. + /// It is used internally by read_from_disk, and directly called by + /// zingo-mobile. + pub fn read_wallet_from_buffer(config: &ZingoConfig, reader: R) -> io::Result { + Runtime::new() + .unwrap() + .block_on(async move { Self::read_wallet_from_buffer_async(config, reader).await }) } -} -#[cfg(feature = "integration_test")] -impl LightClient { - pub fn create_with_wallet(wallet: LightWallet, config: ZingoConfig) -> Self { - LightClient { + pub async fn read_wallet_from_buffer_async( + config: &ZingoConfig, + mut reader: R, + ) -> io::Result { + let wallet = LightWallet::read_internal(&mut reader, config).await?; + + let lc = LightClient { wallet, config: config.clone(), mempool_monitor: std::sync::RwLock::new(None), sync_lock: Mutex::new(()), - bsync_data: Arc::new(RwLock::new(BlazeSyncData::new(&config))), + bsync_data: Arc::new(RwLock::new(BlazeSyncData::new(config))), interrupt_sync: Arc::new(RwLock::new(false)), - } + }; + + debug!( + "Read wallet with birthday {}", + lc.wallet.get_birthday().await + ); + debug!("Created LightClient to {}", &config.get_lightwalletd_uri()); + + Ok(lc) } - pub fn extract_unified_capability(&self) -> Arc> { - self.wallet.wallet_capability() + + pub fn read_wallet_from_disk(config: &ZingoConfig) -> io::Result { + let wallet_path = if config.wallet_exists() { + config.get_wallet_path() + } else { + return Err(Error::new( + ErrorKind::AlreadyExists, + format!( + "Cannot read wallet. No file at {}", + config.get_wallet_path().display() + ), + )); + }; + LightClient::read_wallet_from_buffer(config, BufReader::new(File::open(wallet_path)?)) } } + impl LightClient { fn add_nonchange_notes<'a, 'b, 'c>( &'a self, @@ -342,21 +363,6 @@ impl LightClient { &self.config } - pub fn create_unconnected( - config: &ZingoConfig, - wallet_base: WalletBase, - height: u64, - ) -> io::Result { - Ok(LightClient { - wallet: LightWallet::new(config.clone(), wallet_base, height)?, - config: config.clone(), - mempool_monitor: std::sync::RwLock::new(None), - bsync_data: Arc::new(RwLock::new(BlazeSyncData::new(config))), - sync_lock: Mutex::new(()), - interrupt_sync: Arc::new(RwLock::new(false)), - }) - } - pub async fn do_addresses(&self) -> JsonValue { let mut objectified_addresses = Vec::new(); for address in self.wallet.wallet_capability().read().await.addresses() { @@ -729,81 +735,6 @@ impl LightClient { JsonValue::Array(consumer_ui_notes) } - async fn value_transfer_by_to_address(&self) -> finsight::ValuesSentToAddress { - let summaries = self.do_list_txsummaries().await; - let mut amount_by_address = HashMap::new(); - for summary in summaries { - use ValueTransferKind::*; - match summary.kind { - Sent { amount, to_address } => { - let address = to_address.encode(); - if let std::collections::hash_map::Entry::Vacant(e) = - amount_by_address.entry(address.clone()) - { - e.insert(vec![amount]); - } else { - amount_by_address - .get_mut(&address) - .expect("a vec of u64") - .push(amount); - }; - } - Fee { amount } => { - let fee_key = "fee".to_string(); - if let std::collections::hash_map::Entry::Vacant(e) = - amount_by_address.entry(fee_key.clone()) - { - e.insert(vec![amount]); - } else { - amount_by_address - .get_mut(&fee_key) - .expect("a vec of u64.") - .push(amount); - }; - } - SendToSelf { .. } | Received { .. } => (), - } - } - finsight::ValuesSentToAddress(amount_by_address) - } - - pub async fn do_total_memobytes_to_address(&self) -> finsight::TotalMemoBytesToAddress { - let summaries = self.do_list_txsummaries().await; - let mut memobytes_by_address = HashMap::new(); - for summary in summaries { - use ValueTransferKind::*; - match summary.kind { - Sent { to_address, .. } => { - let address = to_address.encode(); - let bytes = summary.memos.iter().fold(0, |sum, m| sum + m.len()); - memobytes_by_address - .entry(address) - .and_modify(|e| *e += bytes) - .or_insert(bytes); - } - SendToSelf { .. } | Received { .. } | Fee { .. } => (), - } - } - finsight::TotalMemoBytesToAddress(memobytes_by_address) - } - pub async fn do_total_value_to_address(&self) -> finsight::TotalValueToAddress { - let values_sent_to_addresses = self.value_transfer_by_to_address().await; - let mut by_address_total = HashMap::new(); - for key in values_sent_to_addresses.0.keys() { - let sum = values_sent_to_addresses.0[key].iter().sum(); - by_address_total.insert(key.clone(), sum); - } - finsight::TotalValueToAddress(by_address_total) - } - pub async fn do_total_spends_to_address(&self) -> finsight::TotalSendsToAddress { - let values_sent_to_addresses = self.value_transfer_by_to_address().await; - let mut by_address_number_sends = HashMap::new(); - for key in values_sent_to_addresses.0.keys() { - let number_sends = values_sent_to_addresses.0[key].len() as u64; - by_address_number_sends.insert(key.clone(), number_sends); - } - finsight::TotalSendsToAddress(by_address_number_sends) - } pub async fn do_list_txsummaries(&self) -> Vec { let mut summaries: Vec = Vec::new(); @@ -858,7 +789,6 @@ impl LightClient { Ok(array![new_address.encode(&self.config.chain)]) } - pub async fn do_rescan(&self) -> Result { debug!("Rescan starting"); @@ -875,7 +805,6 @@ impl LightClient { response } - pub async fn do_save(&self) -> Result<(), String> { #[cfg(any(target_os = "ios", target_os = "android"))] // on mobile platforms, disable the save, because the saves will be handled by the native layer, and not in rust @@ -944,7 +873,6 @@ impl LightClient { .unwrap() .block_on(async move { self.do_seed_phrase().await }) } - //TODO: Add migrate_sapling_to_orchard argument pub async fn do_send( &self, @@ -1115,146 +1043,102 @@ impl LightClient { .clone() } - pub async fn do_wallet_last_scanned_height(&self) -> JsonValue { - json::JsonValue::from(self.wallet.last_synced_height().await) - } - - pub async fn get_initial_state(&self, height: u64) -> Option<(u64, String, String)> { - if height <= self.config.sapling_activation_height() { - return None; - } - - debug!( - "Getting sapling tree from LightwalletD at height {}", - height - ); - match GrpcConnector::get_trees(self.config.get_lightwalletd_uri(), height).await { - Ok(tree_state) => { - let hash = tree_state.hash.clone(); - let tree = tree_state.sapling_tree.clone(); - Some((tree_state.height, hash, tree)) - } - Err(e) => { - error!("Error getting sapling tree:{e}."); - None + pub async fn do_total_memobytes_to_address(&self) -> finsight::TotalMemoBytesToAddress { + let summaries = self.do_list_txsummaries().await; + let mut memobytes_by_address = HashMap::new(); + for summary in summaries { + use ValueTransferKind::*; + match summary.kind { + Sent { to_address, .. } => { + let address = to_address.encode(); + let bytes = summary.memos.iter().fold(0, |sum, m| sum + m.len()); + memobytes_by_address + .entry(address) + .and_modify(|e| *e += bytes) + .or_insert(bytes); + } + SendToSelf { .. } | Received { .. } | Fee { .. } => (), } } + finsight::TotalMemoBytesToAddress(memobytes_by_address) } - pub fn get_server(&self) -> std::sync::RwLockReadGuard { - self.config.lightwalletd_uri.read().unwrap() - } - - pub fn get_server_uri(&self) -> http::Uri { - self.config.get_lightwalletd_uri() - } - - async fn get_submission_height(&self) -> Result { - Ok(BlockHeight::from_u32( - GrpcConnector::get_latest_block(self.config.get_lightwalletd_uri()) - .await? - .height as u32, - ) + 1) - } - - pub(crate) async fn get_sync_interrupt(&self) -> bool { - *self.interrupt_sync.read().await - } - - pub fn init_logging() -> io::Result<()> { - // Configure logging first. - LOG_INIT.call_once(tracing_subscriber::fmt::init); - - Ok(()) - } - pub async fn interrupt_sync_after_batch(&self, set_interrupt: bool) { - *self.interrupt_sync.write().await = set_interrupt; - } - /// Create a brand new wallet with a new seed phrase. Will fail if a wallet file - /// already exists on disk - pub fn new(config: &ZingoConfig, latest_block: u64) -> io::Result { - #[cfg(not(any(target_os = "ios", target_os = "android")))] - { - if config.wallet_exists() { - return Err(Error::new( - ErrorKind::AlreadyExists, - "Cannot create a new wallet from seed, because a wallet already exists", - )); - } + pub async fn do_total_spends_to_address(&self) -> finsight::TotalSendsToAddress { + let values_sent_to_addresses = self.value_transfer_by_to_address().await; + let mut by_address_number_sends = HashMap::new(); + for key in values_sent_to_addresses.0.keys() { + let number_sends = values_sent_to_addresses.0[key].len() as u64; + by_address_number_sends.insert(key.clone(), number_sends); } - - Self::new_wallet(config, latest_block) - } - /// The wallet this fn associates with the lightclient is specifically derived from - /// a spend authority. - pub fn new_from_wallet_base( - wallet_base: WalletBase, - config: &ZingoConfig, - birthday: u64, - overwrite: bool, - ) -> io::Result { - Runtime::new().unwrap().block_on(async move { - LightClient::new_from_wallet_base_async(wallet_base, config, birthday, overwrite).await - }) + finsight::TotalSendsToAddress(by_address_number_sends) } - /// The wallet this fn associates with the lightclient is specifically derived from - /// a spend authority. - pub async fn new_from_wallet_base_async( - wallet_base: WalletBase, - config: &ZingoConfig, - birthday: u64, - overwrite: bool, - ) -> io::Result { - #[cfg(not(any(target_os = "ios", target_os = "android")))] - { - if !overwrite && config.wallet_exists() { - return Err(Error::new( - ErrorKind::AlreadyExists, - format!( - "Cannot create a new wallet from seed, because a wallet already exists at:\n{:?}", - config.get_wallet_path().as_os_str() - ), - )); - } + pub async fn do_total_value_to_address(&self) -> finsight::TotalValueToAddress { + let values_sent_to_addresses = self.value_transfer_by_to_address().await; + let mut by_address_total = HashMap::new(); + for key in values_sent_to_addresses.0.keys() { + let sum = values_sent_to_addresses.0[key].iter().sum(); + by_address_total.insert(key.clone(), sum); + } + finsight::TotalValueToAddress(by_address_total) + } + + pub async fn do_wallet_last_scanned_height(&self) -> JsonValue { + json::JsonValue::from(self.wallet.last_synced_height().await) + } + + pub async fn get_initial_state(&self, height: u64) -> Option<(u64, String, String)> { + if height <= self.config.sapling_activation_height() { + return None; } - let lightclient = LightClient { - wallet: LightWallet::new(config.clone(), wallet_base, birthday)?, - config: config.clone(), - mempool_monitor: std::sync::RwLock::new(None), - sync_lock: Mutex::new(()), - bsync_data: Arc::new(RwLock::new(BlazeSyncData::new(config))), - interrupt_sync: Arc::new(RwLock::new(false)), - }; - lightclient.set_wallet_initial_state(birthday).await; - lightclient - .do_save() - .await - .map_err(|e| Error::new(ErrorKind::InvalidData, e))?; + debug!( + "Getting sapling tree from LightwalletD at height {}", + height + ); + match GrpcConnector::get_trees(self.config.get_lightwalletd_uri(), height).await { + Ok(tree_state) => { + let hash = tree_state.hash.clone(); + let tree = tree_state.sapling_tree.clone(); + Some((tree_state.height, hash, tree)) + } + Err(e) => { + error!("Error getting sapling tree:{e}."); + None + } + } + } - debug!("Created new wallet!"); + pub fn get_server(&self) -> std::sync::RwLockReadGuard { + self.config.lightwalletd_uri.read().unwrap() + } - Ok(lightclient) + pub fn get_server_uri(&self) -> http::Uri { + self.config.get_lightwalletd_uri() } - fn new_wallet(config: &ZingoConfig, height: u64) -> io::Result { - Runtime::new().unwrap().block_on(async move { - let l = LightClient::create_unconnected(config, WalletBase::FreshEntropy, height)?; - l.set_wallet_initial_state(height).await; + async fn get_submission_height(&self) -> Result { + Ok(BlockHeight::from_u32( + GrpcConnector::get_latest_block(self.config.get_lightwalletd_uri()) + .await? + .height as u32, + ) + 1) + } - debug!("Created new wallet with a new seed!"); - debug!("Created LightClient to {}", &config.get_lightwalletd_uri()); + pub(crate) async fn get_sync_interrupt(&self) -> bool { + *self.interrupt_sync.read().await + } - // Save - l.do_save() - .await - .map_err(|s| io::Error::new(ErrorKind::PermissionDenied, s))?; + pub fn init_logging() -> io::Result<()> { + // Configure logging first. + LOG_INIT.call_once(tracing_subscriber::fmt::init); - Ok(l) - }) + Ok(()) } + pub async fn interrupt_sync_after_batch(&self, set_interrupt: bool) { + *self.interrupt_sync.write().await = set_interrupt; + } #[cfg(feature = "embed_params")] fn read_sapling_params(&self) -> Result<(Vec, Vec), String> { // Read Sapling Params @@ -1277,7 +1161,6 @@ impl LightClient { Ok((sapling_output, sapling_spend)) } - #[cfg(not(feature = "embed_params"))] fn read_sapling_params(&self) -> Result<(Vec, Vec), String> { let path = self @@ -1302,54 +1185,6 @@ impl LightClient { Ok((sapling_output, sapling_spend)) } - /// This constructor depends on a wallet that's read from a buffer. - /// It is used internally by read_from_disk, and directly called by - /// zingo-mobile. - pub fn read_wallet_from_buffer(config: &ZingoConfig, reader: R) -> io::Result { - Runtime::new() - .unwrap() - .block_on(async move { Self::read_wallet_from_buffer_async(config, reader).await }) - } - - pub async fn read_wallet_from_buffer_async( - config: &ZingoConfig, - mut reader: R, - ) -> io::Result { - let wallet = LightWallet::read_internal(&mut reader, config).await?; - - let lc = LightClient { - wallet, - config: config.clone(), - mempool_monitor: std::sync::RwLock::new(None), - sync_lock: Mutex::new(()), - bsync_data: Arc::new(RwLock::new(BlazeSyncData::new(config))), - interrupt_sync: Arc::new(RwLock::new(false)), - }; - - debug!( - "Read wallet with birthday {}", - lc.wallet.get_birthday().await - ); - debug!("Created LightClient to {}", &config.get_lightwalletd_uri()); - - Ok(lc) - } - - pub fn read_wallet_from_disk(config: &ZingoConfig) -> io::Result { - let wallet_path = if config.wallet_exists() { - config.get_wallet_path() - } else { - return Err(Error::new( - ErrorKind::AlreadyExists, - format!( - "Cannot read wallet. No file at {}", - config.get_wallet_path().display() - ), - )); - }; - LightClient::read_wallet_from_buffer(config, BufReader::new(File::open(wallet_path)?)) - } - pub fn set_sapling_params( &mut self, sapling_output: &[u8], @@ -1965,6 +1800,7 @@ impl LightClient { } }; } + pub(crate) async fn update_current_price(&self) -> String { // Get the zec price from the server match get_recent_median_price_from_gemini().await { @@ -1978,6 +1814,43 @@ impl LightClient { } } } + async fn value_transfer_by_to_address(&self) -> finsight::ValuesSentToAddress { + let summaries = self.do_list_txsummaries().await; + let mut amount_by_address = HashMap::new(); + for summary in summaries { + use ValueTransferKind::*; + match summary.kind { + Sent { amount, to_address } => { + let address = to_address.encode(); + if let std::collections::hash_map::Entry::Vacant(e) = + amount_by_address.entry(address.clone()) + { + e.insert(vec![amount]); + } else { + amount_by_address + .get_mut(&address) + .expect("a vec of u64") + .push(amount); + }; + } + Fee { amount } => { + let fee_key = "fee".to_string(); + if let std::collections::hash_map::Entry::Vacant(e) = + amount_by_address.entry(fee_key.clone()) + { + e.insert(vec![amount]); + } else { + amount_by_address + .get_mut(&fee_key) + .expect("a vec of u64.") + .push(amount); + }; + } + SendToSelf { .. } | Received { .. } => (), + } + } + finsight::ValuesSentToAddress(amount_by_address) + } fn write_file_if_not_exists(dir: &Path, name: &str, bytes: &[u8]) -> io::Result<()> { let mut file_path = dir.to_path_buf(); @@ -1990,6 +1863,102 @@ impl LightClient { Ok(()) } } +use serde_json::Value; + +enum PriceFetchError { + ReqwestError(String), + NotJson, + NoElements, + PriceReprError(PriceReprError), + NanValue, +} + +impl std::fmt::Display for PriceFetchError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use PriceFetchError::*; + f.write_str(&match self { + ReqwestError(e) => format!("ReqwestError: {}", e), + NotJson => "NotJson".to_string(), + NoElements => "NoElements".to_string(), + PriceReprError(e) => format!("PriceReprError: {}", e), + NanValue => "NanValue".to_string(), + }) + } +} + +enum PriceReprError { + NoValue, + NoAsStrValue, + NotParseable, +} + +impl std::fmt::Display for PriceReprError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use PriceReprError::*; + fmt.write_str(match self { + NoValue => "NoValue", + NoAsStrValue => "NoAsStrValue", + NotParseable => "NotParseable", + }) + } +} +fn repr_price_as_f64(from_gemini: &Value) -> Result { + if let Some(value) = from_gemini.get("price") { + if let Some(stringable) = value.as_str() { + if let Ok(parsed) = stringable.parse::() { + Ok(parsed) + } else { + Err(PriceReprError::NotParseable) + } + } else { + Err(PriceReprError::NoAsStrValue) + } + } else { + Err(PriceReprError::NoValue) + } +} + +async fn get_recent_median_price_from_gemini() -> Result { + let httpget = + match reqwest::get("https://api.gemini.com/v1/trades/zecusd?limit_trades=11").await { + Ok(httpresponse) => httpresponse, + Err(e) => { + return Err(PriceFetchError::ReqwestError(e.to_string())); + } + }; + let serialized = match httpget.json::().await { + Ok(asjson) => asjson, + Err(_) => { + return Err(PriceFetchError::NotJson); + } + }; + let elements = match serialized.as_array() { + Some(elements) => elements, + None => { + return Err(PriceFetchError::NoElements); + } + }; + let mut trades: Vec = match elements.iter().map(repr_price_as_f64).collect() { + Ok(trades) => trades, + Err(e) => { + return Err(PriceFetchError::PriceReprError(e)); + } + }; + if trades.iter().any(|x| x.is_nan()) { + return Err(PriceFetchError::NanValue); + } + // NOTE: This code will panic if a value is received that: + // 1. was parsed from a string to an f64 + // 2. is not a NaN + // 3. cannot be compared to an f64 + // TODO: Show that this is impossible, or write code to handle + // that case. + trades.sort_by(|a, b| { + a.partial_cmp(b) + .expect("a and b are non-nan f64, I think that makes them comparable") + }); + Ok(trades[5]) +} #[cfg(test)] mod tests { @@ -2008,7 +1977,7 @@ mod tests { let wallet_name = data_dir.join("zingo-wallet.dat"); let config = ZingoConfig::create_unconnected(ChainType::FakeMainnet, Some(data_dir)); - let lc = LightClient::new_from_wallet_base( + let lc = LightClient::create_from_wallet_base( WalletBase::MnemonicPhrase(TEST_SEED.to_string()), &config, 0, @@ -2018,7 +1987,7 @@ mod tests { assert_eq!( format!( "{:?}", - LightClient::new_from_wallet_base( + LightClient::create_from_wallet_base( WalletBase::MnemonicPhrase(TEST_SEED.to_string()), &config, 0, diff --git a/zingolib/src/wallet/keys.rs b/zingolib/src/wallet/keys.rs index e21067bea..842d366f2 100644 --- a/zingolib/src/wallet/keys.rs +++ b/zingolib/src/wallet/keys.rs @@ -11,14 +11,8 @@ use zcash_primitives::{ }; use zingoconfig::ZingoConfig; -#[cfg(feature = "integration_test")] pub mod extended_transparent; -#[cfg(not(feature = "integration_test"))] -pub(crate) mod extended_transparent; -#[cfg(feature = "integration_test")] pub mod unified; -#[cfg(not(feature = "integration_test"))] -pub(crate) mod unified; /// Sha256(Sha256(value)) pub fn double_sha256(payload: &[u8]) -> Vec {