From ee23d3d92acc4704601da49959e1748a4ad1501d Mon Sep 17 00:00:00 2001 From: Arya Date: Sat, 19 Oct 2024 00:25:40 -0400 Subject: [PATCH] create `known_outpoint_hashes` in block verifier to be shared across transaction verifier calls --- zebra-consensus/src/block.rs | 8 +++- zebra-consensus/src/transaction.rs | 21 +++++++---- zebra-consensus/src/transaction/tests.rs | 37 ++++++++++++++++++- zebra-consensus/src/transaction/tests/prop.rs | 6 ++- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/zebra-consensus/src/block.rs b/zebra-consensus/src/block.rs index 2b0013a5a3d..e5facd7e411 100644 --- a/zebra-consensus/src/block.rs +++ b/zebra-consensus/src/block.rs @@ -8,6 +8,7 @@ //! verification, where it may be accepted or rejected. use std::{ + collections::HashSet, future::Future, pin::Pin, sync::Arc, @@ -25,7 +26,7 @@ use zebra_chain::{ amount::Amount, block, parameters::{subsidy::FundingStreamReceiver, Network}, - transparent, + transaction, transparent, work::equihash, }; use zebra_state as zs; @@ -232,6 +233,10 @@ where &block, &transaction_hashes, )); + + let known_outpoint_hashes: Arc> = + Arc::new(known_utxos.keys().map(|outpoint| outpoint.hash).collect()); + for transaction in &block.transactions { let rsp = transaction_verifier .ready() @@ -239,6 +244,7 @@ where .expect("transaction verifier is always ready") .call(tx::Request::Block { transaction: transaction.clone(), + known_outpoint_hashes: known_outpoint_hashes.clone(), known_utxos: known_utxos.clone(), height, time: block.header.time, diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index 0fb20406608..4a4897e67ef 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -148,6 +148,8 @@ pub enum Request { Block { /// The transaction itself. transaction: Arc, + /// Set of transaction hashes that create new transparent outputs. + known_outpoint_hashes: Arc>, /// Additional UTXOs which are known at the time of verification. known_utxos: Arc>, /// The height of the block containing this transaction. @@ -267,6 +269,17 @@ impl Request { } } + /// The set of additional known [`transparent::OutPoint`]s of unspent transaction outputs that's in this request. + pub fn known_outpoint_hashes(&self) -> Arc> { + match self { + Request::Block { + known_outpoint_hashes, + .. + } => known_outpoint_hashes.clone(), + Request::Mempool { .. } => HashSet::new().into(), + } + } + /// The height used to select the consensus rules for verifying this transaction. pub fn height(&self) -> block::Height { match self { @@ -635,14 +648,8 @@ where return None; } - // TODO: Do this transformation in the block verifier and include it in Request::Block instead?. - let known_outpoint_hashes: HashSet = req - .known_utxos() - .keys() - .map(|outpoint| outpoint.hash) - .collect(); - let mut mempool = mempool?; + let known_outpoint_hashes = req.known_outpoint_hashes(); let tx_id = req.transaction().hash(); let mempool::Response::TransactionWithDeps { diff --git a/zebra-consensus/src/transaction/tests.rs b/zebra-consensus/src/transaction/tests.rs index d42bbb8594c..9f3400d1612 100644 --- a/zebra-consensus/src/transaction/tests.rs +++ b/zebra-consensus/src/transaction/tests.rs @@ -2,7 +2,10 @@ // // TODO: split fixed test vectors into a `vectors` module? -use std::{collections::HashMap, sync::Arc}; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; use chrono::{DateTime, TimeZone, Utc}; use color_eyre::eyre::Report; @@ -984,6 +987,7 @@ async fn v5_transaction_is_rejected_before_nu5_activation() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: canopy .activation_height(&network) .expect("Canopy activation height is specified"), @@ -1044,6 +1048,7 @@ fn v5_transaction_is_accepted_after_nu5_activation_for_network(network: Network) .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: expiry_height, time: DateTime::::MAX_UTC, }) @@ -1099,6 +1104,7 @@ async fn v4_transaction_with_transparent_transfer_is_accepted() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: transaction_block_height, time: DateTime::::MAX_UTC, }) @@ -1143,6 +1149,7 @@ async fn v4_transaction_with_last_valid_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(transaction.clone()), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -1188,6 +1195,7 @@ async fn v4_coinbase_transaction_with_low_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(transaction.clone()), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -1235,6 +1243,7 @@ async fn v4_transaction_with_too_low_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(transaction.clone()), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -1285,6 +1294,7 @@ async fn v4_transaction_with_exceeding_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(transaction.clone()), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -1338,6 +1348,7 @@ async fn v4_coinbase_transaction_with_exceeding_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(transaction.clone()), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -1389,6 +1400,7 @@ async fn v4_coinbase_transaction_is_accepted() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: transaction_block_height, time: DateTime::::MAX_UTC, }) @@ -1444,6 +1456,7 @@ async fn v4_transaction_with_transparent_transfer_is_rejected_by_the_script() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: transaction_block_height, time: DateTime::::MAX_UTC, }) @@ -1499,6 +1512,7 @@ async fn v4_transaction_with_conflicting_transparent_spend_is_rejected() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: transaction_block_height, time: DateTime::::MAX_UTC, }) @@ -1570,6 +1584,7 @@ fn v4_transaction_with_conflicting_sprout_nullifier_inside_joinsplit_is_rejected .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: transaction_block_height, time: DateTime::::MAX_UTC, }) @@ -1646,6 +1661,7 @@ fn v4_transaction_with_conflicting_sprout_nullifier_across_joinsplits_is_rejecte .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: transaction_block_height, time: DateTime::::MAX_UTC, }) @@ -1705,6 +1721,7 @@ async fn v5_transaction_with_transparent_transfer_is_accepted() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: transaction_block_height, time: DateTime::::MAX_UTC, }) @@ -1751,6 +1768,7 @@ async fn v5_transaction_with_last_valid_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(transaction.clone()), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -1796,6 +1814,7 @@ async fn v5_coinbase_transaction_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(transaction.clone()), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -1817,6 +1836,7 @@ async fn v5_coinbase_transaction_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(new_transaction.clone()), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -1846,6 +1866,7 @@ async fn v5_coinbase_transaction_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(new_transaction.clone()), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -1877,6 +1898,7 @@ async fn v5_coinbase_transaction_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(new_transaction.clone()), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height: new_expiry_height, time: DateTime::::MAX_UTC, }) @@ -1926,6 +1948,7 @@ async fn v5_transaction_with_too_low_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(transaction.clone()), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -1977,6 +2000,7 @@ async fn v5_transaction_with_exceeding_expiry_height() { .oneshot(Request::Block { transaction: Arc::new(transaction.clone()), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: block_height, time: DateTime::::MAX_UTC, }) @@ -2031,6 +2055,7 @@ async fn v5_coinbase_transaction_is_accepted() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: transaction_block_height, time: DateTime::::MAX_UTC, }) @@ -2088,6 +2113,7 @@ async fn v5_transaction_with_transparent_transfer_is_rejected_by_the_script() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: transaction_block_height, time: DateTime::::MAX_UTC, }) @@ -2145,6 +2171,7 @@ async fn v5_transaction_with_conflicting_transparent_spend_is_rejected() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height: transaction_block_height, time: DateTime::::MAX_UTC, }) @@ -2189,6 +2216,7 @@ fn v4_with_signed_sprout_transfer_is_accepted() { .oneshot(Request::Block { transaction, known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height, time: DateTime::::MAX_UTC, }) @@ -2278,6 +2306,7 @@ async fn v4_with_joinsplit_is_rejected_for_modification( .oneshot(Request::Block { transaction: transaction.clone(), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height, time: DateTime::::MAX_UTC, }) @@ -2324,6 +2353,7 @@ fn v4_with_sapling_spends() { .oneshot(Request::Block { transaction, known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height, time: DateTime::::MAX_UTC, }) @@ -2366,6 +2396,7 @@ fn v4_with_duplicate_sapling_spends() { .oneshot(Request::Block { transaction, known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height, time: DateTime::::MAX_UTC, }) @@ -2410,6 +2441,7 @@ fn v4_with_sapling_outputs_and_no_spends() { .oneshot(Request::Block { transaction, known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height, time: DateTime::::MAX_UTC, }) @@ -2458,6 +2490,7 @@ fn v5_with_sapling_spends() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height, time: DateTime::::MAX_UTC, }) @@ -2501,6 +2534,7 @@ fn v5_with_duplicate_sapling_spends() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height, time: DateTime::::MAX_UTC, }) @@ -2563,6 +2597,7 @@ fn v5_with_duplicate_orchard_action() { .oneshot(Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), height, time: DateTime::::MAX_UTC, }) diff --git a/zebra-consensus/src/transaction/tests/prop.rs b/zebra-consensus/src/transaction/tests/prop.rs index 856742e5d74..b5f179e2e03 100644 --- a/zebra-consensus/src/transaction/tests/prop.rs +++ b/zebra-consensus/src/transaction/tests/prop.rs @@ -1,6 +1,9 @@ //! Randomised property tests for transaction verification. -use std::{collections::HashMap, sync::Arc}; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; use chrono::{DateTime, Duration, Utc}; use proptest::{collection::vec, prelude::*}; @@ -459,6 +462,7 @@ fn validate( .oneshot(transaction::Request::Block { transaction: Arc::new(transaction), known_utxos: Arc::new(known_utxos), + known_outpoint_hashes: Arc::new(HashSet::new()), height, time: block_time, })