Skip to content

Commit

Permalink
implemented mempool scanning
Browse files Browse the repository at this point in the history
  • Loading branch information
Oscar-Pepper committed Dec 4, 2024
1 parent 39a9ef1 commit 9a12326
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 74 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

24 changes: 24 additions & 0 deletions zingo-status/src/confirmation_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ impl ConfirmationStatus {
matches!(self, Self::Confirmed(self_height) if self_height >= comparison_height)
}

/// To return true, the status must be confirmed and no earlier than specified height.
/// # Examples
///
/// ```
/// use zingo_status::confirmation_status::ConfirmationStatus;
/// use zcash_primitives::consensus::BlockHeight;
///
/// assert!(!ConfirmationStatus::Calculated(10.into()).is_confirmed_after_or_at(&9.into()));
/// assert!(!ConfirmationStatus::Calculated(10.into()).is_confirmed_after_or_at(&10.into()));
/// assert!(!ConfirmationStatus::Calculated(10.into()).is_confirmed_after_or_at(&11.into()));
/// assert!(!ConfirmationStatus::Transmitted(10.into()).is_confirmed_after_or_at(&9.into()));
/// assert!(!ConfirmationStatus::Transmitted(10.into()).is_confirmed_after_or_at(&10.into()));
/// assert!(!ConfirmationStatus::Transmitted(10.into()).is_confirmed_after_or_at(&11.into()));
/// assert!(!ConfirmationStatus::Mempool(10.into()).is_confirmed_after_or_at(&9.into()));
/// assert!(!ConfirmationStatus::Mempool(10.into()).is_confirmed_after_or_at(&10.into()));
/// assert!(!ConfirmationStatus::Mempool(10.into()).is_confirmed_after_or_at(&11.into()));
/// assert!(ConfirmationStatus::Confirmed(10.into()).is_confirmed_after_or_at(&9.into()));
/// assert!(!ConfirmationStatus::Confirmed(10.into()).is_confirmed_after_or_at(&10.into()));
/// assert!(!ConfirmationStatus::Confirmed(10.into()).is_confirmed_after_or_at(&11.into()));
/// ```
pub fn is_confirmed_after(&self, comparison_height: &BlockHeight) -> bool {
matches!(self, Self::Confirmed(self_height) if self_height > comparison_height)
}

/// To return true, the status must be confirmed and no later than specified height.
/// # Examples
///
Expand Down
1 change: 1 addition & 0 deletions zingo-sync/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
# Zingo
zingo-netutils = { path = "../zingo-netutils" }
zingo-memo = { path = "../zingo-memo" }
zingo-status = { path = "../zingo-status" }

# Zcash
zcash_client_backend.workspace = true
Expand Down
13 changes: 9 additions & 4 deletions zingo-sync/src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use zcash_primitives::{
memo::Memo,
transaction::{components::amount::NonNegativeAmount, TxId},
};
use zingo_status::confirmation_status::ConfirmationStatus;

use crate::{
keys::{transparent::TransparentAddressId, KeyId},
Expand Down Expand Up @@ -182,10 +183,12 @@ impl WalletBlock {
/// Wallet transaction
#[derive(Getters, CopyGetters)]
pub struct WalletTransaction {
#[getset(get = "pub")]
txid: TxId,
#[getset(get = "pub")]
transaction: zcash_primitives::transaction::Transaction,
#[getset(get_copy = "pub")]
block_height: BlockHeight,
confirmation_status: ConfirmationStatus,
#[getset(skip)]
sapling_notes: Vec<SaplingNote>,
#[getset(skip)]
Expand All @@ -200,17 +203,19 @@ pub struct WalletTransaction {

impl WalletTransaction {
pub fn from_parts(
txid: TxId,
transaction: zcash_primitives::transaction::Transaction,
block_height: BlockHeight,
confirmation_status: ConfirmationStatus,
sapling_notes: Vec<SaplingNote>,
orchard_notes: Vec<OrchardNote>,
outgoing_sapling_notes: Vec<OutgoingSaplingNote>,
outgoing_orchard_notes: Vec<OutgoingOrchardNote>,
transparent_coins: Vec<TransparentCoin>,
) -> Self {
Self {
txid,
transaction,
block_height,
confirmation_status,
sapling_notes,
orchard_notes,
outgoing_sapling_notes,
Expand Down Expand Up @@ -255,7 +260,7 @@ impl WalletTransaction {
impl std::fmt::Debug for WalletTransaction {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("WalletTransaction")
.field("block_height", &self.block_height)
.field("confirmation_status", &self.confirmation_status)
.field("sapling_notes", &self.sapling_notes)
.field("orchard_notes", &self.orchard_notes)
.field("outgoing_sapling_notes", &self.outgoing_sapling_notes)
Expand Down
17 changes: 10 additions & 7 deletions zingo-sync/src/scan/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use zcash_primitives::{
zip32::AccountId,
};
use zingo_memo::ParsedMemo;
use zingo_status::confirmation_status::ConfirmationStatus;

use crate::{
client::{self, FetchRequest},
Expand Down Expand Up @@ -96,16 +97,16 @@ pub(crate) async fn scan_transactions<P: consensus::Parameters>(
panic!("wallet block at transaction height not found!");
}

let confirmation_status = ConfirmationStatus::Confirmed(block_height);
let wallet_transaction = scan_transaction(
consensus_parameters,
ufvks,
transaction,
block_height,
confirmation_status,
&decrypted_note_data,
&mut NullifierMap::new(),
outpoint_map,
&transparent_addresses,
false,
)
.unwrap();
wallet_transactions.insert(txid, wallet_transaction);
Expand All @@ -118,15 +119,15 @@ pub(crate) fn scan_transaction<P: consensus::Parameters>(
consensus_parameters: &P,
ufvks: &HashMap<AccountId, UnifiedFullViewingKey>,
transaction: Transaction,
block_height: BlockHeight,
confirmation_status: ConfirmationStatus,
decrypted_note_data: &DecryptedNoteData,
nullifier_map: &mut NullifierMap,
outpoint_map: &mut OutPointMap,
transparent_addresses: &HashMap<String, TransparentAddressId>,
pending: bool, // TODO: change for confirmation status
) -> Result<WalletTransaction, ()> {
// TODO: condsider splitting into seperate fns for pending and confirmed etc.
// TODO: price? save in wallet block as its relative to time mined?
let block_height = confirmation_status.get_height();
let zip212_enforcement = zcash_primitives::transaction::components::sapling::zip212_enforcement(
consensus_parameters,
block_height,
Expand Down Expand Up @@ -245,8 +246,9 @@ pub(crate) fn scan_transaction<P: consensus::Parameters>(
encoded_memos.append(&mut parse_encoded_memos(&orchard_notes).unwrap());
}

// for confirmed transactions nullifiers are collected during compact block scanning
if pending {
// collect nullifiers for pending transactions
// nullifiers for confirmed transactions are collected during compact block scanning
if !confirmation_status.is_confirmed() {
collect_nullifiers(nullifier_map, block_height, &transaction);
}

Expand Down Expand Up @@ -290,8 +292,9 @@ pub(crate) fn scan_transaction<P: consensus::Parameters>(
// TODO: consider adding nullifiers and transparent outpoint data for efficiency

Ok(WalletTransaction::from_parts(
transaction.txid(),
transaction,
block_height,
confirmation_status,
sapling_notes,
orchard_notes,
outgoing_sapling_notes,
Expand Down
134 changes: 73 additions & 61 deletions zingo-sync/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use zcash_primitives::consensus::{self, BlockHeight};

use tokio::sync::mpsc;
use zcash_primitives::zip32::AccountId;
use zingo_status::confirmation_status::ConfirmationStatus;

pub(crate) mod state;
pub(crate) mod transparent;
Expand Down Expand Up @@ -226,6 +227,77 @@ where
Ok(())
}

async fn process_mempool_stream_response<W>(
consensus_parameters: &impl consensus::Parameters,
fetch_request_sender: mpsc::UnboundedSender<FetchRequest>,
ufvks: &HashMap<AccountId, UnifiedFullViewingKey>,
wallet: &mut W,
mempool_stream_response: Result<Option<RawTransaction>, tonic::Status>,
mempool_stream: &mut tonic::Streaming<RawTransaction>,
) where
W: SyncWallet + SyncTransactions + SyncNullifiers + SyncOutPoints,
{
match mempool_stream_response.unwrap() {
Some(raw_transaction) => {
let block_height =
BlockHeight::from_u32(u32::try_from(raw_transaction.height).unwrap());
let transaction = zcash_primitives::transaction::Transaction::read(
&raw_transaction.data[..],
consensus::BranchId::for_height(consensus_parameters, block_height),
)
.unwrap();

tracing::info!(
"mempool received txid {} at height {}",
transaction.txid(),
block_height
);

let confirmation_status = ConfirmationStatus::Mempool(block_height);
let mut nullifiers = NullifierMap::new();
let mut outpoints = OutPointMap::new();
let transparent_addresses: HashMap<String, TransparentAddressId> = wallet
.get_transparent_addresses()
.unwrap()
.iter()
.map(|(id, address)| (address.clone(), *id))
.collect();
let wallet_transaction = scan_transaction(
consensus_parameters,
ufvks,
transaction,
confirmation_status,
&DecryptedNoteData::new(),
&mut nullifiers,
&mut outpoints,
&transparent_addresses,
)
.unwrap();
wallet.append_nullifiers(nullifiers).unwrap();
wallet.append_outpoints(outpoints).unwrap();
// TODO: add wallet transactions to wallet
if let Some(tx) = wallet
.get_wallet_transactions()
.unwrap()
.get(wallet_transaction.txid())
{
if tx.confirmation_status().is_confirmed() {
return;
}
}
wallet
.insert_wallet_transaction(wallet_transaction)
.unwrap();
}
None => {
// A block was mined, setup a new mempool stream until the next block is mined.
*mempool_stream = client::get_mempool_transaction_stream(fetch_request_sender.clone())
.await
.unwrap();
}
}
}

/// Removes all wallet data above the given `truncate_height`.
fn truncate_wallet_data<W>(wallet: &mut W, truncate_height: BlockHeight) -> Result<(), ()>
where
Expand Down Expand Up @@ -420,7 +492,7 @@ where
.get_wallet_transactions()
.unwrap()
.values()
.map(|tx| tx.block_height())
.filter_map(|tx| tx.confirmation_status().get_confirmed_height())
.collect::<Vec<_>>();
wallet.get_wallet_blocks_mut().unwrap().retain(|height, _| {
*height >= scan_range.block_range().end - 1
Expand All @@ -440,63 +512,3 @@ where

Ok(())
}

async fn process_mempool_stream_response<W>(
consensus_parameters: &impl consensus::Parameters,
fetch_request_sender: mpsc::UnboundedSender<FetchRequest>,
ufvks: &HashMap<AccountId, UnifiedFullViewingKey>,
wallet: &mut W,
mempool_stream_response: Result<Option<RawTransaction>, tonic::Status>,
mempool_stream: &mut tonic::Streaming<RawTransaction>,
) where
W: SyncWallet + SyncNullifiers + SyncOutPoints,
{
match mempool_stream_response.unwrap() {
Some(raw_transaction) => {
let block_height =
BlockHeight::from_u32(u32::try_from(raw_transaction.height).unwrap());
let transaction = zcash_primitives::transaction::Transaction::read(
&raw_transaction.data[..],
consensus::BranchId::for_height(consensus_parameters, block_height),
)
.unwrap();

tracing::info!(
"mempool received txid {} at height {}",
transaction.txid(),
block_height
);

// TODO: create confirmation status
let mut nullifiers = NullifierMap::new();
let mut outpoints = OutPointMap::new();
let transparent_addresses: HashMap<String, TransparentAddressId> = wallet
.get_transparent_addresses()
.unwrap()
.iter()
.map(|(id, address)| (address.clone(), *id))
.collect();
let wallet_transaction = scan_transaction(
consensus_parameters,
ufvks,
transaction,
block_height,
&DecryptedNoteData::new(),
&mut nullifiers,
&mut outpoints,
&transparent_addresses,
true,
)
.unwrap();
wallet.append_nullifiers(nullifiers).unwrap();
wallet.append_outpoints(outpoints).unwrap();
// TODO: add wallet transactions to wallet
}
None => {
// A block was mined, setup a new mempool stream until the next block is mined.
*mempool_stream = client::get_mempool_transaction_stream(fetch_request_sender.clone())
.await
.unwrap();
}
}
}
18 changes: 16 additions & 2 deletions zingo-sync/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ pub trait SyncTransactions: SyncWallet {
&mut self,
) -> Result<&mut HashMap<TxId, WalletTransaction>, Self::Error>;

/// Insert wallet transaction
fn insert_wallet_transaction(
&mut self,
wallet_transaction: WalletTransaction,
) -> Result<(), Self::Error> {
self.get_wallet_transactions_mut()?
.insert(*wallet_transaction.txid(), wallet_transaction);

Ok(())
}

/// Extend wallet transaction map with new wallet transactions
fn extend_wallet_transactions(
&mut self,
Expand All @@ -95,7 +106,7 @@ pub trait SyncTransactions: SyncWallet {
Ok(())
}

/// Removes all wallet transactions above the given `block_height`.
/// Removes all confirmed wallet transactions above the given `block_height`.
/// Also sets any output's spending_transaction field to `None` if it's spending transaction was removed.
fn truncate_wallet_transactions(
&mut self,
Expand All @@ -105,7 +116,10 @@ pub trait SyncTransactions: SyncWallet {
let invalid_txids: Vec<TxId> = self
.get_wallet_transactions()?
.values()
.filter(|tx| tx.block_height() > truncate_height)
.filter(|tx| {
tx.confirmation_status()
.is_confirmed_after(&truncate_height)
})
.map(|tx| tx.transaction().txid())
.collect();

Expand Down

0 comments on commit 9a12326

Please sign in to comment.