diff --git a/darkside-tests/src/chain_generics.rs b/darkside-tests/src/chain_generics.rs index 0f2acb5b78..e9839935ca 100644 --- a/darkside-tests/src/chain_generics.rs +++ b/darkside-tests/src/chain_generics.rs @@ -64,8 +64,8 @@ pub(crate) mod conduct_chain { } /// the mock chain is fed to the Client via lightwalletd. where is that server to be found? - fn lightserver_uri(&self) -> Option { - Some(self.client_builder.server_id.clone()) + fn indexer_uri(&self) -> Option { + Some(self.client_builder.indexer_uri.clone().expect("some uri")) } async fn create_faucet(&mut self) -> LightClient { @@ -76,13 +76,13 @@ pub(crate) mod conduct_chain { .make_unique_data_dir_and_load_config(self.configured_activation_heights); let mut lightclient = LightClient::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Mnemonic { mnemonic: Mnemonic::from_phrase(DARKSIDE_SEED.to_string()).unwrap(), no_of_accounts: NonZeroU32::try_from(1).expect("hard-coded integer"), }, 1.into(), - config.wallet_settings(), + config.wallet_settings.clone(), ) .unwrap(), config, @@ -105,7 +105,7 @@ pub(crate) mod conduct_chain { async fn increase_chain_height(&mut self) { let height_before = - zingolib::grpc_connector::get_latest_block(self.lightserver_uri().unwrap()) + zingolib::grpc_connector::get_latest_block(self.indexer_uri().unwrap()) .await .unwrap() .height; @@ -124,7 +124,7 @@ pub(crate) mod conduct_chain { // trees let trees = zingolib::grpc_connector::get_trees( - self.client_builder.server_id.clone(), + self.client_builder.indexer_uri.clone().expect("some uri"), height_before, ) .await diff --git a/darkside-tests/src/utils.rs b/darkside-tests/src/utils.rs index 7a339e34a8..3e737dcefc 100644 --- a/darkside-tests/src/utils.rs +++ b/darkside-tests/src/utils.rs @@ -360,7 +360,7 @@ pub mod scenarios { ) -> DarksideEnvironment { let (lightwalletd, darkside_connector) = init_darksidewalletd(set_port).await.unwrap(); let client_builder = ClientBuilder::new( - darkside_connector.0.clone(), + Some(darkside_connector.0.clone()), zingolib::testutils::tempfile::tempdir().unwrap(), ); let configured_activation_heights = ActivationHeights::default(); diff --git a/darkside-tests/tests/advanced_reorg_tests.rs b/darkside-tests/tests/advanced_reorg_tests.rs index f5b6db2bc7..d37f5b3a58 100644 --- a/darkside-tests/tests/advanced_reorg_tests.rs +++ b/darkside-tests/tests/advanced_reorg_tests.rs @@ -36,7 +36,7 @@ async fn reorg_changes_incoming_tx_height() { .unwrap(); let wallet_dir = TempDir::new().unwrap(); - let mut light_client = ClientBuilder::new(server_id.clone(), wallet_dir).build_client( + let mut light_client = ClientBuilder::new(Some(server_id.clone()), wallet_dir).build_client( ADVANCED_REORG_TESTS_USER_WALLET.to_string(), 202, true, @@ -194,7 +194,7 @@ async fn reorg_changes_incoming_tx_index() { .unwrap(); let wallet_dir = TempDir::new().unwrap(); - let mut light_client = ClientBuilder::new(server_id.clone(), wallet_dir).build_client( + let mut light_client = ClientBuilder::new(Some(server_id.clone()), wallet_dir).build_client( ADVANCED_REORG_TESTS_USER_WALLET.to_string(), 202, true, @@ -352,7 +352,7 @@ async fn reorg_expires_incoming_tx() { .unwrap(); let wallet_dir = TempDir::new().unwrap(); - let mut light_client = ClientBuilder::new(server_id.clone(), wallet_dir).build_client( + let mut light_client = ClientBuilder::new(Some(server_id.clone()), wallet_dir).build_client( ADVANCED_REORG_TESTS_USER_WALLET.to_string(), 202, true, @@ -532,7 +532,7 @@ async fn reorg_changes_outgoing_tx_height() { .unwrap(); let wallet_dir = TempDir::new().unwrap(); - let mut light_client = ClientBuilder::new(server_id.clone(), wallet_dir).build_client( + let mut light_client = ClientBuilder::new(Some(server_id.clone()), wallet_dir).build_client( ADVANCED_REORG_TESTS_USER_WALLET.to_string(), 202, true, @@ -787,7 +787,7 @@ async fn reorg_expires_outgoing_tx_height() { .unwrap(); let wallet_dir = TempDir::new().unwrap(); - let mut light_client = ClientBuilder::new(server_id.clone(), wallet_dir).build_client( + let mut light_client = ClientBuilder::new(Some(server_id.clone()), wallet_dir).build_client( ADVANCED_REORG_TESTS_USER_WALLET.to_string(), 202, true, @@ -987,7 +987,7 @@ async fn reorg_changes_outgoing_tx_index() { .unwrap(); let wallet_dir = TempDir::new().unwrap(); - let mut light_client = ClientBuilder::new(server_id.clone(), wallet_dir).build_client( + let mut light_client = ClientBuilder::new(Some(server_id.clone()), wallet_dir).build_client( ADVANCED_REORG_TESTS_USER_WALLET.to_string(), 202, true, diff --git a/darkside-tests/tests/tests.rs b/darkside-tests/tests/tests.rs index efd1f60722..03b81b0cf9 100644 --- a/darkside-tests/tests/tests.rs +++ b/darkside-tests/tests/tests.rs @@ -25,7 +25,7 @@ async fn simple_sync() { .unwrap(); let activation_heights = ActivationHeights::default(); let wallet_dir = TempDir::new().unwrap(); - let mut light_client = ClientBuilder::new(server_id, wallet_dir).build_client( + let mut light_client = ClientBuilder::new(Some(server_id), wallet_dir).build_client( DARKSIDE_SEED.to_string(), 1, true, @@ -69,7 +69,7 @@ async fn reorg_receipt_sync_generic() { let activation_heights = ActivationHeights::default(); let wallet_dir = TempDir::new().unwrap(); - let mut light_client = ClientBuilder::new(server_id.clone(), wallet_dir).build_client( + let mut light_client = ClientBuilder::new(Some(server_id.clone()), wallet_dir).build_client( DARKSIDE_SEED.to_string(), 1, true, @@ -128,7 +128,7 @@ async fn sent_transaction_reorged_into_mempool() { .unwrap(); let wallet_dir = TempDir::new().unwrap(); - let mut client_manager = ClientBuilder::new(server_id.clone(), wallet_dir); + let mut client_manager = ClientBuilder::new(Some(server_id.clone()), wallet_dir); let activation_heights = ActivationHeights::default(); let mut light_client = client_manager.build_client(DARKSIDE_SEED.to_string(), 0, true, activation_heights); diff --git a/libtonode-tests/src/chain_generics.rs b/libtonode-tests/src/chain_generics.rs index 0d3ed48cbb..75d2a74ace 100644 --- a/libtonode-tests/src/chain_generics.rs +++ b/libtonode-tests/src/chain_generics.rs @@ -60,7 +60,7 @@ impl ConductChain for LibtonodeEnvironment { ); } - fn lightserver_uri(&self) -> Option { + fn indexer_uri(&self) -> Option { Some(port_to_localhost_uri( self.local_net.indexer().listen_port(), )) diff --git a/libtonode-tests/tests/concrete.rs b/libtonode-tests/tests/concrete.rs index 43a13ec2ac..a742a2104b 100644 --- a/libtonode-tests/tests/concrete.rs +++ b/libtonode-tests/tests/concrete.rs @@ -794,7 +794,7 @@ mod fast { // messages let alice_to_bob = TransactionRequest::new(vec![ Payment::new( - ZcashAddress::from_str(&bob.encode(&faucet.config().network_type())).unwrap(), + ZcashAddress::from_str(&bob.encode(&faucet.config().chain_type)).unwrap(), Zatoshis::from_u64(1_000).unwrap(), Some(Memo::encode( &Memo::from_str(&("Alice->Bob #1\nReply to\n".to_string() + &alice)).unwrap(), @@ -808,7 +808,7 @@ mod fast { .unwrap(); let alice_to_bob_2 = TransactionRequest::new(vec![ Payment::new( - ZcashAddress::from_str(&bob.encode(&faucet.config().network_type())).unwrap(), + ZcashAddress::from_str(&bob.encode(&faucet.config().chain_type)).unwrap(), Zatoshis::from_u64(1_000).unwrap(), Some(Memo::encode( &Memo::from_str(&("Alice->Bob #2\nReply to\n".to_string() + &alice)).unwrap(), @@ -822,7 +822,7 @@ mod fast { .unwrap(); let alice_to_charlie = TransactionRequest::new(vec![ Payment::new( - ZcashAddress::from_str(&charlie.encode(&faucet.config().network_type())).unwrap(), + ZcashAddress::from_str(&charlie.encode(&faucet.config().chain_type)).unwrap(), Zatoshis::from_u64(1_000).unwrap(), Some(Memo::encode( &Memo::from_str(&("Alice->Charlie #2\nReply to\n".to_string() + &alice)) @@ -842,7 +842,7 @@ mod fast { Some(Memo::encode( &Memo::from_str( &("Charlie->Alice #2\nReply to\n".to_string() - + &charlie.encode(&faucet.config().network_type())), + + &charlie.encode(&faucet.config().chain_type)), ) .unwrap(), )), @@ -860,7 +860,7 @@ mod fast { Some(Memo::encode( &Memo::from_str( &("Bob->Alice #2\nReply to\n".to_string() - + &bob.encode(&faucet.config().network_type())), + + &bob.encode(&faucet.config().chain_type)), ) .unwrap(), )), @@ -886,11 +886,11 @@ mod fast { // Collect observations let value_transfers_bob = &recipient - .messages_containing(Some(&bob.encode(&recipient.config().network_type()))) + .messages_containing(Some(&bob.encode(&recipient.config().chain_type))) .await .unwrap(); let value_transfers_charlie = &recipient - .messages_containing(Some(&charlie.encode(&recipient.config().network_type()))) + .messages_containing(Some(&charlie.encode(&recipient.config().chain_type))) .await .unwrap(); let all_vts = &recipient.value_transfers(true).await.unwrap(); @@ -1454,7 +1454,7 @@ mod slow { use zingo_common_components::protocol::ActivationHeights; use zingo_status::confirmation_status::ConfirmationStatus; use zingo_test_vectors::TEST_TXID; - use zingolib::config::{ChainType, ZingoConfig}; + use zingolib::config::{ChainType, ZingoConfigBuilder}; use zingolib::lightclient::error::{LightClientError, SendError}; use zingolib::testutils::lightclient::{from_inputs, get_fees_paid_by_client}; use zingolib::testutils::{ @@ -1780,9 +1780,9 @@ mod slow { false, local_net.validator().get_activation_heights().await, ); - let zingo_config = ZingoConfig::builder() - .set_indexer_uri(client_builder.server_id) - .set_network_type(ChainType::Regtest( + let zingo_config = ZingoConfigBuilder::default() + .set_indexer_uri(client_builder.indexer_uri.clone()) + .set_chain(ChainType::Regtest( local_net.validator().get_activation_heights().await, )) .set_wallet_dir(client_builder.zingo_datadir.path().to_path_buf()) @@ -1795,7 +1795,7 @@ mod slow { min_confirmations: NonZeroU32::try_from(1).unwrap(), }) .set_no_of_accounts(NonZeroU32::try_from(1).unwrap()) - .build(); + .create(); let (recipient_taddr, recipient_sapling, recipient_unified) = ( get_base_address_macro!(original_recipient, "transparent"), @@ -4574,7 +4574,7 @@ mod testnet_test { use pepper_sync::sync_status; use zingo_test_vectors::seeds::HOSPITAL_MUSEUM_SEED; use zingolib::{ - config::{ChainType, DEFAULT_TESTNET_LIGHTWALLETD_SERVER, ZingoConfig}, + config::{ChainType, DEFAULT_TESTNET_INDEXER_URI, ZingoConfigBuilder, some_infallible_uri}, lightclient::LightClient, testutils::tempfile::TempDir, wallet::{LightWallet, WalletBase}, @@ -4592,23 +4592,19 @@ mod testnet_test { while test_count < NUM_TESTS { let wallet_dir = TempDir::new().unwrap(); - let config = ZingoConfig::builder() - .set_network_type(ChainType::Testnet) - .set_indexer_uri( - (DEFAULT_TESTNET_LIGHTWALLETD_SERVER) - .parse::() - .unwrap(), - ) + let config = ZingoConfigBuilder::default() + .set_chain(ChainType::Testnet) + .set_indexer_uri(some_infallible_uri(DEFAULT_TESTNET_INDEXER_URI)) .set_wallet_dir(wallet_dir.path().to_path_buf()) - .build(); + .create(); let wallet = LightWallet::new( ChainType::Testnet, WalletBase::Mnemonic { mnemonic: Mnemonic::from_phrase(HOSPITAL_MUSEUM_SEED).unwrap(), - no_of_accounts: config.no_of_accounts(), + no_of_accounts: config.no_of_accounts, }, 2_000_000.into(), - config.wallet_settings(), + config.wallet_settings.clone(), ) .unwrap(); diff --git a/libtonode-tests/tests/sync.rs b/libtonode-tests/tests/sync.rs index 208b106d4a..f6ceb8bb4f 100644 --- a/libtonode-tests/tests/sync.rs +++ b/libtonode-tests/tests/sync.rs @@ -7,12 +7,12 @@ use zcash_local_net::validator::Validator; use zcash_protocol::consensus::BlockHeight; use zingo_common_components::protocol::ActivationHeights; use zingo_test_vectors::seeds::HOSPITAL_MUSEUM_SEED; -use zingolib::config::{ChainType, ZingoConfig}; +use zingolib::config::{ChainType, ZingoConfigBuilder, some_infallible_uri}; use zingolib::testutils::lightclient::from_inputs::quick_send; use zingolib::testutils::paths::get_cargo_manifest_dir; use zingolib::testutils::tempfile::TempDir; use zingolib::{ - config::{DEFAULT_LIGHTWALLETD_SERVER, construct_lightwalletd_uri}, + config::DEFAULT_INDEXER_URI, get_base_address_macro, lightclient::LightClient, testutils::lightclient::from_inputs::{self}, @@ -28,12 +28,12 @@ async fn sync_mainnet_test() { .expect("Ring to work as a default"); tracing_subscriber::fmt().init(); - let uri = construct_lightwalletd_uri(Some(DEFAULT_LIGHTWALLETD_SERVER.to_string())); + let uri = some_infallible_uri(DEFAULT_INDEXER_URI); let temp_dir = TempDir::new().unwrap(); let temp_path = temp_dir.path().to_path_buf(); - let config = ZingoConfig::builder() + let config = ZingoConfigBuilder::default() .set_indexer_uri(uri.clone()) - .set_network_type(ChainType::Mainnet) + .set_chain(ChainType::Mainnet) .set_wallet_dir(temp_path) .set_wallet_name("".to_string()) .set_wallet_settings(WalletSettings { @@ -44,16 +44,16 @@ async fn sync_mainnet_test() { min_confirmations: NonZeroU32::try_from(1).unwrap(), }) .set_no_of_accounts(NonZeroU32::try_from(1).unwrap()) - .build(); + .create(); let mut lightclient = LightClient::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Mnemonic { mnemonic: Mnemonic::from_phrase(HOSPITAL_MUSEUM_SEED.to_string()).unwrap(), no_of_accounts: NonZeroU32::try_from(1).expect("hard-coded integer"), }, 1_500_000.into(), - config.wallet_settings(), + config.wallet_settings.clone(), ) .unwrap(), config, @@ -97,12 +97,12 @@ async fn sync_status() { .expect("Ring to work as a default"); tracing_subscriber::fmt().init(); - let uri = construct_lightwalletd_uri(Some(DEFAULT_LIGHTWALLETD_SERVER.to_string())); + let uri = some_infallible_uri(DEFAULT_INDEXER_URI); let temp_dir = TempDir::new().unwrap(); let temp_path = temp_dir.path().to_path_buf(); - let config = ZingoConfig::builder() + let config = ZingoConfigBuilder::default() .set_indexer_uri(uri.clone()) - .set_network_type(ChainType::Mainnet) + .set_chain(ChainType::Mainnet) .set_wallet_dir(temp_path) .set_wallet_name("".to_string()) .set_wallet_settings(WalletSettings { @@ -113,16 +113,16 @@ async fn sync_status() { min_confirmations: NonZeroU32::try_from(1).unwrap(), }) .set_no_of_accounts(NonZeroU32::try_from(1).unwrap()) - .build(); + .create(); let mut lightclient = LightClient::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Mnemonic { mnemonic: Mnemonic::from_phrase(HOSPITAL_MUSEUM_SEED.to_string()).unwrap(), no_of_accounts: NonZeroU32::try_from(1).expect("hard-coded integer"), }, 2_496_152.into(), - config.wallet_settings(), + config.wallet_settings.clone(), ) .unwrap(), config, diff --git a/zingo-cli/src/lib.rs b/zingo-cli/src/lib.rs index 8dac067308..ca18f19cd6 100644 --- a/zingo-cli/src/lib.rs +++ b/zingo-cli/src/lib.rs @@ -8,6 +8,7 @@ mod commands; use std::num::NonZeroU32; use std::path::PathBuf; +use std::str::FromStr as _; use std::sync::mpsc::{Receiver, Sender, channel}; use bip0039::Mnemonic; @@ -18,7 +19,7 @@ use zcash_protocol::consensus::BlockHeight; use commands::ShortCircuitedCommand; use pepper_sync::config::{PerformanceLevel, SyncConfig, TransparentAddressDiscovery}; -use zingolib::config::{ChainType, ZingoConfig}; +use zingolib::config::{ChainType, ZingoConfigBuilder}; use zingolib::lightclient::LightClient; use zingolib::wallet::{LightWallet, WalletBase, WalletSettings}; @@ -66,7 +67,7 @@ pub fn build_clap_app() -> clap::ArgMatches { .value_name("server") .help("Lightwalletd server to connect to.") .value_parser(parse_uri) - .default_value(zingolib::config::DEFAULT_LIGHTWALLETD_SERVER)) + .default_value(zingolib::config::DEFAULT_INDEXER_URI)) .arg(Arg::new("data-dir") .long("data-dir") .value_name("data-dir") @@ -271,7 +272,7 @@ pub fn command_loop( /// TODO: Add Doc Comment Here! pub struct ConfigTemplate { params: Vec, - server: http::Uri, + server: Option, seed: Option, ufvk: Option, birthday: u64, @@ -333,24 +334,15 @@ If you don't remember the block height, you can pass '--birthday 0' to scan from } else { PathBuf::from("wallets") }; - let server = matches - .get_one::("server") - .map(ToString::to_string); log::info!("data_dir: {}", &data_dir.to_str().unwrap()); - let server = zingolib::config::construct_lightwalletd_uri(server); + // TODO: Handle NONE?! + let server = matches.get_one::("server").cloned(); let chaintype = if let Some(chain) = matches.get_one::("chain") { - ChainType::try_from(chain.as_str()).map_err(|e| e.to_string())? + ChainType::from_str(chain.as_str()).map_err(|e| e.to_string())? } else { ChainType::Mainnet }; - // Test to make sure the server has all of scheme, host and port - if server.scheme_str().is_none() || server.host().is_none() || server.port().is_none() { - return Err(format!( - "Please provide the --server parameter as [scheme]://[host]:[port].\nYou provided: {server}" - )); - } - let sync = !matches.get_flag("nosync"); let waitsync = matches.get_flag("waitsync"); Ok(Self { @@ -381,9 +373,9 @@ pub type CommandResponse = String; pub fn startup( filled_template: &ConfigTemplate, ) -> std::io::Result<(Sender, Receiver)> { - let config = ZingoConfig::builder() + let config = ZingoConfigBuilder::default() .set_indexer_uri(filled_template.server.clone()) - .set_network_type(filled_template.chaintype) + .set_chain(filled_template.chaintype) .set_wallet_dir(filled_template.data_dir.clone()) .set_wallet_settings(WalletSettings { sync_config: SyncConfig { @@ -394,12 +386,12 @@ pub fn startup( }) .set_no_of_accounts(NonZeroU32::try_from(1).expect("hard-coded non-zero integer")) .set_wallet_name("".to_string()) - .build(); + .create(); let mut lightclient = if let Some(seed_phrase) = filled_template.seed.clone() { LightClient::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Mnemonic { mnemonic: Mnemonic::from_phrase(seed_phrase).map_err(|e| { std::io::Error::new( @@ -410,7 +402,7 @@ pub fn startup( no_of_accounts: NonZeroU32::try_from(1).expect("hard-coded integer"), }, (filled_template.birthday as u32).into(), - config.wallet_settings(), + config.wallet_settings.clone(), ) .map_err(|e| std::io::Error::other(format!("Failed to create wallet. {e}")))?, config.clone(), @@ -421,10 +413,10 @@ pub fn startup( // Create client from UFVK LightClient::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Ufvk(ufvk), (filled_template.birthday as u32).into(), - config.wallet_settings(), + config.wallet_settings.clone(), ) .map_err(|e| std::io::Error::other(format!("Failed to create wallet. {e}")))?, config.clone(), @@ -440,16 +432,16 @@ pub fn startup( println!("Creating a new wallet"); // Call the lightwalletd server to get the current block-height // Do a getinfo first, before opening the wallet - let server_uri = config.indexer_uri(); - - let chain_height = RT - .block_on(async move { + let chain_height = if let Some(server_uri) = config.get_indexer_uri() { + RT.block_on(async move { zingolib::grpc_connector::get_latest_block(server_uri) .await .map(|block_id| BlockHeight::from_u32(block_id.height as u32)) }) - .map_err(|e| std::io::Error::other(format!("Failed to create lightclient. {e}")))?; - + .map_err(|e| std::io::Error::other(format!("Failed to create lightclient. {e}")))? + } else { + BlockHeight::from_u32(0) //NOTE: this will become sapling activation + }; LightClient::new(config.clone(), chain_height, false) .map_err(|e| std::io::Error::other(format!("Failed to create lightclient. {e}")))? }; @@ -458,9 +450,13 @@ pub fn startup( // Print startup Messages info!(""); // Blank line info!("Starting Zingo-CLI"); - info!("Light Client config {config:?}"); + info!("Light Client config {{config.clone():?}}"); - info!("Lightclient connecting to {}", config.indexer_uri()); + if let Some(uri) = config.clone().get_indexer_uri() { + info!("Lightclient connecting to {}", uri) + } else { + info!("Lightclient does not have an indexer configured") + }; } if filled_template.sync { diff --git a/zingolib/src/config.rs b/zingolib/src/config.rs index a2950d5deb..66095075e6 100644 --- a/zingolib/src/config.rs +++ b/zingolib/src/config.rs @@ -1,11 +1,13 @@ //! `ZingConfig` //! TODO: Add Crate Description Here! +#![forbid(unsafe_code)] +#![warn(missing_docs)] use std::{ io::{self, Error}, - net::ToSocketAddrs, num::NonZeroU32, path::{Path, PathBuf}, + sync::{Arc, RwLock}, }; use log::{LevelFilter, info}; @@ -21,82 +23,89 @@ use log4rs::{ encode::pattern::PatternEncoder, filter::threshold::ThresholdFilter, }; - -use zcash_protocol::consensus::{ +use zcash_primitives::consensus::{ BlockHeight, MAIN_NETWORK, NetworkType, NetworkUpgrade, Parameters, TEST_NETWORK, }; - use zingo_common_components::protocol::ActivationHeights; use crate::wallet::WalletSettings; -/// TODO: Add Doc Comment Here! +/// Mainnet UA for donations pub const DEVELOPER_DONATION_ADDRESS: &str = "u1w47nzy4z5g9zvm4h2s4ztpl8vrdmlclqz5sz02742zs5j3tz232u4safvv9kplg7g06wpk5fx0k0rx3r9gg4qk6nkg4c0ey57l0dyxtatqf8403xat7vyge7mmen7zwjcgvryg22khtg3327s6mqqkxnpwlnrt27kxhwg37qys2kpn2d2jl2zkk44l7j7hq9az82594u3qaescr3c9v"; -/// TODO: Add Doc Comment Here! -pub const ZENNIES_FOR_ZINGO_DONATION_ADDRESS: &str = "u1p32nu0pgev5cr0u6t4ja9lcn29kaw37xch8nyglwvp7grl07f72c46hxvw0u3q58ks43ntg324fmulc2xqf4xl3pv42s232m25vaukp05s6av9z76s3evsstax4u6f5g7tql5yqwuks9t4ef6vdayfmrsymenqtshgxzj59hdydzygesqa7pdpw463hu7afqf4an29m69kfasdwr494"; -/// TODO: Add Doc Comment Here! -pub const ZENNIES_FOR_ZINGO_TESTNET_ADDRESS: &str = "utest19zd9laj93deq4lkay48xcfyh0tjec786x6yrng38fp6zusgm0c84h3el99fngh8eks4kxv020r2h2njku6pf69anpqmjq5c3suzcjtlyhvpse0aqje09la48xk6a2cnm822s2yhuzfr47pp4dla9rakdk90g0cee070z57d3trqk87wwj4swz6uf6ts6p5z6lep3xyvueuvt7392tww"; + /// Regtest address for donation in test environments pub const ZENNIES_FOR_ZINGO_REGTEST_ADDRESS: &str = "uregtest14emvr2anyul683p43d0ck55c04r65ld6f0shetcn77z8j7m64hm4ku3wguf60s75f0g3s7r7g89z22f3ff5tsfgr45efj4pe2gyg5krqp5vvl3afu0280zp9ru2379zat5y6nkqkwjxsvpq5900kchcgzaw8v8z3ggt5yymnuj9hymtv3p533fcrk2wnj48g5vg42vle08c2xtanq0e"; -/// TODO: Add Doc Comment Here! -pub const ZENNIES_FOR_ZINGO_AMOUNT: u64 = 1_000_000; -/// The lightserver that handles blockchain requests -pub const DEFAULT_LIGHTWALLETD_SERVER: &str = "https://zec.rocks:443"; -/// Used for testnet -pub const DEFAULT_TESTNET_LIGHTWALLETD_SERVER: &str = "https://testnet.zec.rocks"; -/// TODO: Add Doc Comment Here! -pub const DEFAULT_WALLET_NAME: &str = "zingo-wallet.dat"; -/// TODO: Add Doc Comment Here! -pub const DEFAULT_LOGFILE_NAME: &str = "zingo-wallet.debug.log"; /// Gets the appropriate donation address for the given chain type #[must_use] pub fn get_donation_address_for_chain(chain: &ChainType) -> &'static str { match chain { - ChainType::Mainnet => ZENNIES_FOR_ZINGO_DONATION_ADDRESS, ChainType::Testnet => ZENNIES_FOR_ZINGO_TESTNET_ADDRESS, + ChainType::Mainnet => ZENNIES_FOR_ZINGO_DONATION_ADDRESS, ChainType::Regtest(_) => ZENNIES_FOR_ZINGO_REGTEST_ADDRESS, } } /// The networks a zingolib client can run against -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum ChainType { - /// Mainnet - Mainnet, /// Testnet Testnet, + /// Mainnet + Mainnet, /// Regtest Regtest(ActivationHeights), } +impl ChainType { + const TESTNET_STR: &str = "test"; + const MAINNET_STR: &str = "main"; + const REGTEST_STR: &str = "regtest"; + + /// Returns the string label for this chain type. + pub fn as_str(&self) -> &'static str { + match self { + ChainType::Testnet => Self::TESTNET_STR, + ChainType::Mainnet => Self::MAINNET_STR, + ChainType::Regtest(_) => Self::REGTEST_STR, + } + } +} + impl std::fmt::Display for ChainType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let chain = match self { - ChainType::Mainnet => "main", - ChainType::Testnet => "test", - ChainType::Regtest(_) => "regtest", - }; - write!(f, "{chain}") + f.write_str(self.as_str()) + } +} + +impl std::str::FromStr for ChainType { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + ChainType::MAINNET_STR => Ok(ChainType::Mainnet), + ChainType::TESTNET_STR => Ok(ChainType::Testnet), + ChainType::REGTEST_STR => Ok(ChainType::Regtest(ActivationHeights::default())), + other => Err(format!("Unknown chain type: {other}")), + } } } -// TODO: can we rework the library so we dont have implement Parameters on the public API facing ChainType? -// this trait impl exposes external (zcash_protocol) types to the public API impl Parameters for ChainType { fn network_type(&self) -> NetworkType { match self { - ChainType::Mainnet => NetworkType::Main, ChainType::Testnet => NetworkType::Test, + ChainType::Mainnet => NetworkType::Main, ChainType::Regtest(_) => NetworkType::Regtest, } } fn activation_height(&self, nu: NetworkUpgrade) -> Option { + use ChainType::{Mainnet, Regtest, Testnet}; match self { - ChainType::Mainnet => MAIN_NETWORK.activation_height(nu), - ChainType::Testnet => TEST_NETWORK.activation_height(nu), - ChainType::Regtest(activation_heights) => match nu { + Testnet => TEST_NETWORK.activation_height(nu), + Mainnet => MAIN_NETWORK.activation_height(nu), + Regtest(activation_heights) => match nu { NetworkUpgrade::Overwinter => { activation_heights.overwinter().map(BlockHeight::from_u32) } @@ -114,149 +123,296 @@ impl Parameters for ChainType { } } -impl TryFrom<&str> for ChainType { - type Error = InvalidChainType; +/// Testnet donation address. +pub const ZENNIES_FOR_ZINGO_TESTNET_ADDRESS: &str = "utest19zd9laj93deq4lkay48xcfyh0tjec786x6yrng38fp6zusgm0c84h3el99fngh8eks4kxv020r2h2njku6pf69anpqmjq5c3suzcjtlyhvpse0aqje09la48xk6a2cnm822s2yhuzfr47pp4dla9rakdk90g0cee070z57d3trqk87wwj4swz6uf6ts6p5z6lep3xyvueuvt7392tww"; - fn try_from(value: &str) -> Result { - match value { - "mainnet" => Ok(ChainType::Mainnet), - "testnet" => Ok(ChainType::Testnet), - "regtest" => Ok(ChainType::Regtest(ActivationHeights::default())), - _ => Err(InvalidChainType(value.to_string())), - } - } +/// Mainnet donation address. +pub const ZENNIES_FOR_ZINGO_DONATION_ADDRESS: &str = "u1p32nu0pgev5cr0u6t4ja9lcn29kaw37xch8nyglwvp7grl07f72c46hxvw0u3q58ks43ntg324fmulc2xqf4xl3pv42s232m25vaukp05s6av9z76s3evsstax4u6f5g7tql5yqwuks9t4ef6vdayfmrsymenqtshgxzj59hdydzygesqa7pdpw463hu7afqf4an29m69kfasdwr494"; + +/// Default donation amount. +pub const ZENNIES_FOR_ZINGO_AMOUNT: u64 = 1_000_000; + +/// The lightserver that handles blockchain requests +pub const DEFAULT_INDEXER_URI: &str = "https://zec.rocks:443"; + +/// Default indexer uri for testnet. +pub const DEFAULT_TESTNET_INDEXER_URI: &str = "https://testnet.zec.rocks"; + +/// Parses a URI string into `Some(http::Uri)`. +pub fn some_infallible_uri(s: &str) -> Option { + Some(s.parse().unwrap()) } -/// Invalid chain type. -#[derive(thiserror::Error, Debug)] -#[error("Invalid chain type '{0}'. Expected one of: 'mainnet', 'testnet' or 'regtest'.")] -pub struct InvalidChainType(String); +/// The default wallet file name. +pub const DEFAULT_WALLET_NAME: &str = "zingo-wallet.dat"; + +/// The default log file name +pub const DEFAULT_LOGFILE_NAME: &str = "zingo-wallet.debug.log"; + +/// Re-export pepper-sync `SyncConfig` for use with `load_clientconfig` +/// +pub use pepper_sync::config::{SyncConfig, TransparentAddressDiscovery}; /// Creates a zingo config for lightclient construction. -#[deprecated(note = "replaced by ZingoConfig builder pattern")] pub fn load_clientconfig( - lightwallet_uri: http::Uri, + indexer_uri: Option, data_dir: Option, chain: ChainType, wallet_settings: WalletSettings, no_of_accounts: NonZeroU32, wallet_name: String, ) -> std::io::Result { - check_indexer_uri(&lightwallet_uri); - let wallet_name = wallet_name_or_default(Some(wallet_name)); - let wallet_dir = wallet_dir_or_default(data_dir, chain); - - let config = ZingoConfig { - indexer_uri: lightwallet_uri, - network_type: chain, - wallet_dir, - wallet_name, - logfile_name: DEFAULT_LOGFILE_NAME.into(), - wallet_settings, - no_of_accounts, + use std::net::ToSocketAddrs; + + let wallet_name_config = if wallet_name.is_empty() { + DEFAULT_WALLET_NAME + } else { + &wallet_name + }; + + let config = match indexer_uri.clone() { + None => { + info!("Using offline mode"); + ZingoConfig { + indexer_uri: Arc::new(RwLock::new(None)), + chain_type: chain, + wallet_dir: data_dir, + wallet_name: wallet_name_config.into(), + logfile_name: DEFAULT_LOGFILE_NAME.into(), + wallet_settings, + no_of_accounts, + } + } + Some(indexer_uri) => { + if let (Some(host), Some(port)) = (indexer_uri.host(), indexer_uri.port_u16()) { + match format!("{host}:{port}").to_socket_addrs() { + Ok(_) => info!("Configured indexer: {indexer_uri}"), + Err(e) => info!("Couldn't resolve server {host}:{port}: {e}"), + } + } else { + info!("Configured indexer URI is missing host or port: {indexer_uri}"); + } + + ZingoConfig { + indexer_uri: Arc::new(RwLock::new(Some(indexer_uri))), + chain_type: chain, + wallet_dir: data_dir, + wallet_name: wallet_name_config.into(), + logfile_name: DEFAULT_LOGFILE_NAME.into(), + wallet_settings, + no_of_accounts, + } + } }; Ok(config) } -/// Constructs a http::Uri from a `server` string. If `server` is `None` use the `DEFAULT_LIGHTWALLETD_SERVER`. -/// If the provided string is missing the http prefix, a prefix of `http://` will be added. -/// If the provided string is missing a port, a port of `:9067` will be added. -// TODO: handle errors -#[must_use] -pub fn construct_lightwalletd_uri(server: Option) -> http::Uri { - match server { - Some(s) => { - if s.is_empty() { - return http::Uri::default(); - } else { - let mut s = if s.starts_with("http") { - s - } else { - "http://".to_string() + &s - }; - let uri: http::Uri = s.parse().unwrap(); - if uri.port().is_none() { - s += ":9067"; - } - s - } +/// Builder for constructing a [`ZingoConfig`] for a LightClient. +#[derive(Clone, Debug)] +pub struct ZingoConfigBuilder { + /// Optional indexer endpoint. `None` means offline. + pub indexer_uri: Option, + + /// The network type. + pub chain_type: ChainType, + + /// Whether to watch the mempool. + pub monitor_mempool: Option, + + /// The directory where the wallet and logfiles will be created. + /// By default, this will be in ~/.zcash on Linux and %APPDATA%\Zcash on Windows. + /// For mac it is in: ~/Library/Application Support/Zcash + pub wallet_dir: Option, + /// The filename of the wallet. This will be created in the `wallet_dir`. + pub wallet_name: Option, + /// The filename of the logfile. This will be created in the `wallet_dir`. + pub logfile_name: Option, + /// Wallet settings. + pub wallet_settings: WalletSettings, + /// Number of accounts + pub no_of_accounts: NonZeroU32, +} + +impl ZingoConfigBuilder { + /// Set the URI of the proxy server we download blockchain information from. + /// # Examples + /// ``` + /// use zingolib::config::ZingoConfigBuilder; + /// use http::Uri; + /// assert_eq!(ZingoConfigBuilder::default().set_lightwalletd_uri(("https://zcash.mysideoftheweb.com:19067").parse::().unwrap()).lightwalletd_uri.clone().unwrap(), "https://zcash.mysideoftheweb.com:19067"); + /// ``` + pub fn set_indexer_uri(&mut self, indexer_uri: Option) -> &mut Self { + self.indexer_uri = indexer_uri; + self + } + + /// Set the chain the consuming client will interact with. + /// See + /// for chain types. + /// Note "chain type" is not a formal standard. + /// # Examples + /// ``` + /// use zingolib::config::ZingoConfigBuilder; + /// use zingolib::config::ChainType::Testnet; + /// assert_eq!(ZingoConfigBuilder::default().set_chain(Testnet).create().chain, Testnet); + /// ``` + pub fn set_chain(&mut self, chain: ChainType) -> &mut Self { + self.chain_type = chain; + self + } + + /// TODO: Document this pub fn + pub fn set_wallet_name(&mut self, wallet_name: String) -> &mut Self { + self.wallet_name = Some(PathBuf::from(wallet_name)); + self + } + + /// Set the wallet directory where client transaction data will be stored in a wallet. + /// # Examples + /// ``` + /// use zingolib::config::ZingoConfigBuilder; + /// use tempfile::TempDir; + /// let dir = tempfile::TempDir::with_prefix("zingo_doc_test").unwrap().into_path(); + /// let config = ZingoConfigBuilder::default().set_wallet_dir(dir.clone()).create(); + /// assert_eq!(config.wallet_dir.clone().unwrap(), dir); + /// ``` + pub fn set_wallet_dir(&mut self, dir: PathBuf) -> &mut Self { + self.wallet_dir = Some(dir); + self + } + + /// Overrides wallet settings. + pub fn set_wallet_settings(&mut self, wallet_settings: WalletSettings) -> &mut Self { + self.wallet_settings = wallet_settings; + self + } + + /// Sets how many accounts the wallet should use. + pub fn set_no_of_accounts(&mut self, no_of_accounts: NonZeroU32) -> &mut Self { + self.no_of_accounts = no_of_accounts; + self + } + + /// Builds a [`ZingoConfig`]. If no indexer is set, it's interpreted as offline. + pub fn create(&self) -> ZingoConfig { + let lightwalletd_uri = self.indexer_uri.clone().unwrap_or_default(); + ZingoConfig { + indexer_uri: Arc::new(RwLock::new(Some(lightwalletd_uri))), + chain_type: self.chain_type, + wallet_dir: self.wallet_dir.clone(), + wallet_name: DEFAULT_WALLET_NAME.into(), + logfile_name: DEFAULT_LOGFILE_NAME.into(), + wallet_settings: self.wallet_settings.clone(), + no_of_accounts: self.no_of_accounts, } - None => DEFAULT_LIGHTWALLETD_SERVER.to_string(), } - .parse() - .unwrap() } -/// Configuration data for the creation of a `LightClient`. +impl Default for ZingoConfigBuilder { + fn default() -> Self { + ZingoConfigBuilder { + indexer_uri: None, + monitor_mempool: None, + wallet_dir: None, + wallet_name: None, + logfile_name: None, + chain_type: ChainType::Mainnet, + wallet_settings: WalletSettings { + sync_config: pepper_sync::config::SyncConfig { + transparent_address_discovery: + pepper_sync::config::TransparentAddressDiscovery::minimal(), + performance_level: pepper_sync::config::PerformanceLevel::High, + }, + min_confirmations: NonZeroU32::try_from(3).unwrap(), + }, + no_of_accounts: NonZeroU32::try_from(1).expect("hard coded non-zero integer"), + } + } +} + +/// Configuration object for the creation of a `LightClient`. // TODO: this config should only be used to create a lightclient, the data should then be moved into fields of // lightclient or lightwallet if it needs to retained in memory. #[derive(Clone, Debug)] pub struct ZingoConfig { - /// The URI of the indexer the lightclient is connected to. - indexer_uri: http::Uri, - /// The network type of the blockchain the lightclient is connected to. - // TODO: change for zingo common public API safe type - network_type: ChainType, + /// The indexer URI. + pub indexer_uri: Arc>>, + + /// The chain type. + pub chain_type: ChainType, /// The directory where the wallet and logfiles will be created. By default, this will be in ~/.zcash on Linux and %APPDATA%\Zcash on Windows. - wallet_dir: PathBuf, + pub wallet_dir: Option, /// The filename of the wallet. This will be created in the `wallet_dir`. - wallet_name: String, + pub wallet_name: PathBuf, /// The filename of the logfile. This will be created in the `wallet_dir`. - logfile_name: String, + pub logfile_name: PathBuf, /// Wallet settings. - wallet_settings: WalletSettings, + pub wallet_settings: WalletSettings, /// Number of accounts - no_of_accounts: NonZeroU32, + pub no_of_accounts: NonZeroU32, } impl ZingoConfig { - /// Constructs a default builder. - #[must_use] - pub fn builder() -> ZingoConfigBuilder { - ZingoConfigBuilder::default() - } - - /// Returns indexer URI. + /// Creates a new `ZingoConfigBuilder` for a given chain type. #[must_use] - pub fn indexer_uri(&self) -> http::Uri { - self.indexer_uri.clone() + pub fn build(chain: ChainType) -> ZingoConfigBuilder { + ZingoConfigBuilder { + chain_type: chain, + ..ZingoConfigBuilder::default() + } } - /// Returns wallet directory. + #[cfg(any(test, feature = "testutils"))] + /// create a `ZingoConfig` that helps a `LightClient` connect to a server. #[must_use] - pub fn network_type(&self) -> ChainType { - self.network_type + pub fn create_testnet() -> ZingoConfig { + ZingoConfig::build(ChainType::Testnet) + .set_indexer_uri(some_infallible_uri(DEFAULT_TESTNET_INDEXER_URI)) + .create() } - /// Returns wallet directory. + #[cfg(any(test, feature = "testutils"))] + /// create a `ZingoConfig` that helps a `LightClient` connect to a server. #[must_use] - pub fn wallet_dir(&self) -> PathBuf { - self.wallet_dir.clone() + pub fn create_mainnet() -> ZingoConfig { + ZingoConfig::build(ChainType::Mainnet) + .set_indexer_uri(some_infallible_uri(DEFAULT_INDEXER_URI)) + .create() } - /// Returns wallet file name. + #[cfg(feature = "testutils")] + /// create a `ZingoConfig` that signals a `LightClient` not to connect to a server. #[must_use] - pub fn wallet_name(&self) -> &str { - &self.wallet_name + pub fn create_unconnected(chain: ChainType, dir: Option) -> ZingoConfig { + if let Some(dir) = dir { + ZingoConfig::build(chain).set_wallet_dir(dir).create() + } else { + ZingoConfig::build(chain).create() + } } - /// Returns log file name. + /// Convenience wrapper #[must_use] - pub fn logfile_name(&self) -> &str { - &self.logfile_name + pub fn sapling_activation_height(&self) -> u64 { + self.chain_type + .activation_height(NetworkUpgrade::Sapling) + .unwrap() + .into() } - /// Returns wallet settings.. + /// Returns the activation height for the Nu5 upgrade #[must_use] - pub fn wallet_settings(&self) -> WalletSettings { - self.wallet_settings.clone() + pub fn orchard_activation_height(&self) -> u64 { + self.chain_type + .activation_height(NetworkUpgrade::Nu5) + .unwrap() + .into() } - /// Returns number of accounts.. - #[must_use] - pub fn no_of_accounts(&self) -> NonZeroU32 { - self.no_of_accounts + /// Sets the directory where the wallet file will be stored. + pub fn set_data_dir(&mut self, dir_str: String) { + self.wallet_dir = Some(PathBuf::from(dir_str)); } /// Build the Logging config @@ -291,11 +447,63 @@ impl ZingoConfig { .map_err(|e| Error::other(format!("{e}"))) } - /// Returns the directory that the Zcash proving parameters are located in. + /// Returns the directory where the wallet file is stored. + #[must_use] + pub fn get_zingo_wallet_dir(&self) -> Box { + #[cfg(any(target_os = "ios", target_os = "android"))] + { + PathBuf::from(&self.wallet_dir.as_ref().unwrap()).into_boxed_path() + } + + #[cfg(not(any(target_os = "ios", target_os = "android")))] + { + let mut zcash_data_location; + // If there's some --data-dir path provided, use it + if self.wallet_dir.is_some() { + zcash_data_location = PathBuf::from(&self.wallet_dir.as_ref().unwrap()); + } else { + #[cfg(any(target_os = "macos", target_os = "windows"))] + { + zcash_data_location = + dirs::data_dir().expect("Couldn't determine app data directory!"); + zcash_data_location.push("Zcash"); + } + + #[cfg(not(any(target_os = "macos", target_os = "windows")))] + { + if dirs::home_dir().is_none() { + log::info!("Couldn't determine home dir!"); + } + zcash_data_location = + dirs::home_dir().expect("Couldn't determine home directory!"); + zcash_data_location.push(".zcash"); + } + + match &self.chain_type { + ChainType::Testnet => zcash_data_location.push("testnet3"), + ChainType::Mainnet => {} + ChainType::Regtest(_) => zcash_data_location.push("regtest"), + } + } + + // Create directory if it doesn't exist on non-mobile platforms + match std::fs::create_dir_all(zcash_data_location.clone()) { + Ok(()) => {} + Err(e) => { + tracing::error!("Couldn't create zcash directory!\n{e}"); + panic!("Couldn't create zcash directory!"); + } + } + + zcash_data_location.into_boxed_path() + } + } + + /// Returns the directory where the zcash params are stored. pub fn get_zcash_params_path(&self) -> io::Result> { #[cfg(any(target_os = "ios", target_os = "android"))] { - Ok(self.wallet_dir().into_boxed_path()) + Ok(PathBuf::from(&self.wallet_dir.as_ref().unwrap()).into_boxed_path()) } //TODO: This fn is not correct for regtest mode @@ -304,7 +512,7 @@ impl ZingoConfig { if dirs::home_dir().is_none() { return Err(io::Error::new( io::ErrorKind::InvalidData, - "Couldn't determine home directory!", + "Couldn't determine Home Dir", )); } @@ -314,35 +522,81 @@ impl ZingoConfig { } } - /// Returns full path to wallet file. + /// Returns the indexer's URI. #[must_use] - pub fn get_wallet_path(&self) -> Box { - let mut wallet_path = self.wallet_dir(); - wallet_path.push(&self.wallet_name); + pub fn get_indexer_uri(&self) -> Option { + if self.indexer_uri.read().unwrap().is_some() { + return Some( + self.indexer_uri + .as_ref() + .read() + .unwrap() + .clone() + .expect("Couldn't read configured server URI!") + .clone(), + ); + } + None + } + + /// Returns the directory where the specified wallet file is stored. + #[must_use] + pub fn get_wallet_with_name_pathbuf(&self, wallet_name: String) -> PathBuf { + let mut wallet_location = self.get_zingo_wallet_dir().into_path_buf(); + // if id is empty, the name is the default name + if wallet_name.is_empty() { + wallet_location.push(&self.wallet_name); + } else { + wallet_location.push(wallet_name); + } + wallet_location + } - wallet_path.into_boxed_path() + /// Returns the directory where the specified wallet file is stored. + /// This variant returns a [`Box`]. + #[must_use] + pub fn get_wallet_with_name_path(&self, wallet_name: String) -> Box { + self.get_wallet_with_name_pathbuf(wallet_name) + .into_boxed_path() } - /// Returns full path to the log file. + /// Returns whether the specified wallet file exists. #[must_use] - pub fn get_log_path(&self) -> Box { - let mut log_path = self.wallet_dir(); - log_path.push(&self.logfile_name); + pub fn wallet_with_name_path_exists(&self, wallet_name: String) -> bool { + self.get_wallet_with_name_path(wallet_name).exists() + } - log_path.into_boxed_path() + /// Returns the directory where the wallet file is stored, as a [`PathBuf`]. + #[must_use] + pub fn get_wallet_pathbuf(&self) -> PathBuf { + let mut wallet_location = self.get_zingo_wallet_dir().into_path_buf(); + wallet_location.push(&self.wallet_name); + wallet_location + } + + /// Returns the directory where the wallet file is stored, as a [`Box`]. + #[must_use] + pub fn get_wallet_path(&self) -> Box { + self.get_wallet_pathbuf().into_boxed_path() + } + + /// Returns whether the wallet file exists. + #[must_use] + pub fn wallet_path_exists(&self) -> bool { + self.get_wallet_path().exists() } - /// Creates a backup file of the current wallet file in the wallet directory. - // TODO: move to lightclient or lightwallet + // TODO: DO NOT RETURN STRINGS!! + /// Backups the wallet file to a new file in the same directory. pub fn backup_existing_wallet(&self) -> Result { - if !self.get_wallet_path().exists() { + if !self.wallet_path_exists() { return Err(format!( - "Couldn't find existing wallet to backup. Looked in {}", - self.get_wallet_path().display() + "Couldn't find existing wallet to backup. Looked in {:?}", + self.get_wallet_path().to_str() )); } - let mut backup_file_path = self.wallet_dir(); + let mut backup_file_path = self.get_zingo_wallet_dir().into_path_buf(); backup_file_path.push(format!( "zingo-wallet.backup.{}.dat", std::time::SystemTime::now() @@ -357,304 +611,59 @@ impl ZingoConfig { Ok(backup_file_str) } - /// TEMPORARY - // TODO: this will be removed in following PR which deconstructs config fields into lightclient and lightwallet - // this method will only be a method on lightclient. - pub(crate) fn set_indexer_uri(&mut self, indexer_uri: http::Uri) { - self.indexer_uri = indexer_uri; - } -} - -#[cfg(any(test, feature = "testutils"))] -impl ZingoConfig { - /// create a `ZingoConfig` that helps a `LightClient` connect to a server. - #[must_use] - pub fn create_testnet() -> ZingoConfig { - ZingoConfig::builder() - .set_network_type(ChainType::Testnet) - .set_indexer_uri( - (DEFAULT_TESTNET_LIGHTWALLETD_SERVER) - .parse::() - .unwrap(), - ) - .build() - } - - /// create a `ZingoConfig` that helps a `LightClient` connect to a server. + /// TODO: Add Doc Comment Here! #[must_use] - pub fn create_mainnet() -> ZingoConfig { - ZingoConfig::builder() - .set_network_type(ChainType::Mainnet) - .set_indexer_uri((DEFAULT_LIGHTWALLETD_SERVER).parse::().unwrap()) - .build() - } - - /// create a `ZingoConfig` that signals a `LightClient` not to connect to a server. - #[must_use] - pub fn create_unconnected(chain: ChainType, dir: Option) -> ZingoConfig { - if let Some(dir) = dir { - ZingoConfig::builder() - .set_network_type(chain) - .set_wallet_dir(dir) - .build() - } else { - ZingoConfig::builder().set_network_type(chain).build() - } - } -} - -/// Builder for [`ZingoConfig`]. -#[derive(Clone, Debug)] -pub struct ZingoConfigBuilder { - indexer_uri: Option, - network_type: ChainType, - wallet_dir: Option, - wallet_name: Option, - logfile_name: Option, - wallet_settings: WalletSettings, - no_of_accounts: NonZeroU32, -} - -impl ZingoConfigBuilder { - /// Constructs a new builder for [`ZingoConfig`]. - pub fn new() -> Self { - Self { - indexer_uri: None, - wallet_dir: None, - wallet_name: None, - logfile_name: None, - network_type: ChainType::Mainnet, - wallet_settings: WalletSettings { - sync_config: pepper_sync::config::SyncConfig { - transparent_address_discovery: - pepper_sync::config::TransparentAddressDiscovery::minimal(), - performance_level: pepper_sync::config::PerformanceLevel::High, - }, - min_confirmations: NonZeroU32::try_from(3).unwrap(), - }, - no_of_accounts: NonZeroU32::try_from(1).expect("hard coded non-zero integer"), - } - } - - /// Set indexer URI. - /// # Examples - /// ``` - /// use zingolib::config::ZingoConfig; - /// use http::Uri; - /// let config = ZingoConfig::builder().set_indexer_uri(("https://zcash.mysideoftheweb.com:19067").parse::().unwrap()).build(); - /// assert_eq!(config.indexer_uri(), "https://zcash.mysideoftheweb.com:19067"); - /// ``` - pub fn set_indexer_uri(mut self, indexer_uri: http::Uri) -> Self { - self.indexer_uri = Some(indexer_uri); - self - } - - /// Set network type. - /// # Examples - /// ``` - /// use zingolib::config::ZingoConfig; - /// use zingolib::config::ChainType; - /// let config = ZingoConfig::builder().set_network_type(ChainType::Testnet).build(); - /// assert_eq!(config.network_type(), ChainType::Testnet); - /// ``` - pub fn set_network_type(mut self, network_type: ChainType) -> Self { - self.network_type = network_type; - self - } - - /// Set wallet directory. - /// # Examples - /// ``` - /// use zingolib::config::ZingoConfig; - /// use tempfile::TempDir; - /// let dir = tempfile::TempDir::with_prefix("zingo_doc_test").unwrap().path().to_path_buf(); - /// let config = ZingoConfig::builder().set_wallet_dir(dir.clone()).build(); - /// assert_eq!(config.wallet_dir(), dir); - /// ``` - pub fn set_wallet_dir(mut self, dir: PathBuf) -> Self { - self.wallet_dir = Some(dir); - self - } - - /// Set wallet file name. - pub fn set_wallet_name(mut self, wallet_name: String) -> Self { - self.wallet_name = Some(wallet_name); - self - } - - /// Set log file name. - pub fn set_logfile_name(mut self, logfile_name: String) -> Self { - self.logfile_name = Some(logfile_name); - self - } - - /// Set wallet settings. - pub fn set_wallet_settings(mut self, wallet_settings: WalletSettings) -> Self { - self.wallet_settings = wallet_settings; - self - } - - /// Set number of accounts. - pub fn set_no_of_accounts(mut self, no_of_accounts: NonZeroU32) -> Self { - self.no_of_accounts = no_of_accounts; - self - } - - /// Build a [`ZingoConfig`] from the builder. - pub fn build(self) -> ZingoConfig { - let wallet_dir = wallet_dir_or_default(self.wallet_dir, self.network_type); - let wallet_name = wallet_name_or_default(self.wallet_name); - let logfile_name = logfile_name_or_default(self.logfile_name); - - ZingoConfig { - indexer_uri: self.indexer_uri.clone().unwrap_or_default(), - network_type: self.network_type, - wallet_dir, - wallet_name, - logfile_name, - wallet_settings: self.wallet_settings, - no_of_accounts: self.no_of_accounts, - } - } -} - -impl Default for ZingoConfigBuilder { - fn default() -> Self { - Self::new() - } -} - -// TODO: return errors -fn check_indexer_uri(indexer_uri: &http::Uri) { - if let Some(host) = indexer_uri.host() - && let Some(port) = indexer_uri.port() - { - match format!("{}:{}", host, port,).to_socket_addrs() { - Ok(_) => { - info!("Connected to {indexer_uri}"); - } - Err(e) => { - info!("Couldn't resolve server: {e}"); - } - } - } else { - info!("Using offline mode"); - } -} - -fn wallet_name_or_default(opt_wallet_name: Option) -> String { - let wallet_name = opt_wallet_name.unwrap_or_else(|| DEFAULT_WALLET_NAME.into()); - if wallet_name.is_empty() { - DEFAULT_WALLET_NAME.into() - } else { - wallet_name - } -} - -fn logfile_name_or_default(opt_logfile_name: Option) -> String { - let logfile_name = opt_logfile_name.unwrap_or_else(|| DEFAULT_LOGFILE_NAME.into()); - if logfile_name.is_empty() { - DEFAULT_LOGFILE_NAME.into() - } else { - logfile_name - } -} - -fn wallet_dir_or_default(opt_wallet_dir: Option, chain: ChainType) -> PathBuf { - let wallet_dir: PathBuf; - #[cfg(any(target_os = "ios", target_os = "android"))] - { - // TODO: handle errors - wallet_dir = opt_wallet_dir.unwrap(); - } - - #[cfg(not(any(target_os = "ios", target_os = "android")))] - { - wallet_dir = opt_wallet_dir.clone().unwrap_or_else(|| { - let mut dir = dirs::data_dir().expect("Couldn't determine user's data directory!"); - - #[cfg(any(target_os = "macos", target_os = "windows"))] - { - dir.push("Zcash"); - } - - #[cfg(not(any(target_os = "macos", target_os = "windows")))] - { - dir.push(".zcash"); - } - - match chain { - ChainType::Mainnet => {} - ChainType::Testnet => dir.push("testnet3"), - ChainType::Regtest(_) => dir.push("regtest"), - } - - dir - }); + pub fn get_log_path(&self) -> Box { + let mut log_path = self.get_zingo_wallet_dir().into_path_buf(); + log_path.push(&self.logfile_name); + //tracing::info!("LogFile:\n{}", log_path.to_str().unwrap()); - // Create directory if it doesn't exist on non-mobile platforms - match std::fs::create_dir_all(wallet_dir.clone()) { - Ok(()) => {} - Err(e) => { - panic!("Couldn't create zcash directory!\n {e}"); - } - } + log_path.into_boxed_path() } - - wallet_dir } #[cfg(test)] mod tests { use std::num::NonZeroU32; - use pepper_sync::config::{PerformanceLevel, SyncConfig, TransparentAddressDiscovery}; - use crate::{ - config::{ChainType, ZingoConfig}, + config::{DEFAULT_INDEXER_URI, some_infallible_uri}, wallet::WalletSettings, }; #[tokio::test] - async fn test_load_clientconfig() { + async fn test_load_clientconfig_serverless() { rustls::crypto::ring::default_provider() .install_default() .expect("Ring to work as a default"); tracing_subscriber::fmt().init(); - let valid_uri = crate::config::construct_lightwalletd_uri(Some( - crate::config::DEFAULT_LIGHTWALLETD_SERVER.to_string(), - )); + let valid_uri = some_infallible_uri(DEFAULT_INDEXER_URI); // let invalid_uri = construct_lightwalletd_uri(Some("Invalid URI".to_string())); let temp_dir = tempfile::TempDir::new().unwrap(); let temp_path = temp_dir.path().to_path_buf(); // let temp_path_invalid = temp_dir.path().to_path_buf(); - let valid_config = ZingoConfig::builder() - .set_indexer_uri(valid_uri.clone()) - .set_network_type(ChainType::Mainnet) - .set_wallet_dir(temp_path) - .set_wallet_settings(WalletSettings { - sync_config: SyncConfig { - transparent_address_discovery: TransparentAddressDiscovery::minimal(), - performance_level: PerformanceLevel::High, + let valid_config = crate::config::load_clientconfig( + valid_uri.clone(), + Some(temp_path), + crate::config::ChainType::Mainnet, + WalletSettings { + sync_config: pepper_sync::config::SyncConfig { + transparent_address_discovery: + pepper_sync::config::TransparentAddressDiscovery::minimal(), + performance_level: pepper_sync::config::PerformanceLevel::High, }, min_confirmations: NonZeroU32::try_from(1).unwrap(), - }) - .set_no_of_accounts(NonZeroU32::try_from(1).expect("hard-coded non-zero integer")) - .set_wallet_name("".to_string()) - .build(); - - assert_eq!(valid_config.indexer_uri(), valid_uri); - assert_eq!(valid_config.network_type, ChainType::Mainnet); - - // let invalid_config = load_clientconfig_serverless( - // invalid_uri.clone(), - // Some(temp_path_invalid), - // ChainType::Mainnet, - // true, - // ); - // assert_eq!(invalid_config.is_err(), true); + }, + 1.try_into().unwrap(), + "".to_string(), + ) + .unwrap(); + + assert!(valid_config.get_indexer_uri().is_some()); + assert_eq!(valid_config.get_indexer_uri(), valid_uri); + assert_eq!(valid_config.chain_type, crate::config::ChainType::Mainnet); } } diff --git a/zingolib/src/lightclient.rs b/zingolib/src/lightclient.rs index 4b0219a6a8..3852d34243 100644 --- a/zingolib/src/lightclient.rs +++ b/zingolib/src/lightclient.rs @@ -71,19 +71,19 @@ impl LightClient { overwrite: bool, ) -> Result { let sapling_activation_height = config - .network_type() + .chain_type .activation_height(zcash_protocol::consensus::NetworkUpgrade::Sapling) .expect("should have some sapling activation height"); let birthday = sapling_activation_height.max(chain_height - 100); Self::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::FreshEntropy { - no_of_accounts: config.no_of_accounts(), + no_of_accounts: config.no_of_accounts, }, birthday, - config.wallet_settings(), + config.wallet_settings.clone(), )?, config, overwrite, @@ -139,8 +139,7 @@ impl LightClient { let buffer = BufReader::new(File::open(wallet_path).map_err(LightClientError::FileError)?); Self::create_from_wallet( - LightWallet::read(buffer, config.network_type()) - .map_err(LightClientError::FileError)?, + LightWallet::read(buffer, config.chain_type).map_err(LightClientError::FileError)?, config, true, ) @@ -157,13 +156,13 @@ impl LightClient { } /// Returns URI of the indexer the lightclient is connected to. - pub fn indexer_uri(&self) -> http::Uri { - self.config.indexer_uri() + pub fn indexer_uri(&self) -> Option { + self.config.get_indexer_uri() } /// Set indexer uri. pub fn set_indexer_uri(&mut self, server: http::Uri) { - self.config.set_indexer_uri(server); + *self.config.indexer_uri.write().unwrap() = Some(server); } /// Creates a tor client for current price updates. @@ -173,7 +172,7 @@ impl LightClient { &mut self, tor_dir: Option, ) -> Result<(), LightClientError> { - let tor_dir = tor_dir.unwrap_or_else(|| self.config.wallet_dir().join("tor")); + let tor_dir = tor_dir.unwrap_or_else(|| self.config.get_zingo_wallet_dir().join("tor")); tokio::fs::create_dir_all(tor_dir.as_path()) .await .map_err(LightClientError::FileError)?; @@ -190,22 +189,26 @@ impl LightClient { /// Returns server information. // TODO: return concrete struct with from json impl pub async fn do_info(&self) -> String { - match crate::grpc_connector::get_info(self.indexer_uri()).await { - Ok(i) => { - let o = json::object! { - "version" => i.version, - "git_commit" => i.git_commit, - "server_uri" => self.indexer_uri().to_string(), - "vendor" => i.vendor, - "taddr_support" => i.taddr_support, - "chain_name" => i.chain_name, - "sapling_activation_height" => i.sapling_activation_height, - "consensus_branch_id" => i.consensus_branch_id, - "latest_block_height" => i.block_height - }; - o.pretty(2) + if let Some(uri) = self.indexer_uri() { + match crate::grpc_connector::get_info(uri.clone()).await { + Ok(i) => { + let o = json::object! { + "version" => i.version, + "git_commit" => i.git_commit, + "server_uri" => uri.to_string(), + "vendor" => i.vendor, + "taddr_support" => i.taddr_support, + "chain_name" => i.chain_name, + "sapling_activation_height" => i.sapling_activation_height, + "consensus_branch_id" => i.consensus_branch_id, + "latest_block_height" => i.block_height + }; + o.pretty(2) + } + Err(e) => e, } - Err(e) => e, + } else { + "NO SERVER FOR YOU FOOL!".to_string() } } @@ -320,7 +323,7 @@ impl std::fmt::Debug for LightClient { #[cfg(test)] mod tests { use crate::{ - config::{ChainType, ZingoConfig}, + config::{ChainType, ZingoConfigBuilder}, lightclient::error::LightClientError, wallet::LightWallet, }; @@ -334,19 +337,19 @@ mod tests { #[tokio::test] async fn new_wallet_from_phrase() { let temp_dir = TempDir::new().unwrap(); - let config = ZingoConfig::builder() - .set_network_type(ChainType::Regtest(ActivationHeights::default())) + let config = ZingoConfigBuilder::default() + .set_chain(ChainType::Regtest(ActivationHeights::default())) .set_wallet_dir(temp_dir.path().to_path_buf()) - .build(); + .create(); let mut lc = LightClient::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Mnemonic { mnemonic: Mnemonic::from_phrase(CHIMNEY_BETTER_SEED.to_string()).unwrap(), - no_of_accounts: config.no_of_accounts(), + no_of_accounts: config.no_of_accounts, }, 1.into(), - config.wallet_settings(), + config.wallet_settings.clone(), ) .unwrap(), config.clone(), @@ -359,13 +362,13 @@ mod tests { let lc_file_exists_error = LightClient::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Mnemonic { mnemonic: Mnemonic::from_phrase(CHIMNEY_BETTER_SEED.to_string()).unwrap(), - no_of_accounts: config.no_of_accounts(), + no_of_accounts: config.no_of_accounts, }, 1.into(), - config.wallet_settings(), + config.wallet_settings.clone(), ) .unwrap(), config, diff --git a/zingolib/src/lightclient/error.rs b/zingolib/src/lightclient/error.rs index 64625ea300..b5c627b2f2 100644 --- a/zingolib/src/lightclient/error.rs +++ b/zingolib/src/lightclient/error.rs @@ -65,6 +65,9 @@ pub enum TransmissionError { /// Transmission failed. #[error("Transmission failed. {0}")] TransmissionFailed(String), + /// Transmission failed. + #[error("No indexer configured {0}")] + OfflineMode(String), /// Transaction to transmit does not have `Calculated` status: {0} #[error("Transaction to transmit does not have `Calculated` status: {0}")] IncorrectTransactionStatus(TxId), diff --git a/zingolib/src/lightclient/propose.rs b/zingolib/src/lightclient/propose.rs index 81b7a59954..566f52f281 100644 --- a/zingolib/src/lightclient/propose.rs +++ b/zingolib/src/lightclient/propose.rs @@ -17,7 +17,7 @@ use crate::wallet::error::ProposeShieldError; impl LightClient { fn append_zingo_zenny_receiver(&self, receivers: &mut Vec) { - let zfz_address = get_donation_address_for_chain(&self.config().network_type()); + let zfz_address = get_donation_address_for_chain(&self.config().chain_type); let dev_donation_receiver = Receiver::new( crate::utils::conversion::address_from_str(zfz_address).expect("Hard coded str"), Zatoshis::from_u64(ZENNIES_FOR_ZINGO_AMOUNT).expect("Hard coded u64."), @@ -182,10 +182,10 @@ mod shielding { }; fn create_basic_client() -> LightClient { - let config = ZingoConfigBuilder::default().build(); + let config = ZingoConfigBuilder::default().create(); LightClient::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Mnemonic { mnemonic: Mnemonic::from_phrase(seeds::HOSPITAL_MUSEUM_SEED.to_string()) .unwrap(), diff --git a/zingolib/src/lightclient/send.rs b/zingolib/src/lightclient/send.rs index 3c6148b4cf..b8710906ea 100644 --- a/zingolib/src/lightclient/send.rs +++ b/zingolib/src/lightclient/send.rs @@ -24,15 +24,22 @@ impl LightClient { proposal: Proposal, sending_account: zip32::AccountId, ) -> Result, LightClientError> { - let calculated_txids = self - .wallet - .write() - .await - .calculate_transactions(proposal, sending_account) - .await - .map_err(SendError::CalculateSendError)?; + if let Some(indexer_uri) = self.indexer_uri() { + let calculated_txids = self + .wallet + .write() + .await + .calculate_transactions(proposal, sending_account) + .await + .map_err(SendError::CalculateSendError)?; - self.transmit_transactions(calculated_txids).await + self.transmit_transactions(indexer_uri, calculated_txids) + .await + } else { + Err(LightClientError::SendError(SendError::TransmissionError( + TransmissionError::OfflineMode("FOOL YOU ARE OFFLINE!".to_string()), + ))) + } } async fn shield( @@ -40,15 +47,22 @@ impl LightClient { proposal: Proposal, shielding_account: zip32::AccountId, ) -> Result, LightClientError> { - let calculated_txids = self - .wallet - .write() - .await - .calculate_transactions(proposal, shielding_account) - .await - .map_err(SendError::CalculateShieldError)?; + if let Some(indexer_uri) = self.indexer_uri() { + let calculated_txids = self + .wallet + .write() + .await + .calculate_transactions(proposal, shielding_account) + .await + .map_err(SendError::CalculateShieldError)?; - self.transmit_transactions(calculated_txids).await + self.transmit_transactions(indexer_uri, calculated_txids) + .await + } else { + Err(LightClientError::SendError(SendError::TransmissionError( + TransmissionError::OfflineMode("FOOL YOU ARE OFFLINE!".to_string()), + ))) + } } /// Creates and transmits transactions from a stored proposal. @@ -124,6 +138,7 @@ impl LightClient { /// Returns list of txids for successfully transmitted transactions. async fn transmit_transactions( &mut self, + indexer_uri: http::Uri, calculated_txids: NonEmpty, ) -> Result, LightClientError> { let mut wallet = self.wallet.write().await; @@ -160,7 +175,7 @@ impl LightClient { let mut retry_count = 0; let txid_from_server = loop { let transmission_result = crate::grpc_connector::send_transaction( - self.indexer_uri(), + indexer_uri.clone(), transaction_bytes.clone().into_boxed_slice(), ) .await @@ -228,7 +243,7 @@ mod test { use zingo_test_vectors::seeds::ABANDON_ART_SEED; use crate::{ - config::ZingoConfig, + config::ZingoConfigBuilder, lightclient::{LightClient, sync::test::sync_example_wallet}, mocks::proposal::ProposalBuilder, testutils::chain_generics::{ @@ -239,10 +254,10 @@ mod test { #[tokio::test] async fn complete_and_broadcast_unconnected_error() { - let config = ZingoConfig::builder().build(); + let config = ZingoConfigBuilder::default().create(); let mut lc = LightClient::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Mnemonic { mnemonic: Mnemonic::from_phrase(ABANDON_ART_SEED.to_string()).unwrap(), no_of_accounts: 1.try_into().unwrap(), diff --git a/zingolib/src/lightclient/sync.rs b/zingolib/src/lightclient/sync.rs index 90965403ee..008489db91 100644 --- a/zingolib/src/lightclient/sync.rs +++ b/zingolib/src/lightclient/sync.rs @@ -27,7 +27,8 @@ impl LightClient { )); } - let client = zingo_netutils::get_client(self.config.indexer_uri()).await?; + let client = + zingo_netutils::get_client(self.config.get_indexer_uri().expect("TO FIX THIS")).await?; let wallet_guard = self.wallet.read().await; let network = wallet_guard.network; let sync_config = wallet_guard.wallet_settings.sync_config.clone(); diff --git a/zingolib/src/testutils.rs b/zingolib/src/testutils.rs index 1e50b5c265..8601b3e055 100644 --- a/zingolib/src/testutils.rs +++ b/zingolib/src/testutils.rs @@ -66,7 +66,7 @@ pub fn build_fvk_client(fvks: &[&Fvk], config: ZingoConfig) -> LightClient { ); LightClient::create_from_wallet( LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Ufvk(ufvk), 1.into(), WalletSettings { diff --git a/zingolib/src/testutils/chain_generics/conduct_chain.rs b/zingolib/src/testutils/chain_generics/conduct_chain.rs index 6f4825aa9a..11f8bb7fe2 100644 --- a/zingolib/src/testutils/chain_generics/conduct_chain.rs +++ b/zingolib/src/testutils/chain_generics/conduct_chain.rs @@ -19,7 +19,7 @@ pub trait ConductChain { async fn setup() -> Self; /// used to connect to server via grpc - fn lightserver_uri(&self) -> Option; + fn indexer_uri(&self) -> Option; /// builds a faucet (funded from mining) async fn create_faucet(&mut self) -> LightClient; @@ -44,7 +44,7 @@ pub trait ConductChain { /// loads a client from bytes fn load_client(&mut self, config: ZingoConfig, data: &[u8]) -> LightClient { LightClient::create_from_wallet( - LightWallet::read(data, config.network_type()).unwrap(), + LightWallet::read(data, config.chain_type).unwrap(), config, false, ) diff --git a/zingolib/src/testutils/chain_generics/networked.rs b/zingolib/src/testutils/chain_generics/networked.rs index 5a5a7d69a1..12eccc78a7 100644 --- a/zingolib/src/testutils/chain_generics/networked.rs +++ b/zingolib/src/testutils/chain_generics/networked.rs @@ -3,20 +3,23 @@ use http::Uri; use zcash_protocol::consensus::BlockHeight; -use crate::{config::DEFAULT_TESTNET_LIGHTWALLETD_SERVER, lightclient::LightClient}; +use crate::{ + config::{DEFAULT_TESTNET_INDEXER_URI, some_infallible_uri}, + lightclient::LightClient, +}; use super::conduct_chain::ConductChain; /// this is essentially a placeholder. /// allows using existing `ChainGeneric` functions with `TestNet` wallets pub struct NetworkedTestEnvironment { - indexer_uri: Uri, + indexer_uri: Option, latest_known_server_height: Option, } impl NetworkedTestEnvironment { async fn update_server_height(&mut self) { - let latest = crate::grpc_connector::get_latest_block(self.lightserver_uri().unwrap()) + let latest = crate::grpc_connector::get_latest_block(self.indexer_uri().unwrap()) .await .unwrap() .height as u32; @@ -30,8 +33,7 @@ impl NetworkedTestEnvironment { impl ConductChain for NetworkedTestEnvironment { async fn setup() -> Self { Self { - indexer_uri: ::from_str(DEFAULT_TESTNET_LIGHTWALLETD_SERVER) - .unwrap(), + indexer_uri: some_infallible_uri(DEFAULT_TESTNET_INDEXER_URI), latest_known_server_height: None, } } @@ -56,8 +58,8 @@ impl ConductChain for NetworkedTestEnvironment { } } - fn lightserver_uri(&self) -> Option { - Some(self.indexer_uri.clone()) + fn indexer_uri(&self) -> Option { + self.indexer_uri.clone() } fn confirmation_patience_blocks(&self) -> usize { diff --git a/zingolib/src/testutils/chain_generics/with_assertions.rs b/zingolib/src/testutils/chain_generics/with_assertions.rs index 232fd93d71..a2ddb23b6d 100644 --- a/zingolib/src/testutils/chain_generics/with_assertions.rs +++ b/zingolib/src/testutils/chain_generics/with_assertions.rs @@ -122,7 +122,7 @@ where timestamped_test_log("following proposal, preparing to unwind if an assertion fails."); let server_height_at_send = BlockHeight::from( - crate::grpc_connector::get_latest_block(environment.lightserver_uri().unwrap()) + crate::grpc_connector::get_latest_block(environment.indexer_uri().unwrap()) .await .unwrap() .height as u32, diff --git a/zingolib/src/testutils/lightclient.rs b/zingolib/src/testutils/lightclient.rs index c60ed2a70b..dabf5ba8bf 100644 --- a/zingolib/src/testutils/lightclient.rs +++ b/zingolib/src/testutils/lightclient.rs @@ -18,15 +18,12 @@ pub async fn new_client_from_save_buffer( .wallet .write() .await - .write(&mut wallet_bytes, &template_client.config.network_type()) + .write(&mut wallet_bytes, &template_client.config.chain_type) .map_err(LightClientError::FileError)?; //TODO: improve read/write error variants LightClient::create_from_wallet( - LightWallet::read( - wallet_bytes.as_slice(), - template_client.config.network_type(), - ) - .map_err(LightClientError::FileError)?, + LightWallet::read(wallet_bytes.as_slice(), template_client.config.chain_type) + .map_err(LightClientError::FileError)?, template_client.config.clone(), false, ) diff --git a/zingolib/src/wallet/disk/testing/examples.rs b/zingolib/src/wallet/disk/testing/examples.rs index 51b1a3fbcf..6692dcaa16 100644 --- a/zingolib/src/wallet/disk/testing/examples.rs +++ b/zingolib/src/wallet/disk/testing/examples.rs @@ -1,8 +1,6 @@ use std::num::NonZeroU32; use bytes::Buf; -use http::Uri; - use zcash_protocol::{PoolType, ShieldedProtocol}; use pepper_sync::config::{PerformanceLevel, SyncConfig, TransparentAddressDiscovery}; @@ -10,7 +8,7 @@ use zingo_common_components::protocol::ActivationHeights; use zingo_test_vectors::seeds; use super::super::LightWallet; -use crate::config::{ChainType, DEFAULT_LIGHTWALLETD_SERVER, ZingoConfig}; +use crate::config::{ChainType, DEFAULT_INDEXER_URI, ZingoConfigBuilder, some_infallible_uri}; use crate::lightclient::LightClient; use crate::wallet::WalletSettings; @@ -279,11 +277,9 @@ impl NetworkSeedVersion { let config = match self { NetworkSeedVersion::Regtest(_) => { // Probably should be undefined. For the purpose of these tests, I hope it doesnt matter. - let lightwalletd_uri = DEFAULT_LIGHTWALLETD_SERVER.parse::().unwrap(); - - ZingoConfig::builder() - .set_indexer_uri(lightwalletd_uri) - .set_network_type(ChainType::Regtest(ActivationHeights::default())) + ZingoConfigBuilder::default() + .set_indexer_uri(some_infallible_uri(DEFAULT_INDEXER_URI)) + .set_chain(ChainType::Regtest(ActivationHeights::default())) .set_wallet_name("".to_string()) .set_wallet_settings(WalletSettings { sync_config: SyncConfig { @@ -293,13 +289,13 @@ impl NetworkSeedVersion { min_confirmations: NonZeroU32::try_from(1).unwrap(), }) .set_no_of_accounts(NonZeroU32::try_from(1).unwrap()) - .build() + .create() } NetworkSeedVersion::Testnet(_) => crate::config::ZingoConfig::create_testnet(), NetworkSeedVersion::Mainnet(_) => crate::config::ZingoConfig::create_mainnet(), }; - let wallet = self.load_example_wallet(config.network_type()); + let wallet = self.load_example_wallet(config.chain_type); LightClient::create_from_wallet(wallet, config, true).unwrap() } diff --git a/zingolib/src/wallet/disk/testing/tests.rs b/zingolib/src/wallet/disk/testing/tests.rs index cb16d62989..67b51972f3 100644 --- a/zingolib/src/wallet/disk/testing/tests.rs +++ b/zingolib/src/wallet/disk/testing/tests.rs @@ -234,12 +234,12 @@ async fn reload_wallet_from_buffer() { .wallet .write() .await - .write(&mut mid_buffer, &mid_client.config.network_type()) + .write(&mut mid_buffer, &mid_client.config.chain_type) .unwrap(); let config = ZingoConfig::create_testnet(); let client = LightClient::create_from_wallet( - LightWallet::read(&mid_buffer[..], config.network_type()).unwrap(), + LightWallet::read(&mid_buffer[..], config.chain_type).unwrap(), config, true, ) diff --git a/zingolib/src/wallet/keys/legacy/extended_transparent.rs b/zingolib/src/wallet/keys/legacy/extended_transparent.rs index 07b48197b5..3912b76766 100644 --- a/zingolib/src/wallet/keys/legacy/extended_transparent.rs +++ b/zingolib/src/wallet/keys/legacy/extended_transparent.rs @@ -112,7 +112,7 @@ impl ExtendedPrivKey { .derive_private_key(KeyIndex::hardened_from_normalize_index(44).unwrap()) .unwrap() .derive_private_key( - KeyIndex::hardened_from_normalize_index(config.network_type().coin_type()).unwrap(), + KeyIndex::hardened_from_normalize_index(config.chain_type.coin_type()).unwrap(), ) .unwrap() .derive_private_key(KeyIndex::hardened_from_normalize_index(position).unwrap()) diff --git a/zingolib/src/wallet/transaction.rs b/zingolib/src/wallet/transaction.rs index f255ae4416..c0f01b5843 100644 --- a/zingolib/src/wallet/transaction.rs +++ b/zingolib/src/wallet/transaction.rs @@ -185,14 +185,14 @@ mod tests { use zingo_status::confirmation_status::ConfirmationStatus; use crate::{ - config::ZingoConfig, + config::ZingoConfigBuilder, wallet::{LightWallet, WalletBase, WalletSettings, error::WalletError}, }; fn test_wallet() -> LightWallet { - let config = ZingoConfig::builder().build(); + let config = ZingoConfigBuilder::default().create(); LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::FreshEntropy { no_of_accounts: 1.try_into().unwrap(), }, diff --git a/zingolib_testutils/src/scenarios.rs b/zingolib_testutils/src/scenarios.rs index aaf376248c..eaa56281dd 100644 --- a/zingolib_testutils/src/scenarios.rs +++ b/zingolib_testutils/src/scenarios.rs @@ -34,6 +34,7 @@ use network_combo::DefaultValidator; use pepper_sync::config::{PerformanceLevel, SyncConfig, TransparentAddressDiscovery}; use zingo_common_components::protocol::ActivationHeights; use zingo_test_vectors::{FUND_OFFLOAD_ORCHARD_ONLY, seeds}; +use zingolib::config::ZingoConfigBuilder; use zingolib::config::{ChainType, ZingoConfig}; use zingolib::get_base_address_macro; use zingolib::lightclient::LightClient; @@ -134,7 +135,7 @@ async fn zebrad_shielded_funds( /// Struct for building lightclients for integration testing pub struct ClientBuilder { /// Indexer URI - pub server_id: http::Uri, + pub indexer_uri: Option, /// Directory for wallet files pub zingo_datadir: TempDir, client_number: u8, @@ -142,10 +143,10 @@ pub struct ClientBuilder { impl ClientBuilder { /// TODO: Add Doc Comment Here! - pub fn new(server_id: http::Uri, zingo_datadir: TempDir) -> Self { + pub fn new(indexer_uri: Option, zingo_datadir: TempDir) -> Self { let client_number = 0; ClientBuilder { - server_id, + indexer_uri, zingo_datadir, client_number, } @@ -163,19 +164,18 @@ impl ClientBuilder { self.zingo_datadir.path().to_string_lossy(), self.client_number ); - self.create_clientconfig(PathBuf::from(conf_path), configured_activation_heights) + self.create_lightclient_config(PathBuf::from(conf_path), configured_activation_heights) } - /// TODO: Add Doc Comment Here! - pub fn create_clientconfig( + pub fn create_lightclient_config( &self, conf_path: PathBuf, configured_activation_heights: ActivationHeights, ) -> ZingoConfig { std::fs::create_dir(&conf_path).unwrap(); - ZingoConfig::builder() - .set_indexer_uri(self.server_id.clone()) - .set_network_type(ChainType::Regtest(configured_activation_heights)) + ZingoConfigBuilder::default() + .set_indexer_uri(self.indexer_uri.clone()) + .set_chain(ChainType::Regtest(configured_activation_heights)) .set_wallet_dir(conf_path) .set_wallet_name("".to_string()) .set_wallet_settings(WalletSettings { @@ -185,8 +185,7 @@ impl ClientBuilder { }, min_confirmations: NonZeroU32::try_from(1).unwrap(), }) - .set_no_of_accounts(NonZeroU32::try_from(1).unwrap()) - .build() + .create() } /// TODO: Add Doc Comment Here! @@ -214,13 +213,13 @@ impl ClientBuilder { ) -> LightClient { let config = self.make_unique_data_dir_and_load_config(configured_activation_heights); let mut wallet = LightWallet::new( - config.network_type(), + config.chain_type, WalletBase::Mnemonic { mnemonic: Mnemonic::from_phrase(mnemonic_phrase).unwrap(), no_of_accounts: 1.try_into().unwrap(), }, (birthday as u32).into(), - config.wallet_settings(), + config.wallet_settings.clone(), ) .unwrap(); wallet @@ -459,7 +458,7 @@ pub async fn custom_clients( } let client_builder = ClientBuilder::new( - port_to_localhost_uri(local_net.indexer().listen_port()), + Some(port_to_localhost_uri(local_net.indexer().listen_port())), tempfile::tempdir().unwrap(), ); @@ -490,7 +489,7 @@ pub async fn unfunded_mobileclient() -> LocalNet LocalNet { let local_net = unfunded_mobileclient().await; let mut client_builder = ClientBuilder::new( - port_to_localhost_uri(local_net.indexer().port()), + Some(port_to_localhost_uri(local_net.indexer().port())), tempfile::tempdir().unwrap(), ); let mut faucet = @@ -519,7 +518,7 @@ pub async fn funded_orchard_with_3_txs_mobileclient( ) -> LocalNet { let local_net = unfunded_mobileclient().await; let mut client_builder = ClientBuilder::new( - port_to_localhost_uri(local_net.indexer().port()), + Some(port_to_localhost_uri(local_net.indexer().port())), tempfile::tempdir().unwrap(), ); let mut faucet = @@ -577,7 +576,7 @@ pub async fn funded_transparent_mobileclient( ) -> LocalNet { let local_net = unfunded_mobileclient().await; let mut client_builder = ClientBuilder::new( - port_to_localhost_uri(local_net.indexer().port()), + Some(port_to_localhost_uri(local_net.indexer().port())), tempfile::tempdir().unwrap(), ); let mut faucet = @@ -618,7 +617,7 @@ pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( ) -> LocalNet { let local_net = unfunded_mobileclient().await; let mut client_builder = ClientBuilder::new( - port_to_localhost_uri(local_net.indexer().port()), + Some(port_to_localhost_uri(local_net.indexer().port())), tempfile::tempdir().unwrap(), ); let mut faucet =