Skip to content

Commit

Permalink
Merge pull request #967 from fluidvanadium/note_query
Browse files Browse the repository at this point in the history
Note query
  • Loading branch information
zancas authored Apr 21, 2024
2 parents 5297ce6 + 1443744 commit 316f163
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 45 deletions.
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions zingolib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ derive_more = "0.99.17"
either = "1.8.1"
serde = { version = "1.0.188", features = ["derive"] }
sapling-crypto.workspace = true
getset = "0.1.2"

[dev-dependencies]
portpicker = "0.1.0"
Expand Down
89 changes: 89 additions & 0 deletions zingolib/src/wallet/notes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod sapling;
pub use sapling::SaplingNote;
pub mod orchard;
pub use orchard::OrchardNote;
pub mod query;

use zcash_client_backend::PoolType;
use zcash_primitives::transaction::TxId;
Expand Down Expand Up @@ -97,3 +98,91 @@ pub mod mocks {
}
}
}

#[cfg(test)]
pub mod tests {
use crate::{
test_framework::mocks::default_txid,
wallet::notes::{
query::NoteQuery, sapling::mocks::SaplingNoteBuilder,
transparent::mocks::TransparentNoteBuilder, NoteInterface,
},
};

use super::query::{NotePoolQuery, NoteSpendStatusQuery};

#[test]
fn note_queries() {
let spend = Some((default_txid(), 112358));

let transparent_unspent_note = TransparentNoteBuilder::default().build();
let transparent_pending_spent_note = TransparentNoteBuilder::default()
.unconfirmed_spent(spend)
.build();
let transparent_spent_note = TransparentNoteBuilder::default().spent(spend).build();
let sapling_unspent_note = SaplingNoteBuilder::default().build();
let sapling_pending_spent_note = SaplingNoteBuilder::default()
.unconfirmed_spent(spend)
.build();
let sapling_spent_note = SaplingNoteBuilder::default().spent(spend).build();

let unspent_query = NoteSpendStatusQuery::new(true, false, false);
let pending_or_spent_query = NoteSpendStatusQuery::new(false, true, true);
let spent_query = NoteSpendStatusQuery::new(false, false, true);

let transparent_query = NotePoolQuery::new(true, false, false);
let shielded_query = NotePoolQuery::new(false, true, true);
let any_pool_query = NotePoolQuery::new(true, true, true);

let unspent_transparent_query = NoteQuery::new(unspent_query, transparent_query);
let unspent_any_pool_query = NoteQuery::new(unspent_query, any_pool_query);
let pending_or_spent_transparent_query =
NoteQuery::new(pending_or_spent_query, transparent_query);
let pending_or_spent_shielded_query =
NoteQuery::new(pending_or_spent_query, shielded_query);
let spent_shielded_query = NoteQuery::new(spent_query, shielded_query);
let spent_any_pool_query = NoteQuery::new(spent_query, any_pool_query);

assert!(transparent_unspent_note.query(unspent_transparent_query));
assert!(transparent_unspent_note.query(unspent_any_pool_query));
assert!(!transparent_unspent_note.query(pending_or_spent_transparent_query));
assert!(!transparent_unspent_note.query(pending_or_spent_shielded_query));
assert!(!transparent_unspent_note.query(spent_shielded_query));
assert!(!transparent_unspent_note.query(spent_any_pool_query));

assert!(!transparent_pending_spent_note.query(unspent_transparent_query));
assert!(!transparent_pending_spent_note.query(unspent_any_pool_query));
assert!(transparent_pending_spent_note.query(pending_or_spent_transparent_query));
assert!(!transparent_pending_spent_note.query(pending_or_spent_shielded_query));
assert!(!transparent_pending_spent_note.query(spent_shielded_query));
assert!(!transparent_pending_spent_note.query(spent_any_pool_query));

assert!(!transparent_spent_note.query(unspent_transparent_query));
assert!(!transparent_spent_note.query(unspent_any_pool_query));
assert!(transparent_spent_note.query(pending_or_spent_transparent_query));
assert!(!transparent_spent_note.query(pending_or_spent_shielded_query));
assert!(!transparent_spent_note.query(spent_shielded_query));
assert!(transparent_spent_note.query(spent_any_pool_query));

assert!(!sapling_unspent_note.query(unspent_transparent_query));
assert!(sapling_unspent_note.query(unspent_any_pool_query));
assert!(!sapling_unspent_note.query(pending_or_spent_transparent_query));
assert!(!sapling_unspent_note.query(pending_or_spent_shielded_query));
assert!(!sapling_unspent_note.query(spent_shielded_query));
assert!(!sapling_unspent_note.query(spent_any_pool_query));

assert!(!sapling_pending_spent_note.query(unspent_transparent_query));
assert!(!sapling_pending_spent_note.query(unspent_any_pool_query));
assert!(!sapling_pending_spent_note.query(pending_or_spent_transparent_query));
assert!(sapling_pending_spent_note.query(pending_or_spent_shielded_query));
assert!(!sapling_pending_spent_note.query(spent_shielded_query));
assert!(!sapling_pending_spent_note.query(spent_any_pool_query));

assert!(!sapling_spent_note.query(unspent_transparent_query));
assert!(!sapling_spent_note.query(unspent_any_pool_query));
assert!(!sapling_spent_note.query(pending_or_spent_transparent_query));
assert!(sapling_spent_note.query(pending_or_spent_shielded_query));
assert!(sapling_spent_note.query(spent_shielded_query));
assert!(sapling_spent_note.query(spent_any_pool_query));
}
}
38 changes: 33 additions & 5 deletions zingolib/src/wallet/notes/interface.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
//! TODO: Add Mod Description Here!
use incrementalmerkletree::{Hashable, Position};
use zcash_client_backend::{PoolType, ShieldedProtocol};
use zcash_primitives::{memo::Memo, merkle_tree::HashSer, transaction::TxId};

use super::super::{
data::TransactionRecord,
keys::unified::WalletCapability,
traits::{FromBytes, FromCommitment, Nullifier, ReadableWriteable, ToBytes},
Pool,
use super::{
super::{
data::TransactionRecord,
keys::unified::WalletCapability,
traits::{FromBytes, FromCommitment, Nullifier, ReadableWriteable, ToBytes},
Pool,
},
query::{NotePoolQuery, NoteQuery, NoteSpendStatusQuery},
};

/// TODO: Add Doc Comment Here!
pub trait NoteInterface: Sized {
/// returns the zcash_client_backend PoolType enum (one of 3)
fn pool_type(&self) -> PoolType;

/// TODO: Add Doc Comment Here!
fn spent(&self) -> &Option<(TxId, u32)>;

Expand All @@ -37,6 +44,27 @@ pub trait NoteInterface: Sized {
fn is_spent_or_pending_spent(&self) -> bool {
self.is_spent() || self.is_pending_spent()
}

/// Returns true if the note has one of the spend statuses enumerated by the query
fn spend_status_query(&self, query: NoteSpendStatusQuery) -> bool {
(*query.unspent() && !self.is_spent() && !self.is_pending_spent())
|| (*query.pending_spent() && self.is_pending_spent())
|| (*query.spent() && self.is_spent())
}

/// Returns true if the note is one of the pools enumerated by the query.
fn pool_query(&self, query: NotePoolQuery) -> bool {
(*query.transparent() && self.pool_type() == PoolType::Transparent)
|| (*query.sapling()
&& self.pool_type() == PoolType::Shielded(ShieldedProtocol::Sapling))
|| (*query.orchard()
&& self.pool_type() == PoolType::Shielded(ShieldedProtocol::Orchard))
}

/// Returns true if the note is one of the spend statuses enumerated by the query AND one of the pools enumerated by the query.
fn query(&self, query: NoteQuery) -> bool {
self.spend_status_query(*query.spend_status()) && self.pool_query(*query.pools())
}
}

/// ShieldedNotes are either part of a Sapling or Orchard Pool
Expand Down
5 changes: 5 additions & 0 deletions zingolib/src/wallet/notes/orchard.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! TODO: Add Mod Description Here!
use incrementalmerkletree::Position;
use zcash_client_backend::{PoolType, ShieldedProtocol};
use zcash_primitives::{memo::Memo, transaction::TxId};

use super::{
Expand Down Expand Up @@ -42,6 +43,10 @@ pub struct OrchardNote {
}

impl NoteInterface for OrchardNote {
fn pool_type(&self) -> PoolType {
PoolType::Shielded(ShieldedProtocol::Orchard)
}

fn spent(&self) -> &Option<(TxId, u32)> {
&self.spent
}
Expand Down
70 changes: 70 additions & 0 deletions zingolib/src/wallet/notes/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//! Contains structs for querying a database about notes.

use derive_more::Constructor;
use getset::Getters;

/// Selects received notes by how they been spent
#[derive(Getters, Constructor, Clone, Copy)]
pub struct NoteSpendStatusQuery {
/// will the query include unspent notes?
#[getset(get = "pub")]
unspent: bool,
/// will the query include pending_spent notes?
#[getset(get = "pub")]
pending_spent: bool,
/// will the query include spent notes?
#[getset(get = "pub")]
spent: bool,
}

/// Selects received notes by pool
#[derive(Getters, Constructor, Clone, Copy)]
pub struct NotePoolQuery {
/// will the query include transparent notes? (coins)
#[getset(get = "pub")]
transparent: bool,
/// will the query include sapling notes?
#[getset(get = "pub")]
sapling: bool,
/// will the query include orchard notes?
#[getset(get = "pub")]
orchard: bool,
}

/// Selects received notes by any properties
#[derive(Getters, Constructor, Clone, Copy)]
pub struct NoteQuery {
/// selects spend status properties
#[getset(get = "pub")]
spend_status: NoteSpendStatusQuery,
/// selects pools
#[getset(get = "pub")]
pools: NotePoolQuery,
}

impl NoteQuery {
/// will the query include unspent notes?
pub fn unspent(&self) -> &bool {
self.spend_status().unspent()
}
/// will the query include pending_spent notes?
pub fn pending_spent(&self) -> &bool {
self.spend_status().pending_spent()
}
/// will the query include spent notes?
pub fn spent(&self) -> &bool {
self.spend_status().spent()
}
/// will the query include transparent notes? (coins)
pub fn transparent(&self) -> &bool {
self.pools().transparent()
}
/// will the query include sapling notes?
pub fn sapling(&self) -> &bool {
self.pools().sapling()
}
/// will the query include orchard notes?
pub fn orchard(&self) -> &bool {
self.pools().orchard()
}
}
25 changes: 5 additions & 20 deletions zingolib/src/wallet/notes/sapling.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! TODO: Add Mod Description Here!
use incrementalmerkletree::Position;
use zcash_client_backend::{PoolType, ShieldedProtocol};
use zcash_primitives::{memo::Memo, transaction::TxId};

use super::{
Expand Down Expand Up @@ -62,6 +63,10 @@ impl std::fmt::Debug for SaplingNote {
}

impl NoteInterface for SaplingNote {
fn pool_type(&self) -> PoolType {
PoolType::Shielded(ShieldedProtocol::Sapling)
}

fn spent(&self) -> &Option<(TxId, u32)> {
&self.spent
}
Expand Down Expand Up @@ -280,23 +285,3 @@ pub mod mocks {
}
}
}

#[cfg(test)]
pub mod tests {
use crate::{
test_framework::mocks::default_txid,
wallet::notes::{sapling::mocks::SaplingNoteBuilder, NoteInterface},
};

#[test]
fn pending_spent_note_is_pending_spent() {
let spend = Some((default_txid(), 641312));
let note = SaplingNoteBuilder::default()
.unconfirmed_spent(spend)
.build();
assert!(!note.is_spent());
assert!(note.is_pending_spent());
assert!(note.is_spent_or_pending_spent());
assert_eq!(note.pending_spent(), &spend);
}
}
25 changes: 5 additions & 20 deletions zingolib/src/wallet/notes/transparent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::io::Write;

use byteorder::{ReadBytesExt, WriteBytesExt};

use zcash_client_backend::PoolType;
use zcash_primitives::transaction::{components::OutPoint, TxId};

use super::NoteInterface;
Expand All @@ -29,6 +30,10 @@ pub struct TransparentNote {
}

impl NoteInterface for TransparentNote {
fn pool_type(&self) -> PoolType {
PoolType::Transparent
}

fn spent(&self) -> &Option<(TxId, u32)> {
&self.spent
}
Expand Down Expand Up @@ -249,23 +254,3 @@ pub mod mocks {
}
}
}

#[cfg(test)]
pub mod tests {
use crate::{
test_framework::mocks::default_txid,
wallet::notes::{transparent::mocks::TransparentNoteBuilder, NoteInterface},
};

#[test]
fn pending_spent_note_is_pending_spent() {
let spend = Some((default_txid(), 112358));
let note = TransparentNoteBuilder::default()
.unconfirmed_spent(spend)
.build();
assert!(!note.is_spent());
assert!(note.is_pending_spent());
assert!(note.is_spent_or_pending_spent());
assert_eq!(note.pending_spent(), &spend);
}
}

0 comments on commit 316f163

Please sign in to comment.