Skip to content

Commit

Permalink
adds transactions to ChainTipBlock and rejects transactions from the …
Browse files Browse the repository at this point in the history
…mempool that have been invalidated by the latest block commit
  • Loading branch information
arya2 committed Oct 20, 2022
1 parent 267db7f commit f158500
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 2 deletions.
2 changes: 2 additions & 0 deletions zebra-state/src/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ impl From<PreparedBlock> for ChainTipBlock {
new_outputs: _,
transaction_hashes,
} = prepared;

Self {
hash,
height,
time: block.header.time,
transactions: block.transactions.clone(),
transaction_hashes,
previous_block_hash: block.header.previous_block_hash,
}
Expand Down
8 changes: 7 additions & 1 deletion zebra-state/src/service/chain_tip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use zebra_chain::{
block,
chain_tip::ChainTip,
parameters::{Network, NetworkUpgrade},
transaction,
transaction::{self, Transaction},
};

use crate::{
Expand Down Expand Up @@ -56,6 +56,9 @@ pub struct ChainTipBlock {
)]
pub time: DateTime<Utc>,

/// The block transactions.
pub transactions: Vec<Arc<Transaction>>,

/// The mined transaction IDs of the transactions in `block`,
/// in the same order as `block.transactions`.
pub transaction_hashes: Arc<[transaction::Hash]>,
Expand Down Expand Up @@ -84,6 +87,7 @@ impl From<ContextuallyValidBlock> for ChainTipBlock {
hash,
height,
time: block.header.time,
transactions: block.transactions.clone(),
transaction_hashes,
previous_block_hash: block.header.previous_block_hash,
}
Expand All @@ -99,10 +103,12 @@ impl From<FinalizedBlock> for ChainTipBlock {
transaction_hashes,
..
} = finalized;

Self {
hash,
height,
time: block.header.time,
transactions: block.transactions.clone(),
transaction_hashes,
previous_block_hash: block.header.previous_block_hash,
}
Expand Down
28 changes: 28 additions & 0 deletions zebrad/src/components/mempool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,39 @@ impl Service<Request> for Mempool {
if let Some(TipAction::Grow { block }) = tip_action {
tracing::trace!(block_height = ?block.height, "handling blocks added to tip");

let spent_outpoints = block
.transactions
.iter()
.flat_map(|tx| tx.spent_outpoints())
.collect();

let sprout_nullifiers = block
.transactions
.iter()
.flat_map(|transaction| transaction.sprout_nullifiers())
.collect();
let sapling_nullifiers = block
.transactions
.iter()
.flat_map(|transaction| transaction.sapling_nullifiers())
.collect();
let orchard_nullifiers = block
.transactions
.iter()
.flat_map(|transaction| transaction.orchard_nullifiers())
.collect();

// Cancel downloads/verifications/storage of transactions
// with the same mined IDs as recently mined transactions.
let mined_ids = block.transaction_hashes.iter().cloned().collect();
tx_downloads.cancel(&mined_ids);
storage.remove_same_effects(&mined_ids);
storage.reject_invalidated_transactions(
spent_outpoints,
sprout_nullifiers,
sapling_nullifiers,
orchard_nullifiers,
);
storage.clear_tip_rejections();
}

Expand Down
59 changes: 58 additions & 1 deletion zebrad/src/components/mempool/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ use std::{

use thiserror::Error;

use zebra_chain::transaction::{self, Hash, UnminedTx, UnminedTxId, VerifiedUnminedTx};
use zebra_chain::{
orchard, sapling, sprout,
transaction::{self, Hash, UnminedTx, UnminedTxId, VerifiedUnminedTx},
transparent::OutPoint,
};

use self::{eviction_list::EvictionList, verified_set::VerifiedSet};
use super::{config, downloads::TransactionDownloadVerifyError, MempoolError};
Expand Down Expand Up @@ -78,6 +82,9 @@ pub enum SameEffectsChainRejectionError {
#[error("best chain tip has reached transaction expiry height")]
Expired,

#[error("transaction outpoints or nullifiers were committed to the best chain")]
Mined,

/// Otherwise valid transaction removed from mempool due to [ZIP-401] random
/// eviction.
///
Expand Down Expand Up @@ -294,6 +301,56 @@ impl Storage {
.remove_all_that(|tx| mined_ids.contains(&tx.transaction.id.mined_id()))
}

/// Removes and rejects transactions from the mempool that contain any outpoints or nullifiers in
/// the `spent_outpoints` or `nullifiers` collections that are passed in.
///
/// Returns the number of transactions that were removed and rejected.
pub fn reject_invalidated_transactions(
&mut self,
spent_outpoints: HashSet<OutPoint>,
sprout_nullifiers: HashSet<&sprout::Nullifier>,
sapling_nullifiers: HashSet<&sapling::Nullifier>,
orchard_nullifiers: HashSet<&orchard::Nullifier>,
) -> usize {
let mined_ids: HashSet<_> = self
.verified
.transactions()
.filter_map(|tx| {
if tx
.transaction
.spent_outpoints()
.any(|outpoint| spent_outpoints.contains(&outpoint))
|| tx
.transaction
.sprout_nullifiers()
.any(|nullifier| sprout_nullifiers.contains(nullifier))
|| tx
.transaction
.sapling_nullifiers()
.any(|nullifier| sapling_nullifiers.contains(nullifier))
|| tx
.transaction
.orchard_nullifiers()
.any(|nullifier| orchard_nullifiers.contains(nullifier))
{
Some(tx.id)
} else {
None
}
})
.collect();

let num_removals = self
.verified
.remove_all_that(|tx| mined_ids.contains(&tx.transaction.id));

for mined_id in mined_ids {
self.reject(mined_id, SameEffectsChainRejectionError::Mined.into());
}

num_removals
}

/// Clears the whole mempool storage.
#[allow(dead_code)]
pub fn clear(&mut self) {
Expand Down
1 change: 1 addition & 0 deletions zebrad/src/components/mempool/tests/prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ impl FakeChainTip {
hash: chain_tip_block.hash,
height,
time: previous.time + mock_block_time_delta,
transactions: chain_tip_block.transactions.clone(),
transaction_hashes: chain_tip_block.transaction_hashes.clone(),
previous_block_hash: previous.hash,
}
Expand Down

0 comments on commit f158500

Please sign in to comment.