Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make chain tip updates and access more efficient #2695

Merged
merged 2 commits into from
Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions zebra-chain/src/chain_tip.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Chain tip interfaces.

use std::sync::Arc;

use crate::{block, transaction};

/// An interface for querying the chain tip.
Expand All @@ -18,7 +20,7 @@ pub trait ChainTip {
///
/// All transactions with these mined IDs should be rejected from the mempool,
/// even if their authorizing data is different.
fn best_tip_mined_transaction_ids(&self) -> Vec<transaction::Hash>;
fn best_tip_mined_transaction_ids(&self) -> Arc<[transaction::Hash]>;
}

/// A chain tip that is always empty.
Expand All @@ -34,7 +36,7 @@ impl ChainTip for NoChainTip {
None
}

fn best_tip_mined_transaction_ids(&self) -> Vec<transaction::Hash> {
Vec::new()
fn best_tip_mined_transaction_ids(&self) -> Arc<[transaction::Hash]> {
Arc::new([])
}
}
7 changes: 2 additions & 5 deletions zebra-consensus/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,8 @@ where
// the header binds to the transactions in the blocks.

// Precomputing this avoids duplicating transaction hash computations.
let transaction_hashes = block
.transactions
.iter()
.map(|t| t.hash())
.collect::<Vec<_>>();
let transaction_hashes: Arc<[_]> =
block.transactions.iter().map(|t| t.hash()).collect();

check::merkle_root_validity(network, &block, &transaction_hashes)?;

Expand Down
4 changes: 2 additions & 2 deletions zebra-state/src/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ impl Prepare for Arc<Block> {
let block = self;
let hash = block.hash();
let height = block.coinbase_height().unwrap();
let transaction_hashes: Vec<_> = block.transactions.iter().map(|tx| tx.hash()).collect();
let new_outputs = transparent::new_ordered_outputs(&block, transaction_hashes.as_slice());
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);

PreparedBlock {
block,
Expand Down
14 changes: 5 additions & 9 deletions zebra-state/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub struct PreparedBlock {
/// earlier transaction.
pub new_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
/// A precomputed list of the hashes of the transactions in this block.
pub transaction_hashes: Vec<transaction::Hash>,
pub transaction_hashes: Arc<[transaction::Hash]>,
}

/// A contextually validated block, ready to be committed directly to the finalized state with
Expand All @@ -93,7 +93,7 @@ pub struct ContextuallyValidBlock {
pub(crate) hash: block::Hash,
pub(crate) height: block::Height,
pub(crate) new_outputs: HashMap<transparent::OutPoint, transparent::Utxo>,
pub(crate) transaction_hashes: Vec<transaction::Hash>,
pub(crate) transaction_hashes: Arc<[transaction::Hash]>,
/// The sum of the chain value pool changes of all transactions in this block.
pub(crate) chain_value_pool_change: ValueBalance<NegativeAllowed>,
}
Expand All @@ -110,7 +110,7 @@ pub struct FinalizedBlock {
pub(crate) hash: block::Hash,
pub(crate) height: block::Height,
pub(crate) new_outputs: HashMap<transparent::OutPoint, transparent::Utxo>,
pub(crate) transaction_hashes: Vec<transaction::Hash>,
pub(crate) transaction_hashes: Arc<[transaction::Hash]>,
}

impl From<&PreparedBlock> for PreparedBlock {
Expand Down Expand Up @@ -165,12 +165,8 @@ impl From<Arc<Block>> for FinalizedBlock {
.coinbase_height()
.expect("finalized blocks must have a valid coinbase height");
let hash = block.hash();
let transaction_hashes = block
.transactions
.iter()
.map(|tx| tx.hash())
.collect::<Vec<_>>();
let new_outputs = transparent::new_outputs(&block, transaction_hashes.as_slice());
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
let new_outputs = transparent::new_outputs(&block, &transaction_hashes);

Self {
block,
Expand Down
19 changes: 6 additions & 13 deletions zebra-state/src/service/chain_tip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ use std::sync::Arc;

use tokio::sync::watch;

use zebra_chain::{
block::{self, Block},
chain_tip::ChainTip,
transaction,
};
use zebra_chain::{block, chain_tip::ChainTip, transaction};

use crate::{request::ContextuallyValidBlock, FinalizedBlock};

Expand All @@ -21,27 +17,25 @@ type ChainTipData = Option<ChainTipBlock>;
/// Used to efficiently update the [`ChainTipSender`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChainTipBlock {
pub(crate) block: Arc<Block>,
pub(crate) hash: block::Hash,
pub(crate) height: block::Height,

/// The mined transaction IDs of the transactions in `block`,
/// in the same order as `block.transactions`.
pub(crate) transaction_hashes: Vec<transaction::Hash>,
pub(crate) transaction_hashes: Arc<[transaction::Hash]>,
}

impl From<ContextuallyValidBlock> for ChainTipBlock {
fn from(contextually_valid: ContextuallyValidBlock) -> Self {
let ContextuallyValidBlock {
block,
block: _,
hash,
height,
new_outputs: _,
transaction_hashes,
chain_value_pool_change: _,
} = contextually_valid;
Self {
block,
hash,
height,
transaction_hashes,
Expand All @@ -52,14 +46,13 @@ impl From<ContextuallyValidBlock> for ChainTipBlock {
impl From<FinalizedBlock> for ChainTipBlock {
fn from(finalized: FinalizedBlock) -> Self {
let FinalizedBlock {
block,
block: _,
hash,
height,
new_outputs: _,
transaction_hashes,
} = finalized;
Self {
block,
hash,
height,
transaction_hashes,
Expand Down Expand Up @@ -180,11 +173,11 @@ impl ChainTip for ChainTipReceiver {
///
/// All transactions with these mined IDs should be rejected from the mempool,
/// even if their authorizing data is different.
fn best_tip_mined_transaction_ids(&self) -> Vec<transaction::Hash> {
fn best_tip_mined_transaction_ids(&self) -> Arc<[transaction::Hash]> {
self.receiver
.borrow()
.as_ref()
.map(|block| block.transaction_hashes.clone())
.unwrap_or_default()
.unwrap_or_else(|| Arc::new([]))
}
}
43 changes: 31 additions & 12 deletions zebra-state/src/service/chain_tip/tests/prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@ proptest! {
for update in tip_updates {
match update {
BlockUpdate::Finalized(block) => {
let block = block.map(FinalizedBlock::from).map(ChainTipBlock::from);
chain_tip_sender.set_finalized_tip(block.clone());
if block.is_some() {
latest_finalized_tip = block;
let chain_tip = block.clone().map(FinalizedBlock::from).map(ChainTipBlock::from);
chain_tip_sender.set_finalized_tip(chain_tip.clone());
if let Some(block) = block {
latest_finalized_tip = Some((chain_tip, block));
}
}
BlockUpdate::NonFinalized(block) => {
let block = block.map(FinalizedBlock::from).map(ChainTipBlock::from);
chain_tip_sender.set_best_non_finalized_tip(block.clone());
if block.is_some() {
latest_non_finalized_tip = block;
let chain_tip = block.clone().map(FinalizedBlock::from).map(ChainTipBlock::from);
chain_tip_sender.set_best_non_finalized_tip(chain_tip.clone());
if let Some(block) = block {
latest_non_finalized_tip = Some((chain_tip, block));
seen_non_finalized_tip = true;
}
}
Expand All @@ -57,18 +57,37 @@ proptest! {
latest_finalized_tip
};

let expected_height = expected_tip.as_ref().and_then(|block| block.block.coinbase_height());
let chain_tip_height = expected_tip
.as_ref()
.and_then(|(chain_tip, _block)| chain_tip.as_ref())
.map(|chain_tip| chain_tip.height);
let expected_height = expected_tip.as_ref().and_then(|(_chain_tip, block)| block.coinbase_height());
prop_assert_eq!(chain_tip_receiver.best_tip_height(), chain_tip_height);
prop_assert_eq!(chain_tip_receiver.best_tip_height(), expected_height);

let expected_hash = expected_tip.as_ref().map(|block| block.block.hash());
let chain_tip_hash = expected_tip
.as_ref()
.and_then(|(chain_tip, _block)| chain_tip.as_ref())
.map(|chain_tip| chain_tip.hash);
let expected_hash = expected_tip.as_ref().map(|(_chain_tip, block)| block.hash());
prop_assert_eq!(chain_tip_receiver.best_tip_hash(), chain_tip_hash);
prop_assert_eq!(chain_tip_receiver.best_tip_hash(), expected_hash);

let expected_transaction_ids: Vec<_> = expected_tip
let chain_tip_transaction_ids = expected_tip
.as_ref()
.and_then(|(chain_tip, _block)| chain_tip.as_ref())
.map(|chain_tip| chain_tip.transaction_hashes.clone())
.unwrap_or_else(|| Arc::new([]));
let expected_transaction_ids = expected_tip
.as_ref()
.iter()
.flat_map(|block| block.block.transactions.clone())
.flat_map(|(_chain_tip, block)| block.transactions.clone())
.map(|transaction| transaction.hash())
.collect();
prop_assert_eq!(
chain_tip_receiver.best_tip_mined_transaction_ids(),
chain_tip_transaction_ids
);
prop_assert_eq!(
chain_tip_receiver.best_tip_mined_transaction_ids(),
expected_transaction_ids
Expand Down
6 changes: 4 additions & 2 deletions zebra-state/src/service/chain_tip/tests/vectors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::iter;

use zebra_chain::chain_tip::{ChainTip, NoChainTip};

use super::super::ChainTipSender;
Expand All @@ -10,7 +12,7 @@ fn best_tip_is_initially_empty() {
assert_eq!(chain_tip_receiver.best_tip_hash(), None);
assert_eq!(
chain_tip_receiver.best_tip_mined_transaction_ids(),
Vec::new()
iter::empty().collect()
);
}

Expand All @@ -22,6 +24,6 @@ fn empty_chain_tip_is_empty() {
assert_eq!(chain_tip_receiver.best_tip_hash(), None);
assert_eq!(
chain_tip_receiver.best_tip_mined_transaction_ids(),
Vec::new()
iter::empty().collect()
);
}
2 changes: 1 addition & 1 deletion zebra-state/src/service/finalized_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ impl FinalizedState {
for (transaction_index, (transaction, transaction_hash)) in block
.transactions
.iter()
.zip(transaction_hashes.into_iter())
.zip(transaction_hashes.iter())
.enumerate()
{
let transaction_location = TransactionLocation {
Expand Down