diff --git a/libtonode-tests/tests/chain_generics.rs b/libtonode-tests/tests/chain_generics.rs index c217a879a0..270a51c2c2 100644 --- a/libtonode-tests/tests/chain_generics.rs +++ b/libtonode-tests/tests/chain_generics.rs @@ -192,6 +192,128 @@ mod chain_generics { async fn simpool_change_50_000_orchard_to_orchard() { fixtures::shpool_to_pool::(Orchard, Shielded(Orchard), 50_000).await; } + #[tokio::test] + async fn simpool_insufficient_1_sapling_to_transparent() { + fixtures::shpool_to_pool_insufficient_error::( + Sapling, + Transparent, + 1, + ) + .await; + } + #[tokio::test] + async fn simpool_insufficient_1_sapling_to_sapling() { + fixtures::shpool_to_pool_insufficient_error::( + Sapling, + Shielded(Sapling), + 1, + ) + .await; + } + #[tokio::test] + async fn simpool_insufficient_1_sapling_to_orchard() { + fixtures::shpool_to_pool_insufficient_error::( + Sapling, + Shielded(Orchard), + 1, + ) + .await; + } + #[tokio::test] + async fn simpool_insufficient_1_orchard_to_transparent() { + fixtures::shpool_to_pool_insufficient_error::( + Orchard, + Transparent, + 1, + ) + .await; + } + #[tokio::test] + async fn simpool_insufficient_1_orchard_to_sapling() { + fixtures::shpool_to_pool_insufficient_error::( + Orchard, + Shielded(Sapling), + 1, + ) + .await; + } + #[tokio::test] + async fn simpool_insufficient_1_orchard_to_orchard() { + fixtures::shpool_to_pool_insufficient_error::( + Orchard, + Shielded(Orchard), + 1, + ) + .await + } + #[tokio::test] + async fn simpool_insufficient_10_000_sapling_to_transparent() { + fixtures::shpool_to_pool_insufficient_error::( + Sapling, + Transparent, + 10_000, + ) + .await; + } + #[tokio::test] + async fn simpool_insufficient_10_000_sapling_to_sapling() { + fixtures::shpool_to_pool_insufficient_error::( + Sapling, + Shielded(Sapling), + 10_000, + ) + .await; + } + #[tokio::test] + async fn simpool_insufficient_10_000_sapling_to_orchard() { + fixtures::shpool_to_pool_insufficient_error::( + Sapling, + Shielded(Orchard), + 10_000, + ) + .await; + } + #[tokio::test] + async fn simpool_insufficient_10_000_orchard_to_transparent() { + fixtures::shpool_to_pool_insufficient_error::( + Orchard, + Transparent, + 10_000, + ) + .await; + } + #[tokio::test] + async fn simpool_insufficient_10_000_orchard_to_sapling() { + fixtures::shpool_to_pool_insufficient_error::( + Orchard, + Shielded(Sapling), + 10_000, + ) + .await; + } + #[tokio::test] + async fn simpool_insufficient_10_000_orchard_to_orchard() { + fixtures::shpool_to_pool_insufficient_error::( + Orchard, + Shielded(Orchard), + 10_000, + ) + .await; + } + #[tokio::test] + async fn simpool_no_fund_1_000_000_to_transparent() { + fixtures::to_pool_unfunded_error::(Transparent, 1_000_000).await; + } + #[tokio::test] + async fn simpool_no_fund_1_000_000_to_sapling() { + fixtures::to_pool_unfunded_error::(Shielded(Sapling), 1_000_000) + .await; + } + #[tokio::test] + async fn simpool_no_fund_1_000_000_to_orchard() { + fixtures::to_pool_unfunded_error::(Shielded(Orchard), 1_000_000) + .await; + } mod conduct_chain { use zcash_client_backend::PoolType; diff --git a/zingolib/src/testutils/chain_generics/fixtures.rs b/zingolib/src/testutils/chain_generics/fixtures.rs index 345f97a8c8..9a7e7750ea 100644 --- a/zingolib/src/testutils/chain_generics/fixtures.rs +++ b/zingolib/src/testutils/chain_generics/fixtures.rs @@ -513,7 +513,7 @@ where .await; let tertiary = environment.create_client().await; - let expected_fee = fee_tables::one_to_one(shpool, pool, true); + let expected_fee = fee_tables::one_to_one(Some(shpool), pool, true); let ref_primary: Arc = Arc::new(primary); let ref_secondary: Arc = Arc::new(secondary); @@ -540,3 +540,97 @@ where .await ); } + +/// the simplest test that sends from a specific shielded pool to another specific pool. error variant. +pub async fn shpool_to_pool_insufficient_error( + shpool: ShieldedProtocol, + pool: PoolType, + underflow_amount: u64, +) where + CC: ConductChain, +{ + let mut environment = CC::setup().await; + + let primary = environment.fund_client_orchard(1_000_000).await; + let secondary = environment.create_client().await; + + let expected_fee = fee_tables::one_to_one(Some(shpool), pool, true); + let secondary_fund = 100_000 + expected_fee - underflow_amount; + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &primary, + vec![(&secondary, Shielded(shpool), secondary_fund, None)], + false, + ) + .await; + + let tertiary = environment.create_client().await; + + let ref_secondary: Arc = Arc::new(secondary); + let ref_tertiary: Arc = Arc::new(tertiary); + + let tertiary_fund = 100_000; + assert_eq!( + from_inputs::propose( + &ref_secondary, + vec![( + ref_tertiary + .wallet + .get_first_address(pool) + .unwrap() + .as_str(), + tertiary_fund, + None, + )], + ) + .await + .unwrap_err() + .to_string(), + format!( + "Insufficient balance (have {}, need {} including fee)", + secondary_fund, + tertiary_fund + expected_fee + ) + ); +} + +/// the simplest test that sends from a specific shielded pool to another specific pool. also known as simpool. +pub async fn to_pool_unfunded_error(pool: PoolType, try_amount: u64) +where + CC: ConductChain, +{ + let mut environment = CC::setup().await; + + let secondary = environment.create_client().await; + let tertiary = environment.create_client().await; + + let ref_secondary: Arc = Arc::new(secondary); + let ref_tertiary: Arc = Arc::new(tertiary); + + ref_secondary.do_sync(false).await.unwrap(); + + let expected_fee = fee_tables::one_to_one(None, pool, true); + + assert_eq!( + from_inputs::propose( + &ref_secondary, + vec![( + ref_tertiary + .wallet + .get_first_address(pool) + .unwrap() + .as_str(), + try_amount, + None, + )], + ) + .await + .unwrap_err() + .to_string(), + format!( + "Insufficient balance (have {}, need {} including fee)", + 0, + try_amount + expected_fee + ) + ); +} diff --git a/zingolib/src/testutils/fee_tables.rs b/zingolib/src/testutils/fee_tables.rs index 553f36781c..64bd055d0e 100644 --- a/zingolib/src/testutils/fee_tables.rs +++ b/zingolib/src/testutils/fee_tables.rs @@ -13,7 +13,11 @@ use zcash_primitives::transaction::fees::zip317::MARGINAL_FEE; /// estimates a fee based on the zip317 protocol rules /// -pub fn one_to_one(source_protocol: ShieldedProtocol, target_pool: PoolType, change: bool) -> u64 { +pub fn one_to_one( + source_protocol: Option, + target_pool: PoolType, + change: bool, +) -> u64 { let transparent_inputs = 0; let mut transparent_outputs = 0; let mut sapling_inputs = 0; @@ -21,8 +25,9 @@ pub fn one_to_one(source_protocol: ShieldedProtocol, target_pool: PoolType, chan let mut orchard_inputs = 0; let mut orchard_outputs = 0; match source_protocol { - Sapling => sapling_inputs += 1, - Orchard => orchard_inputs += 1, + Some(Sapling) => sapling_inputs += 1, + Some(Orchard) => orchard_inputs += 1, + _ => {} } match target_pool { Transparent => transparent_outputs += 1, diff --git a/zingolib/src/wallet/describe.rs b/zingolib/src/wallet/describe.rs index a7fc9ec350..2ff054a8bf 100644 --- a/zingolib/src/wallet/describe.rs +++ b/zingolib/src/wallet/describe.rs @@ -313,17 +313,21 @@ mod test { use crate::wallet::LightWallet; - // these functions are tested gold and a better pattern for address lookup. - // maybe later they can be gated in. + /// these functions have clearer typing than + /// the production functions using json that could be upgraded soon impl LightWallet { #[allow(clippy::result_unit_err)] - /// gets a UnifiedAddress, the first the wallet. this is the only receiver implemented as 2024-09-22 + /// gets a UnifiedAddress, the first of the wallet. + /// zingolib includes derivations of further addresses. + /// ZingoMobile uses one address. pub fn get_first_ua(&self) -> Result { - if let Some(possible_ua) = self.wallet_capability().addresses().iter().next() { - Ok(possible_ua.clone()) - } else { - Err(()) - } + Ok(self + .wallet_capability() + .addresses() + .iter() + .next() + .ok_or(())? + .clone()) } #[allow(clippy::result_unit_err)]