Skip to content

Commit 314d3e9

Browse files
Merge pull request #1159 from Oscar-Pepper/confirmed_balance_excluding_dust
Confirmed balance excluding dust
2 parents 5ff25e8 + 56606c7 commit 314d3e9

File tree

4 files changed

+145
-7
lines changed

4 files changed

+145
-7
lines changed

libtonode-tests/tests/legacy.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,7 +1460,10 @@ mod slow {
14601460
- first_send_to_transparent
14611461
- (2 * u64::from(MINIMUM_FEE));
14621462
assert_eq!(
1463-
recipient.wallet.maybe_verified_orchard_balance(None).await,
1463+
recipient
1464+
.wallet
1465+
.shielded_balance::<OrchardDomain>(None, &[])
1466+
.await,
14641467
Some(expected_funds)
14651468
);
14661469
assert_eq!(
@@ -1543,7 +1546,10 @@ mod slow {
15431546
- third_send_to_transparent
15441547
- (3 * u64::from(MINIMUM_FEE));
15451548
assert_eq!(
1546-
recipient.wallet.maybe_verified_orchard_balance(None).await,
1549+
recipient
1550+
.wallet
1551+
.shielded_balance::<OrchardDomain>(None, &[])
1552+
.await,
15471553
Some(second_wave_expected_funds),
15481554
);
15491555

zingolib/src/mocks.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,16 @@ pub mod orchard_note {
279279
build_method!(rho, Rho);
280280
build_method!(random_seed, RandomSeed);
281281

282+
/// selects a default recipient address for the orchard note
283+
pub fn default_recipient(&mut self) -> &mut Self {
284+
let bytes = [0; 32];
285+
let sk = SpendingKey::from_bytes(bytes).unwrap();
286+
let fvk: FullViewingKey = (&sk).into();
287+
let recipient = fvk.address_at(0u32, Scope::External);
288+
289+
self.recipient(recipient)
290+
}
291+
282292
/// selects a random recipient address for the orchard note
283293
pub fn randomize_recipient(&mut self) -> &mut Self {
284294
let mut rng = OsRng;
@@ -343,7 +353,7 @@ pub mod orchard_note {
343353
impl Default for OrchardCryptoNoteBuilder {
344354
fn default() -> Self {
345355
Self::new()
346-
.randomize_recipient()
356+
.default_recipient()
347357
.randomize_rho_and_rseed()
348358
.value(NoteValue::from_raw(800_000))
349359
.clone()

zingolib/src/wallet/describe.rs

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use orchard::note_encryption::OrchardDomain;
33

44
use sapling_crypto::note_encryption::SaplingDomain;
5+
use zcash_primitives::transaction::fees::zip317::MARGINAL_FEE;
56

67
use std::{cmp, sync::Arc};
78
use tokio::sync::RwLock;
@@ -82,6 +83,7 @@ impl LightWallet {
8283
}
8384

8485
/// TODO: Add Doc Comment Here!
86+
// TODO: this should minus the fee of sending the confirmed balance!
8587
pub async fn spendable_orchard_balance(&self, target_addr: Option<String>) -> Option<u64> {
8688
if let Capability::Spend(_) = self.wallet_capability().orchard {
8789
self.verified_balance::<OrchardDomain>(target_addr).await
@@ -91,6 +93,7 @@ impl LightWallet {
9193
}
9294

9395
/// TODO: Add Doc Comment Here!
96+
// TODO: this should minus the fee of sending the confirmed balance!
9497
pub async fn spendable_sapling_balance(&self, target_addr: Option<String>) -> Option<u64> {
9598
if let Capability::Spend(_) = self.wallet_capability().sapling {
9699
self.verified_balance::<SaplingDomain>(target_addr).await
@@ -161,12 +164,33 @@ impl LightWallet {
161164
self.shielded_balance::<D>(target_addr, filters).await
162165
}
163166

164-
/// TODO: Add Doc Comment Here!
167+
/// Returns balance for a given shielded pool excluding any notes with value less than marginal fee
168+
/// that are confirmed on the block chain (the block has at least 1 confirmation)
169+
pub async fn confirmed_balance_excluding_dust<D: DomainWalletExt>(
170+
&self,
171+
target_addr: Option<String>,
172+
) -> Option<u64>
173+
where
174+
<D as Domain>::Recipient: Recipient,
175+
<D as Domain>::Note: PartialEq + Clone,
176+
{
177+
#[allow(clippy::type_complexity)]
178+
let filters: &[Box<dyn Fn(&&D::WalletNote, &TransactionRecord) -> bool>] = &[
179+
Box::new(|_, transaction| transaction.status.is_confirmed()),
180+
Box::new(|note, _| !note.pending_receipt()),
181+
Box::new(|note, _| note.value() >= MARGINAL_FEE.into_u64()),
182+
];
183+
self.shielded_balance::<D>(target_addr, filters).await
184+
}
185+
186+
/// Deprecated for `shielded_balance`
187+
#[deprecated(note = "deprecated for `shielded_balance` as incorrectly named and unnecessary")]
165188
pub async fn maybe_verified_orchard_balance(&self, addr: Option<String>) -> Option<u64> {
166189
self.shielded_balance::<OrchardDomain>(addr, &[]).await
167190
}
168191

169-
/// TODO: Add Doc Comment Here!
192+
/// Deprecated for `shielded_balance`
193+
#[deprecated(note = "deprecated for `shielded_balance` as incorrectly named and unnecessary")]
170194
pub async fn maybe_verified_sapling_balance(&self, addr: Option<String>) -> Option<u64> {
171195
self.shielded_balance::<SaplingDomain>(addr, &[]).await
172196
}
@@ -203,7 +227,7 @@ impl LightWallet {
203227
/// Get the height of the anchor block
204228
pub async fn get_anchor_height(&self) -> u32 {
205229
match self.get_target_height_and_anchor_offset().await {
206-
Some((height, anchor_offset)) => height - anchor_offset as u32 - 1,
230+
Some((height, anchor_offset)) => height - anchor_offset as u32 - 1, // what is the purpose of this -1 ?
207231
None => 0,
208232
}
209233
}
@@ -290,3 +314,101 @@ impl LightWallet {
290314
self.transaction_context.transaction_metadata_set.clone()
291315
}
292316
}
317+
318+
#[cfg(test)]
319+
mod tests {
320+
use orchard::note_encryption::OrchardDomain;
321+
use sapling_crypto::note_encryption::SaplingDomain;
322+
323+
use zingo_status::confirmation_status::ConfirmationStatus;
324+
use zingoconfig::ZingoConfigBuilder;
325+
326+
use crate::{
327+
mocks::{orchard_note::OrchardCryptoNoteBuilder, SaplingCryptoNoteBuilder},
328+
wallet::{
329+
notes::{
330+
orchard::mocks::OrchardNoteBuilder, sapling::mocks::SaplingNoteBuilder,
331+
transparent::mocks::TransparentOutputBuilder,
332+
},
333+
transaction_record::mocks::TransactionRecordBuilder,
334+
LightWallet, WalletBase,
335+
},
336+
};
337+
338+
#[tokio::test]
339+
async fn confirmed_balance_excluding_dust() {
340+
let wallet = LightWallet::new(
341+
ZingoConfigBuilder::default().create(),
342+
WalletBase::FreshEntropy,
343+
1,
344+
)
345+
.unwrap();
346+
let confirmed_tx_record = TransactionRecordBuilder::default()
347+
.status(ConfirmationStatus::Confirmed(80.into()))
348+
.transparent_outputs(TransparentOutputBuilder::default())
349+
.sapling_notes(SaplingNoteBuilder::default())
350+
.sapling_notes(SaplingNoteBuilder::default())
351+
.sapling_notes(
352+
SaplingNoteBuilder::default()
353+
.note(
354+
SaplingCryptoNoteBuilder::default()
355+
.value(sapling_crypto::value::NoteValue::from_raw(3_000))
356+
.clone(),
357+
)
358+
.clone(),
359+
)
360+
.orchard_notes(OrchardNoteBuilder::default())
361+
.orchard_notes(OrchardNoteBuilder::default())
362+
.orchard_notes(
363+
OrchardNoteBuilder::default()
364+
.note(
365+
OrchardCryptoNoteBuilder::default()
366+
.value(orchard::value::NoteValue::from_raw(5_000))
367+
.clone(),
368+
)
369+
.clone(),
370+
)
371+
.orchard_notes(
372+
OrchardNoteBuilder::default()
373+
.note(
374+
OrchardCryptoNoteBuilder::default()
375+
.value(orchard::value::NoteValue::from_raw(2_000))
376+
.clone(),
377+
)
378+
.clone(),
379+
)
380+
.build();
381+
let pending_tx_record = TransactionRecordBuilder::default()
382+
.status(ConfirmationStatus::Pending(95.into()))
383+
.transparent_outputs(TransparentOutputBuilder::default())
384+
.sapling_notes(SaplingNoteBuilder::default())
385+
.orchard_notes(OrchardNoteBuilder::default())
386+
.build();
387+
{
388+
let mut tx_map = wallet
389+
.transaction_context
390+
.transaction_metadata_set
391+
.write()
392+
.await;
393+
tx_map
394+
.transaction_records_by_id
395+
.insert_transaction_record(confirmed_tx_record);
396+
tx_map
397+
.transaction_records_by_id
398+
.insert_transaction_record(pending_tx_record);
399+
}
400+
401+
assert_eq!(
402+
wallet
403+
.confirmed_balance_excluding_dust::<SaplingDomain>(None)
404+
.await,
405+
Some(400_000)
406+
);
407+
assert_eq!(
408+
wallet
409+
.confirmed_balance_excluding_dust::<OrchardDomain>(None)
410+
.await,
411+
Some(1_605_000)
412+
);
413+
}
414+
}

zingolib/src/wallet/transaction_record.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ pub mod mocks {
655655
),
656656
),
657657
datetime: Some(1705077003),
658-
txid: Some(crate::mocks::default_txid()),
658+
txid: Some(crate::mocks::random_txid()),
659659
spent_sapling_nullifiers: vec![],
660660
spent_orchard_nullifiers: vec![],
661661
transparent_outputs: vec![],

0 commit comments

Comments
 (0)