docs(state): Use different terms for block verification and state queues (#7061)

* claridy some checkpoint verifier docs

* update documentation of `Request::CommitSemanticallyVerifiedBlock` and `Request::CommitCheckpointVerifiedBlock`

* replace `prepared` with `semantically_verified` in state service checks code

* replace `non-finalized` where needed in docs of the state service

* fix double space in doc

* replace `finalized` where needed in docs of the state service

* change some docs in state queued_blocks.rs

* Rewrite pending UTXO checkpoint block comment

* Fix trailing space in docs

* Apply suggestions from code review

Co-authored-by: teor <teor@riseup.net>

---------

Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
Alfredo Garcia 2023-07-04 18:29:41 -03:00 committed by GitHub
parent 9b32ab7878
commit 5859fac5b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 103 additions and 91 deletions

View File

@ -315,7 +315,7 @@ pub fn merkle_root_validity(
// //
// Duplicate transactions should cause a block to be // Duplicate transactions should cause a block to be
// rejected, as duplicate transactions imply that the block contains a // rejected, as duplicate transactions imply that the block contains a
// double-spend. As a defense-in-depth, however, we also check that there // double-spend. As a defense-in-depth, however, we also check that there
// are no duplicate transaction hashes. // are no duplicate transaction hashes.
// //
// ## Checkpoint Validation // ## Checkpoint Validation

View File

@ -4,11 +4,11 @@
//! speed up the initial chain sync for Zebra. This list is distributed //! speed up the initial chain sync for Zebra. This list is distributed
//! with Zebra. //! with Zebra.
//! //!
//! The checkpoint verifier queues pending blocks. Once there is a //! The checkpoint verifier queues pending blocks. Once there is a
//! chain from the previous checkpoint to a target checkpoint, it //! chain from the previous checkpoint to a target checkpoint, it
//! verifies all the blocks in that chain, and sends accepted blocks to //! verifies all the blocks in that chain, and sends accepted blocks to
//! the state service as finalized chain state, skipping contextual //! the state service as finalized chain state, skipping the majority of
//! verification checks. //! contextual verification checks.
//! //!
//! Verification starts at the first checkpoint, which is the genesis //! Verification starts at the first checkpoint, which is the genesis
//! block for the configured network. //! block for the configured network.

View File

@ -163,7 +163,7 @@ pub struct SemanticallyVerifiedBlock {
} }
/// A block ready to be committed directly to the finalized state with /// A block ready to be committed directly to the finalized state with
/// no checks. /// a small number of checks if compared with a `ContextuallyVerifiedBlock`.
/// ///
/// This is exposed for use in checkpointing. /// This is exposed for use in checkpointing.
/// ///
@ -455,12 +455,11 @@ impl DerefMut for CheckpointVerifiedBlock {
/// A query about or modification to the chain state, via the /// A query about or modification to the chain state, via the
/// [`StateService`](crate::service::StateService). /// [`StateService`](crate::service::StateService).
pub enum Request { pub enum Request {
/// Performs contextual validation of the given block, committing it to the /// Performs contextual validation of the given semantically verified block,
/// state if successful. /// committing it to the state if successful.
/// ///
/// It is the caller's responsibility to perform semantic validation. This /// This request can be made out-of-order; the state service will queue it
/// request can be made out-of-order; the state service will queue it until /// until its parent is ready.
/// its parent is ready.
/// ///
/// Returns [`Response::Committed`] with the hash of the block when it is /// Returns [`Response::Committed`] with the hash of the block when it is
/// committed to the state, or an error if the block fails contextual /// committed to the state, or an error if the block fails contextual
@ -478,12 +477,12 @@ pub enum Request {
/// documentation for details. /// documentation for details.
CommitSemanticallyVerifiedBlock(SemanticallyVerifiedBlock), CommitSemanticallyVerifiedBlock(SemanticallyVerifiedBlock),
/// Commit a checkpointed block to the state, skipping most block validation. /// Commit a checkpointed block to the state, skipping most but not all
/// contextual validation.
/// ///
/// This is exposed for use in checkpointing, which produces finalized /// This is exposed for use in checkpointing, which produces checkpoint vefified
/// blocks. It is the caller's responsibility to ensure that the block is /// blocks. This request can be made out-of-order; the state service will queue
/// semantically valid and final. This request can be made out-of-order; /// it until its parent is ready.
/// the state service will queue it until its parent is ready.
/// ///
/// Returns [`Response::Committed`] with the hash of the newly committed /// Returns [`Response::Committed`] with the hash of the newly committed
/// block, or an error. /// block, or an error.
@ -495,8 +494,9 @@ pub enum Request {
/// ///
/// # Note /// # Note
/// ///
/// Finalized and non-finalized blocks are an internal Zebra implementation detail. /// [`SemanticallyVerifiedBlock`], [`ContextuallyVerifiedBlock`] and
/// There is no difference between these blocks on the network, or in Zebra's /// [`CheckpointVerifiedBlock`] are an internal Zebra implementation detail.
/// There is no difference between these blocks on the Zcash network, or in Zebra's
/// network or syncer implementations. /// network or syncer implementations.
/// ///
/// # Consensus /// # Consensus

View File

@ -141,7 +141,7 @@ pub(crate) struct StateService {
/// so they can be written to the [`FinalizedState`]. /// so they can be written to the [`FinalizedState`].
/// ///
/// This sender is dropped after the state has finished sending all the checkpointed blocks, /// This sender is dropped after the state has finished sending all the checkpointed blocks,
/// and the lowest non-finalized block arrives. /// and the lowest semantically verified block arrives.
finalized_block_write_sender: finalized_block_write_sender:
Option<tokio::sync::mpsc::UnboundedSender<QueuedCheckpointVerified>>, Option<tokio::sync::mpsc::UnboundedSender<QueuedCheckpointVerified>>,
@ -154,11 +154,6 @@ pub(crate) struct StateService {
/// ///
/// If `invalid_block_write_reset_receiver` gets a reset, this is: /// If `invalid_block_write_reset_receiver` gets a reset, this is:
/// - the hash of the last valid committed block (the parent of the invalid block). /// - the hash of the last valid committed block (the parent of the invalid block).
//
// TODO:
// - turn this into an IndexMap containing recent non-finalized block hashes and heights
// (they are all potential tips)
// - remove block hashes once their heights are strictly less than the finalized tip
finalized_block_write_last_sent_hash: block::Hash, finalized_block_write_last_sent_hash: block::Hash,
/// A set of block hashes that have been sent to the block write task. /// A set of block hashes that have been sent to the block write task.
@ -455,7 +450,7 @@ impl StateService {
(state, read_service, latest_chain_tip, chain_tip_change) (state, read_service, latest_chain_tip, chain_tip_change)
} }
/// Queue a finalized block for verification and storage in the finalized state. /// Queue a checkpoint verified block for verification and storage in the finalized state.
/// ///
/// Returns a channel receiver that provides the result of the block commit. /// Returns a channel receiver that provides the result of the block commit.
fn queue_and_commit_to_finalized_state( fn queue_and_commit_to_finalized_state(
@ -471,7 +466,7 @@ impl StateService {
let queued_height = checkpoint_verified.height; let queued_height = checkpoint_verified.height;
// If we're close to the final checkpoint, make the block's UTXOs available for // If we're close to the final checkpoint, make the block's UTXOs available for
// full verification of non-finalized blocks, even when it is in the channel. // semantic block verification, even when it is in the channel.
if self.is_close_to_final_checkpoint(queued_height) { if self.is_close_to_final_checkpoint(queued_height) {
self.non_finalized_block_write_sent_hashes self.non_finalized_block_write_sent_hashes
.add_finalized(&checkpoint_verified) .add_finalized(&checkpoint_verified)
@ -481,32 +476,32 @@ impl StateService {
let queued = (checkpoint_verified, rsp_tx); let queued = (checkpoint_verified, rsp_tx);
if self.finalized_block_write_sender.is_some() { if self.finalized_block_write_sender.is_some() {
// We're still committing finalized blocks // We're still committing checkpoint verified blocks
if let Some(duplicate_queued) = self if let Some(duplicate_queued) = self
.finalized_state_queued_blocks .finalized_state_queued_blocks
.insert(queued_prev_hash, queued) .insert(queued_prev_hash, queued)
{ {
Self::send_checkpoint_verified_block_error( Self::send_checkpoint_verified_block_error(
duplicate_queued, duplicate_queued,
"dropping older finalized block: got newer duplicate block", "dropping older checkpoint verified block: got newer duplicate block",
); );
} }
self.drain_finalized_queue_and_commit(); self.drain_finalized_queue_and_commit();
} else { } else {
// We've finished committing finalized blocks, so drop any repeated queued blocks, // We've finished committing checkpoint verified blocks to the finalized state,
// and return an error. // so drop any repeated queued blocks, and return an error.
// //
// TODO: track the latest sent height, and drop any blocks under that height // TODO: track the latest sent height, and drop any blocks under that height
// every time we send some blocks (like QueuedSemanticallyVerifiedBlocks) // every time we send some blocks (like QueuedSemanticallyVerifiedBlocks)
Self::send_checkpoint_verified_block_error( Self::send_checkpoint_verified_block_error(
queued, queued,
"already finished committing finalized blocks: dropped duplicate block, \ "already finished committing checkpoint verified blocks: dropped duplicate block, \
block is already committed to the state", block is already committed to the state",
); );
self.clear_finalized_block_queue( self.clear_finalized_block_queue(
"already finished committing finalized blocks: dropped duplicate block, \ "already finished committing checkpoint verified blocks: dropped duplicate block, \
block is already committed to the state", block is already committed to the state",
); );
} }
@ -636,7 +631,7 @@ impl StateService {
std::mem::drop(finalized); std::mem::drop(finalized);
} }
/// Queue a non finalized block for verification and check if any queued /// Queue a semantically verified block for contextual verification and check if any queued
/// blocks are ready to be verified and committed to the state. /// blocks are ready to be verified and committed to the state.
/// ///
/// This function encodes the logic for [committing non-finalized blocks][1] /// This function encodes the logic for [committing non-finalized blocks][1]
@ -694,8 +689,8 @@ impl StateService {
rsp_rx rsp_rx
}; };
// We've finished sending finalized blocks when: // We've finished sending checkpoint verified blocks when:
// - we've sent the finalized block for the last checkpoint, and // - we've sent the verified block for the last checkpoint, and
// - it has been successfully written to disk. // - it has been successfully written to disk.
// //
// We detect the last checkpoint by looking for non-finalized blocks // We detect the last checkpoint by looking for non-finalized blocks
@ -709,13 +704,13 @@ impl StateService {
&& self.read_service.db.finalized_tip_hash() && self.read_service.db.finalized_tip_hash()
== self.finalized_block_write_last_sent_hash == self.finalized_block_write_last_sent_hash
{ {
// Tell the block write task to stop committing finalized blocks, // Tell the block write task to stop committing checkpoint verified blocks to the finalized state,
// and move on to committing non-finalized blocks. // and move on to committing semantically verified blocks to the non-finalized state.
std::mem::drop(self.finalized_block_write_sender.take()); std::mem::drop(self.finalized_block_write_sender.take());
// We've finished committing finalized blocks, so drop any repeated queued blocks. // We've finished committing checkpoint verified blocks to finalized state, so drop any repeated queued blocks.
self.clear_finalized_block_queue( self.clear_finalized_block_queue(
"already finished committing finalized blocks: dropped duplicate block, \ "already finished committing checkpoint verified blocks: dropped duplicate block, \
block is already committed to the state", block is already committed to the state",
); );
} }
@ -754,7 +749,7 @@ impl StateService {
/// Returns `true` if `queued_height` is near the final checkpoint. /// Returns `true` if `queued_height` is near the final checkpoint.
/// ///
/// The non-finalized block verifier needs access to UTXOs from finalized blocks /// The semantic block verifier needs access to UTXOs from checkpoint verified blocks
/// near the final checkpoint, so that it can verify blocks that spend those UTXOs. /// near the final checkpoint, so that it can verify blocks that spend those UTXOs.
/// ///
/// If it doesn't have the required UTXOs, some blocks will time out, /// If it doesn't have the required UTXOs, some blocks will time out,
@ -818,7 +813,7 @@ impl StateService {
// required by `Request::CommitSemanticallyVerifiedBlock` call // required by `Request::CommitSemanticallyVerifiedBlock` call
assert!( assert!(
block.height > self.network.mandatory_checkpoint_height(), block.height > self.network.mandatory_checkpoint_height(),
"invalid non-finalized block height: the canopy checkpoint is mandatory, pre-canopy \ "invalid semantically verified block height: the canopy checkpoint is mandatory, pre-canopy \
blocks, and the canopy activation block, must be committed to the state as finalized \ blocks, and the canopy activation block, must be committed to the state as finalized \
blocks" blocks"
); );
@ -970,11 +965,16 @@ impl Service<Request> for StateService {
Request::CommitCheckpointVerifiedBlock(finalized) => { Request::CommitCheckpointVerifiedBlock(finalized) => {
// # Consensus // # Consensus
// //
// A non-finalized block verification could have called AwaitUtxo // A semantic block verification could have called AwaitUtxo
// before this finalized block arrived in the state. // before this checkpoint verified block arrived in the state.
// So we need to check for pending UTXOs here for non-finalized blocks, // So we need to check for pending UTXO requests sent by running
// even though it is redundant for most finalized blocks. // semantic block verifications.
// (Finalized blocks are verified using block hash checkpoints //
// This check is redundant for most checkpoint verified blocks,
// because semantic verification can only succeed near the final
// checkpoint, when all the UTXOs are available for the verifying block.
//
// (Checkpoint block UTXOs are verified using block hash checkpoints
// and transaction merkle tree block header commitments.) // and transaction merkle tree block header commitments.)
self.pending_utxos self.pending_utxos
.check_against_ordered(&finalized.new_outputs); .check_against_ordered(&finalized.new_outputs);

View File

@ -38,8 +38,8 @@ mod tests;
pub(crate) use difficulty::AdjustedDifficulty; pub(crate) use difficulty::AdjustedDifficulty;
/// Check that the `prepared` block is contextually valid for `network`, based /// Check that the semantically verified block is contextually valid for `network`,
/// on the `finalized_tip_height` and `relevant_chain`. /// based on the `finalized_tip_height` and `relevant_chain`.
/// ///
/// This function performs checks that require a small number of recent blocks, /// This function performs checks that require a small number of recent blocks,
/// including previous hash, previous height, and block difficulty. /// including previous hash, previous height, and block difficulty.
@ -50,9 +50,9 @@ pub(crate) use difficulty::AdjustedDifficulty;
/// # Panics /// # Panics
/// ///
/// If the state contains less than 28 ([`POW_ADJUSTMENT_BLOCK_SPAN`]) blocks. /// If the state contains less than 28 ([`POW_ADJUSTMENT_BLOCK_SPAN`]) blocks.
#[tracing::instrument(skip(prepared, finalized_tip_height, relevant_chain))] #[tracing::instrument(skip(semantically_verified, finalized_tip_height, relevant_chain))]
pub(crate) fn block_is_valid_for_recent_chain<C>( pub(crate) fn block_is_valid_for_recent_chain<C>(
prepared: &SemanticallyVerifiedBlock, semantically_verified: &SemanticallyVerifiedBlock,
network: Network, network: Network,
finalized_tip_height: Option<block::Height>, finalized_tip_height: Option<block::Height>,
relevant_chain: C, relevant_chain: C,
@ -64,7 +64,7 @@ where
{ {
let finalized_tip_height = finalized_tip_height let finalized_tip_height = finalized_tip_height
.expect("finalized state must contain at least one block to do contextual validation"); .expect("finalized state must contain at least one block to do contextual validation");
check::block_is_not_orphaned(finalized_tip_height, prepared.height)?; check::block_is_not_orphaned(finalized_tip_height, semantically_verified.height)?;
let relevant_chain: Vec<_> = relevant_chain let relevant_chain: Vec<_> = relevant_chain
.into_iter() .into_iter()
@ -78,7 +78,7 @@ where
let parent_height = parent_block let parent_height = parent_block
.coinbase_height() .coinbase_height()
.expect("valid blocks have a coinbase height"); .expect("valid blocks have a coinbase height");
check::height_one_more_than_parent_height(parent_height, prepared.height)?; check::height_one_more_than_parent_height(parent_height, semantically_verified.height)?;
if relevant_chain.len() < POW_ADJUSTMENT_BLOCK_SPAN { if relevant_chain.len() < POW_ADJUSTMENT_BLOCK_SPAN {
// skip this check during tests if we don't have enough blocks in the chain // skip this check during tests if we don't have enough blocks in the chain
@ -107,9 +107,9 @@ where
) )
}); });
let difficulty_adjustment = let difficulty_adjustment =
AdjustedDifficulty::new_from_block(&prepared.block, network, relevant_data); AdjustedDifficulty::new_from_block(&semantically_verified.block, network, relevant_data);
check::difficulty_threshold_and_time_are_valid( check::difficulty_threshold_and_time_are_valid(
prepared.block.header.difficulty_threshold, semantically_verified.block.header.difficulty_threshold,
difficulty_adjustment, difficulty_adjustment,
)?; )?;
@ -375,23 +375,23 @@ where
pub(crate) fn initial_contextual_validity( pub(crate) fn initial_contextual_validity(
finalized_state: &ZebraDb, finalized_state: &ZebraDb,
non_finalized_state: &NonFinalizedState, non_finalized_state: &NonFinalizedState,
prepared: &SemanticallyVerifiedBlock, semantically_verified: &SemanticallyVerifiedBlock,
) -> Result<(), ValidateContextError> { ) -> Result<(), ValidateContextError> {
let relevant_chain = any_ancestor_blocks( let relevant_chain = any_ancestor_blocks(
non_finalized_state, non_finalized_state,
finalized_state, finalized_state,
prepared.block.header.previous_block_hash, semantically_verified.block.header.previous_block_hash,
); );
// Security: check proof of work before any other checks // Security: check proof of work before any other checks
check::block_is_valid_for_recent_chain( check::block_is_valid_for_recent_chain(
prepared, semantically_verified,
non_finalized_state.network, non_finalized_state.network,
finalized_state.finalized_tip_height(), finalized_state.finalized_tip_height(),
relevant_chain, relevant_chain,
)?; )?;
check::nullifier::no_duplicates_in_finalized_chain(prepared, finalized_state)?; check::nullifier::no_duplicates_in_finalized_chain(semantically_verified, finalized_state)?;
Ok(()) Ok(())
} }

View File

@ -190,7 +190,7 @@ fn fetch_sprout_final_treestates(
/// treestate of any prior `JoinSplit` _within the same transaction_. /// treestate of any prior `JoinSplit` _within the same transaction_.
/// ///
/// This method searches for anchors in the supplied `sprout_final_treestates` /// This method searches for anchors in the supplied `sprout_final_treestates`
/// (which must be populated with all treestates pointed to in the `prepared` block; /// (which must be populated with all treestates pointed to in the `semantically_verified` block;
/// see [`fetch_sprout_final_treestates()`]); or in the interstitial /// see [`fetch_sprout_final_treestates()`]); or in the interstitial
/// treestates which are computed on the fly in this function. /// treestates which are computed on the fly in this function.
#[tracing::instrument(skip(sprout_final_treestates, transaction))] #[tracing::instrument(skip(sprout_final_treestates, transaction))]
@ -322,20 +322,23 @@ fn sprout_anchors_refer_to_treestates(
pub(crate) fn block_sapling_orchard_anchors_refer_to_final_treestates( pub(crate) fn block_sapling_orchard_anchors_refer_to_final_treestates(
finalized_state: &ZebraDb, finalized_state: &ZebraDb,
parent_chain: &Arc<Chain>, parent_chain: &Arc<Chain>,
prepared: &SemanticallyVerifiedBlock, semantically_verified: &SemanticallyVerifiedBlock,
) -> Result<(), ValidateContextError> { ) -> Result<(), ValidateContextError> {
prepared.block.transactions.iter().enumerate().try_for_each( semantically_verified
|(tx_index_in_block, transaction)| { .block
.transactions
.iter()
.enumerate()
.try_for_each(|(tx_index_in_block, transaction)| {
sapling_orchard_anchors_refer_to_final_treestates( sapling_orchard_anchors_refer_to_final_treestates(
finalized_state, finalized_state,
Some(parent_chain), Some(parent_chain),
transaction, transaction,
prepared.transaction_hashes[tx_index_in_block], semantically_verified.transaction_hashes[tx_index_in_block],
Some(tx_index_in_block), Some(tx_index_in_block),
Some(prepared.height), Some(semantically_verified.height),
) )
}, })
)
} }
/// Accepts a [`ZebraDb`], [`Arc<Chain>`](Chain), and [`SemanticallyVerifiedBlock`]. /// Accepts a [`ZebraDb`], [`Arc<Chain>`](Chain), and [`SemanticallyVerifiedBlock`].
@ -353,18 +356,20 @@ pub(crate) fn block_sapling_orchard_anchors_refer_to_final_treestates(
pub(crate) fn block_fetch_sprout_final_treestates( pub(crate) fn block_fetch_sprout_final_treestates(
finalized_state: &ZebraDb, finalized_state: &ZebraDb,
parent_chain: &Arc<Chain>, parent_chain: &Arc<Chain>,
prepared: &SemanticallyVerifiedBlock, semantically_verified: &SemanticallyVerifiedBlock,
) -> HashMap<sprout::tree::Root, Arc<sprout::tree::NoteCommitmentTree>> { ) -> HashMap<sprout::tree::Root, Arc<sprout::tree::NoteCommitmentTree>> {
let mut sprout_final_treestates = HashMap::new(); let mut sprout_final_treestates = HashMap::new();
for (tx_index_in_block, transaction) in prepared.block.transactions.iter().enumerate() { for (tx_index_in_block, transaction) in
semantically_verified.block.transactions.iter().enumerate()
{
fetch_sprout_final_treestates( fetch_sprout_final_treestates(
&mut sprout_final_treestates, &mut sprout_final_treestates,
finalized_state, finalized_state,
Some(parent_chain), Some(parent_chain),
transaction, transaction,
Some(tx_index_in_block), Some(tx_index_in_block),
Some(prepared.height), Some(semantically_verified.height),
); );
} }
@ -381,7 +386,7 @@ pub(crate) fn block_fetch_sprout_final_treestates(
/// treestate of any prior `JoinSplit` _within the same transaction_. /// treestate of any prior `JoinSplit` _within the same transaction_.
/// ///
/// This method searches for anchors in the supplied `sprout_final_treestates` /// This method searches for anchors in the supplied `sprout_final_treestates`
/// (which must be populated with all treestates pointed to in the `prepared` block; /// (which must be populated with all treestates pointed to in the `semantically_verified` block;
/// see [`fetch_sprout_final_treestates()`]); or in the interstitial /// see [`fetch_sprout_final_treestates()`]); or in the interstitial
/// treestates which are computed on the fly in this function. /// treestates which are computed on the fly in this function.
#[tracing::instrument(skip(sprout_final_treestates, block, transaction_hashes))] #[tracing::instrument(skip(sprout_final_treestates, block, transaction_hashes))]

View File

@ -30,24 +30,24 @@ use crate::service;
/// > even if they have the same bit pattern. /// > even if they have the same bit pattern.
/// ///
/// <https://zips.z.cash/protocol/protocol.pdf#nullifierset> /// <https://zips.z.cash/protocol/protocol.pdf#nullifierset>
#[tracing::instrument(skip(prepared, finalized_state))] #[tracing::instrument(skip(semantically_verified, finalized_state))]
pub(crate) fn no_duplicates_in_finalized_chain( pub(crate) fn no_duplicates_in_finalized_chain(
prepared: &SemanticallyVerifiedBlock, semantically_verified: &SemanticallyVerifiedBlock,
finalized_state: &ZebraDb, finalized_state: &ZebraDb,
) -> Result<(), ValidateContextError> { ) -> Result<(), ValidateContextError> {
for nullifier in prepared.block.sprout_nullifiers() { for nullifier in semantically_verified.block.sprout_nullifiers() {
if finalized_state.contains_sprout_nullifier(nullifier) { if finalized_state.contains_sprout_nullifier(nullifier) {
Err(nullifier.duplicate_nullifier_error(true))?; Err(nullifier.duplicate_nullifier_error(true))?;
} }
} }
for nullifier in prepared.block.sapling_nullifiers() { for nullifier in semantically_verified.block.sapling_nullifiers() {
if finalized_state.contains_sapling_nullifier(nullifier) { if finalized_state.contains_sapling_nullifier(nullifier) {
Err(nullifier.duplicate_nullifier_error(true))?; Err(nullifier.duplicate_nullifier_error(true))?;
} }
} }
for nullifier in prepared.block.orchard_nullifiers() { for nullifier in semantically_verified.block.orchard_nullifiers() {
if finalized_state.contains_orchard_nullifier(nullifier) { if finalized_state.contains_orchard_nullifier(nullifier) {
Err(nullifier.duplicate_nullifier_error(true))?; Err(nullifier.duplicate_nullifier_error(true))?;
} }

View File

@ -36,14 +36,16 @@ use crate::{
/// - spends of an immature transparent coinbase output, /// - spends of an immature transparent coinbase output,
/// - unshielded spends of a transparent coinbase output. /// - unshielded spends of a transparent coinbase output.
pub fn transparent_spend( pub fn transparent_spend(
prepared: &SemanticallyVerifiedBlock, semantically_verified: &SemanticallyVerifiedBlock,
non_finalized_chain_unspent_utxos: &HashMap<transparent::OutPoint, transparent::OrderedUtxo>, non_finalized_chain_unspent_utxos: &HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
non_finalized_chain_spent_utxos: &HashSet<transparent::OutPoint>, non_finalized_chain_spent_utxos: &HashSet<transparent::OutPoint>,
finalized_state: &ZebraDb, finalized_state: &ZebraDb,
) -> Result<HashMap<transparent::OutPoint, transparent::OrderedUtxo>, ValidateContextError> { ) -> Result<HashMap<transparent::OutPoint, transparent::OrderedUtxo>, ValidateContextError> {
let mut block_spends = HashMap::new(); let mut block_spends = HashMap::new();
for (spend_tx_index_in_block, transaction) in prepared.block.transactions.iter().enumerate() { for (spend_tx_index_in_block, transaction) in
semantically_verified.block.transactions.iter().enumerate()
{
// Coinbase inputs represent new coins, // Coinbase inputs represent new coins,
// so there are no UTXOs to mark as spent. // so there are no UTXOs to mark as spent.
let spends = transaction let spends = transaction
@ -55,7 +57,7 @@ pub fn transparent_spend(
let utxo = transparent_spend_chain_order( let utxo = transparent_spend_chain_order(
spend, spend,
spend_tx_index_in_block, spend_tx_index_in_block,
&prepared.new_outputs, &semantically_verified.new_outputs,
non_finalized_chain_unspent_utxos, non_finalized_chain_unspent_utxos,
non_finalized_chain_spent_utxos, non_finalized_chain_spent_utxos,
finalized_state, finalized_state,
@ -70,7 +72,8 @@ pub fn transparent_spend(
// We don't want to use UTXOs from invalid pending blocks, // We don't want to use UTXOs from invalid pending blocks,
// so we check transparent coinbase maturity and shielding // so we check transparent coinbase maturity and shielding
// using known valid UTXOs during non-finalized chain validation. // using known valid UTXOs during non-finalized chain validation.
let spend_restriction = transaction.coinbase_spend_restriction(prepared.height); let spend_restriction =
transaction.coinbase_spend_restriction(semantically_verified.height);
transparent_coinbase_spend(spend, spend_restriction, utxo.as_ref())?; transparent_coinbase_spend(spend, spend_restriction, utxo.as_ref())?;
// We don't delete the UTXOs until the block is committed, // We don't delete the UTXOs until the block is committed,
@ -86,7 +89,7 @@ pub fn transparent_spend(
} }
} }
remaining_transaction_value(prepared, &block_spends)?; remaining_transaction_value(semantically_verified, &block_spends)?;
Ok(block_spends) Ok(block_spends)
} }
@ -225,10 +228,12 @@ pub fn transparent_coinbase_spend(
/// ///
/// <https://zips.z.cash/protocol/protocol.pdf#transactions> /// <https://zips.z.cash/protocol/protocol.pdf#transactions>
pub fn remaining_transaction_value( pub fn remaining_transaction_value(
prepared: &SemanticallyVerifiedBlock, semantically_verified: &SemanticallyVerifiedBlock,
utxos: &HashMap<transparent::OutPoint, transparent::OrderedUtxo>, utxos: &HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
) -> Result<(), ValidateContextError> { ) -> Result<(), ValidateContextError> {
for (tx_index_in_block, transaction) in prepared.block.transactions.iter().enumerate() { for (tx_index_in_block, transaction) in
semantically_verified.block.transactions.iter().enumerate()
{
if transaction.is_coinbase() { if transaction.is_coinbase() {
continue; continue;
} }
@ -243,26 +248,28 @@ pub fn remaining_transaction_value(
{ {
Err(ValidateContextError::NegativeRemainingTransactionValue { Err(ValidateContextError::NegativeRemainingTransactionValue {
amount_error, amount_error,
height: prepared.height, height: semantically_verified.height,
tx_index_in_block, tx_index_in_block,
transaction_hash: prepared.transaction_hashes[tx_index_in_block], transaction_hash: semantically_verified.transaction_hashes
[tx_index_in_block],
}) })
} }
Err(amount_error) => { Err(amount_error) => {
Err(ValidateContextError::CalculateRemainingTransactionValue { Err(ValidateContextError::CalculateRemainingTransactionValue {
amount_error, amount_error,
height: prepared.height, height: semantically_verified.height,
tx_index_in_block, tx_index_in_block,
transaction_hash: prepared.transaction_hashes[tx_index_in_block], transaction_hash: semantically_verified.transaction_hashes
[tx_index_in_block],
}) })
} }
}, },
Err(value_balance_error) => { Err(value_balance_error) => {
Err(ValidateContextError::CalculateTransactionValueBalances { Err(ValidateContextError::CalculateTransactionValueBalances {
value_balance_error, value_balance_error,
height: prepared.height, height: semantically_verified.height,
tx_index_in_block, tx_index_in_block,
transaction_hash: prepared.transaction_hashes[tx_index_in_block], transaction_hash: semantically_verified.transaction_hashes[tx_index_in_block],
}) })
} }
}? }?

View File

@ -15,13 +15,13 @@ use crate::{BoxError, CheckpointVerifiedBlock, SemanticallyVerifiedBlock};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
/// A finalized state queue block, and its corresponding [`Result`] channel. /// A queued checkpoint verified block, and its corresponding [`Result`] channel.
pub type QueuedCheckpointVerified = ( pub type QueuedCheckpointVerified = (
CheckpointVerifiedBlock, CheckpointVerifiedBlock,
oneshot::Sender<Result<block::Hash, BoxError>>, oneshot::Sender<Result<block::Hash, BoxError>>,
); );
/// A non-finalized state queue block, and its corresponding [`Result`] channel. /// A queued semantically verified block, and its corresponding [`Result`] channel.
pub type QueuedSemanticallyVerified = ( pub type QueuedSemanticallyVerified = (
SemanticallyVerifiedBlock, SemanticallyVerifiedBlock,
oneshot::Sender<Result<block::Hash, BoxError>>, oneshot::Sender<Result<block::Hash, BoxError>>,
@ -264,10 +264,10 @@ impl SentHashes {
self.update_metrics_for_block(block.height); self.update_metrics_for_block(block.height);
} }
/// Stores the finalized `block`'s hash, height, and UTXOs, so they can be used to check if a /// Stores the checkpoint verified `block`'s hash, height, and UTXOs, so they can be used to check if a
/// block or UTXO is available in the state. /// block or UTXO is available in the state.
/// ///
/// Used for finalized blocks close to the final checkpoint, so non-finalized blocks can look up /// Used for checkpoint verified blocks close to the final checkpoint, so the semantic block verifier can look up
/// their UTXOs. /// their UTXOs.
/// ///
/// Assumes that blocks are added in the order of their height between `finish_batch` calls /// Assumes that blocks are added in the order of their height between `finish_batch` calls