Skip to content

Commit

Permalink
Merge pull request zingolabs#1423 from fluidvanadium/generic_insuffic…
Browse files Browse the repository at this point in the history
…ient

Test that sending with insufficient funds returns a useful error.
  • Loading branch information
zancas authored Oct 1, 2024
2 parents 349c736 + 9219832 commit dd1c588
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 12 deletions.
122 changes: 122 additions & 0 deletions libtonode-tests/tests/chain_generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,128 @@ mod chain_generics {
async fn simpool_change_50_000_orchard_to_orchard() {
fixtures::shpool_to_pool::<LibtonodeEnvironment>(Orchard, Shielded(Orchard), 50_000).await;
}
#[tokio::test]
async fn simpool_insufficient_1_sapling_to_transparent() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Sapling,
Transparent,
1,
)
.await;
}
#[tokio::test]
async fn simpool_insufficient_1_sapling_to_sapling() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Sapling,
Shielded(Sapling),
1,
)
.await;
}
#[tokio::test]
async fn simpool_insufficient_1_sapling_to_orchard() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Sapling,
Shielded(Orchard),
1,
)
.await;
}
#[tokio::test]
async fn simpool_insufficient_1_orchard_to_transparent() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Orchard,
Transparent,
1,
)
.await;
}
#[tokio::test]
async fn simpool_insufficient_1_orchard_to_sapling() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Orchard,
Shielded(Sapling),
1,
)
.await;
}
#[tokio::test]
async fn simpool_insufficient_1_orchard_to_orchard() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Orchard,
Shielded(Orchard),
1,
)
.await
}
#[tokio::test]
async fn simpool_insufficient_10_000_sapling_to_transparent() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Sapling,
Transparent,
10_000,
)
.await;
}
#[tokio::test]
async fn simpool_insufficient_10_000_sapling_to_sapling() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Sapling,
Shielded(Sapling),
10_000,
)
.await;
}
#[tokio::test]
async fn simpool_insufficient_10_000_sapling_to_orchard() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Sapling,
Shielded(Orchard),
10_000,
)
.await;
}
#[tokio::test]
async fn simpool_insufficient_10_000_orchard_to_transparent() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Orchard,
Transparent,
10_000,
)
.await;
}
#[tokio::test]
async fn simpool_insufficient_10_000_orchard_to_sapling() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Orchard,
Shielded(Sapling),
10_000,
)
.await;
}
#[tokio::test]
async fn simpool_insufficient_10_000_orchard_to_orchard() {
fixtures::shpool_to_pool_insufficient_error::<LibtonodeEnvironment>(
Orchard,
Shielded(Orchard),
10_000,
)
.await;
}
#[tokio::test]
async fn simpool_no_fund_1_000_000_to_transparent() {
fixtures::to_pool_unfunded_error::<LibtonodeEnvironment>(Transparent, 1_000_000).await;
}
#[tokio::test]
async fn simpool_no_fund_1_000_000_to_sapling() {
fixtures::to_pool_unfunded_error::<LibtonodeEnvironment>(Shielded(Sapling), 1_000_000)
.await;
}
#[tokio::test]
async fn simpool_no_fund_1_000_000_to_orchard() {
fixtures::to_pool_unfunded_error::<LibtonodeEnvironment>(Shielded(Orchard), 1_000_000)
.await;
}
mod conduct_chain {
use zcash_client_backend::PoolType;

Expand Down
96 changes: 95 additions & 1 deletion zingolib/src/testutils/chain_generics/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<LightClient> = Arc::new(primary);
let ref_secondary: Arc<LightClient> = Arc::new(secondary);
Expand All @@ -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<CC>(
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<LightClient> = Arc::new(secondary);
let ref_tertiary: Arc<LightClient> = 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<CC>(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<LightClient> = Arc::new(secondary);
let ref_tertiary: Arc<LightClient> = 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
)
);
}
11 changes: 8 additions & 3 deletions zingolib/src/testutils/fee_tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,21 @@ use zcash_primitives::transaction::fees::zip317::MARGINAL_FEE;

/// estimates a fee based on the zip317 protocol rules
/// <https://zips.z.cash/zip-0317>
pub fn one_to_one(source_protocol: ShieldedProtocol, target_pool: PoolType, change: bool) -> u64 {
pub fn one_to_one(
source_protocol: Option<ShieldedProtocol>,
target_pool: PoolType,
change: bool,
) -> u64 {
let transparent_inputs = 0;
let mut transparent_outputs = 0;
let mut sapling_inputs = 0;
let mut sapling_outputs = 0;
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,
Expand Down
20 changes: 12 additions & 8 deletions zingolib/src/wallet/describe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<zcash_keys::address::UnifiedAddress, ()> {
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)]
Expand Down

0 comments on commit dd1c588

Please sign in to comment.