From 006c2ae42b446201ae3920e975bfd98b34c461e1 Mon Sep 17 00:00:00 2001 From: Marek Date: Wed, 21 Jun 2023 18:58:11 +0200 Subject: [PATCH] change(state): Refactor the structure of verified blocks (#7025) * Refactor `CheckpointVerifiedBlock` This commit turns `CheckpointVerifiedBlock` into a wrapper of `SemanticallyVerifiedBlock` since both structs have the same fields. * Refactor `ContextuallyVerifiedBlockWithTrees` This commit uses `SemanticallyVerifiedBlock` in `ContextuallyVerifiedBlockWithTrees` instead of `CheckpointVerifiedBlock`. --- zebra-state/src/arbitrary.rs | 4 +- zebra-state/src/request.rs | 123 ++++++++++-------- zebra-state/src/service/chain_tip.rs | 6 +- zebra-state/src/service/finalized_state.rs | 2 +- .../service/finalized_state/zebra_db/block.rs | 17 ++- .../service/finalized_state/zebra_db/chain.rs | 11 +- .../finalized_state/zebra_db/shielded.rs | 15 +-- .../finalized_state/zebra_db/transparent.rs | 6 +- .../non_finalized_state/tests/vectors.rs | 4 +- 9 files changed, 100 insertions(+), 88 deletions(-) diff --git a/zebra-state/src/arbitrary.rs b/zebra-state/src/arbitrary.rs index 2a8eaa5ea..9f87c749c 100644 --- a/zebra-state/src/arbitrary.rs +++ b/zebra-state/src/arbitrary.rs @@ -186,12 +186,12 @@ impl CheckpointVerifiedBlock { let new_outputs = transparent::new_ordered_outputs_with_height(&block, height, &transaction_hashes); - Self { + Self(SemanticallyVerifiedBlock { block, hash, height, new_outputs, transaction_hashes, - } + }) } } diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index dbff91022..5c1516886 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -2,7 +2,7 @@ use std::{ collections::{HashMap, HashSet}, - ops::RangeInclusive, + ops::{Deref, DerefMut, RangeInclusive}, sync::Arc, }; @@ -162,6 +162,17 @@ pub struct SemanticallyVerifiedBlock { pub transaction_hashes: Arc<[transaction::Hash]>, } +/// A block ready to be committed directly to the finalized state with +/// no checks. +/// +/// This is exposed for use in checkpointing. +/// +/// Note: The difference between a `CheckpointVerifiedBlock` and a `ContextuallyVerifiedBlock` is +/// that the `CheckpointVerifier` doesn't bind the transaction authorizing data to the +/// `ChainHistoryBlockTxAuthCommitmentHash`, but the `NonFinalizedState` and `FinalizedState` do. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CheckpointVerifiedBlock(pub(crate) SemanticallyVerifiedBlock); + // Some fields are pub(crate), so we can add whatever db-format-dependent // precomputation we want here without leaking internal details. @@ -211,36 +222,6 @@ pub struct ContextuallyVerifiedBlock { pub(crate) chain_value_pool_change: ValueBalance, } -/// A block ready to be committed directly to the finalized state with -/// no checks. -/// -/// This is exposed for use in checkpointing. -/// -/// Note: The difference between a `CheckpointVerifiedBlock` and a `ContextuallyVerifiedBlock` is -/// that the `CheckpointVerifier` doesn't bind the transaction authorizing data to the -/// `ChainHistoryBlockTxAuthCommitmentHash`, but the `NonFinalizedState` and `FinalizedState` do. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct CheckpointVerifiedBlock { - /// The block to commit to the state. - pub block: Arc, - /// The hash of the block. - pub hash: block::Hash, - /// The height of the block. - pub height: block::Height, - /// New transparent outputs created in this block, indexed by - /// [`OutPoint`](transparent::OutPoint). - /// - /// Note: although these transparent outputs are newly created, they may not - /// be unspent, since a later transaction in a block can spend outputs of an - /// earlier transaction. - /// - /// This field can also contain unrelated outputs, which are ignored. - pub(crate) new_outputs: HashMap, - /// A precomputed list of the hashes of the transactions in this block, - /// in the same order as `block.transactions`. - pub transaction_hashes: Arc<[transaction::Hash]>, -} - /// Wraps note commitment trees and the history tree together. pub struct Treestate { /// Note commitment trees. @@ -273,20 +254,18 @@ impl Treestate { /// Zebra's non-finalized state passes this `struct` over to the finalized state /// when committing a block. The associated treestate is passed so that the /// finalized state does not have to retrieve the previous treestate from the -/// database and recompute the new one. +/// database and recompute a new one. pub struct ContextuallyVerifiedBlockWithTrees { /// A block ready to be committed. - pub checkpoint_verified: CheckpointVerifiedBlock, + pub block: SemanticallyVerifiedBlock, /// The tresstate associated with the block. pub treestate: Option, } impl ContextuallyVerifiedBlockWithTrees { - pub fn new(block: ContextuallyVerifiedBlock, treestate: Treestate) -> Self { - let checkpoint_verified = CheckpointVerifiedBlock::from(block); - + pub fn new(contextually_verified: ContextuallyVerifiedBlock, treestate: Treestate) -> Self { Self { - checkpoint_verified, + block: SemanticallyVerifiedBlock::from(contextually_verified), treestate: Some(treestate), } } @@ -294,14 +273,23 @@ impl ContextuallyVerifiedBlockWithTrees { impl From> for ContextuallyVerifiedBlockWithTrees { fn from(block: Arc) -> Self { - Self::from(CheckpointVerifiedBlock::from(block)) + Self::from(SemanticallyVerifiedBlock::from(block)) + } +} + +impl From for ContextuallyVerifiedBlockWithTrees { + fn from(semantically_verified: SemanticallyVerifiedBlock) -> Self { + Self { + block: semantically_verified, + treestate: None, + } } } impl From for ContextuallyVerifiedBlockWithTrees { - fn from(block: CheckpointVerifiedBlock) -> Self { + fn from(checkpoint_verified: CheckpointVerifiedBlock) -> Self { Self { - checkpoint_verified: block, + block: checkpoint_verified.0, treestate: None, } } @@ -358,6 +346,24 @@ impl ContextuallyVerifiedBlock { } } +impl SemanticallyVerifiedBlock { + fn with_hash(block: Arc, hash: block::Hash) -> Self { + let height = block + .coinbase_height() + .expect("coinbase height was already checked"); + let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect(); + let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes); + + SemanticallyVerifiedBlock { + block, + hash, + height, + new_outputs, + transaction_hashes, + } + } +} + impl CheckpointVerifiedBlock { /// Create a block that's ready to be committed to the finalized state, /// using a precalculated [`block::Hash`]. @@ -365,19 +371,7 @@ impl CheckpointVerifiedBlock { /// Note: a [`CheckpointVerifiedBlock`] isn't actually finalized /// until [`Request::CommitCheckpointVerifiedBlock`] returns success. pub fn with_hash(block: Arc, hash: block::Hash) -> Self { - let height = block - .coinbase_height() - .expect("coinbase height was already checked"); - let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect(); - let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes); - - Self { - block, - hash, - height, - new_outputs, - transaction_hashes, - } + Self(SemanticallyVerifiedBlock::with_hash(block, hash)) } } @@ -389,7 +383,15 @@ impl From> for CheckpointVerifiedBlock { } } -impl From for CheckpointVerifiedBlock { +impl From> for SemanticallyVerifiedBlock { + fn from(block: Arc) -> Self { + let hash = block.hash(); + + SemanticallyVerifiedBlock::with_hash(block, hash) + } +} + +impl From for SemanticallyVerifiedBlock { fn from(contextually_valid: ContextuallyVerifiedBlock) -> Self { let ContextuallyVerifiedBlock { block, @@ -411,6 +413,19 @@ impl From for CheckpointVerifiedBlock { } } +impl Deref for CheckpointVerifiedBlock { + type Target = SemanticallyVerifiedBlock; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for CheckpointVerifiedBlock { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + #[derive(Clone, Debug, PartialEq, Eq)] /// A query about or modification to the chain state, via the /// [`StateService`](crate::service::StateService). diff --git a/zebra-state/src/service/chain_tip.rs b/zebra-state/src/service/chain_tip.rs index c08571c76..76f57bfab 100644 --- a/zebra-state/src/service/chain_tip.rs +++ b/zebra-state/src/service/chain_tip.rs @@ -21,7 +21,7 @@ use zebra_chain::{ use crate::{ request::ContextuallyVerifiedBlock, service::watch_receiver::WatchReceiver, - CheckpointVerifiedBlock, + CheckpointVerifiedBlock, SemanticallyVerifiedBlock, }; use TipAction::*; @@ -109,13 +109,13 @@ impl From for ChainTipBlock { impl From for ChainTipBlock { fn from(finalized: CheckpointVerifiedBlock) -> Self { - let CheckpointVerifiedBlock { + let CheckpointVerifiedBlock(SemanticallyVerifiedBlock { block, hash, height, transaction_hashes, .. - } = finalized; + }) = finalized; Self { hash, diff --git a/zebra-state/src/service/finalized_state.rs b/zebra-state/src/service/finalized_state.rs index c6ca264f3..ca1f58870 100644 --- a/zebra-state/src/service/finalized_state.rs +++ b/zebra-state/src/service/finalized_state.rs @@ -228,7 +228,7 @@ impl FinalizedState { contextually_verified_with_trees: ContextuallyVerifiedBlockWithTrees, source: &str, ) -> Result { - let finalized = contextually_verified_with_trees.checkpoint_verified; + let finalized = contextually_verified_with_trees.block; let committed_tip_hash = self.db.finalized_tip_hash(); let committed_tip_height = self.db.finalized_tip_height(); diff --git a/zebra-state/src/service/finalized_state/zebra_db/block.rs b/zebra-state/src/service/finalized_state/zebra_db/block.rs index 61e19100a..aad9f2272 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/block.rs @@ -38,9 +38,8 @@ use crate::{ transparent::{AddressBalanceLocation, OutputLocation}, }, zebra_db::{metrics::block_precommit_metrics, ZebraDb}, - CheckpointVerifiedBlock, }, - BoxError, HashOrHeight, + BoxError, HashOrHeight, SemanticallyVerifiedBlock, }; #[cfg(test)] @@ -282,7 +281,7 @@ impl ZebraDb { /// - Propagates any errors from updating history and note commitment trees pub(in super::super) fn write_block( &mut self, - finalized: CheckpointVerifiedBlock, + finalized: SemanticallyVerifiedBlock, history_tree: Arc, note_commitment_trees: NoteCommitmentTrees, network: Network, @@ -430,7 +429,7 @@ impl DiskWriteBatch { pub fn prepare_block_batch( &mut self, db: &DiskDb, - finalized: CheckpointVerifiedBlock, + finalized: SemanticallyVerifiedBlock, new_outputs_by_out_loc: BTreeMap, spent_utxos_by_outpoint: HashMap, spent_utxos_by_out_loc: BTreeMap, @@ -439,7 +438,7 @@ impl DiskWriteBatch { note_commitment_trees: NoteCommitmentTrees, value_pool: ValueBalance, ) -> Result<(), BoxError> { - let CheckpointVerifiedBlock { + let SemanticallyVerifiedBlock { block, hash, height, @@ -495,7 +494,7 @@ impl DiskWriteBatch { pub fn prepare_block_header_and_transaction_data_batch( &mut self, db: &DiskDb, - finalized: &CheckpointVerifiedBlock, + finalized: &SemanticallyVerifiedBlock, ) -> Result<(), BoxError> { // Blocks let block_header_by_height = db.cf_handle("block_header_by_height").unwrap(); @@ -507,7 +506,7 @@ impl DiskWriteBatch { let hash_by_tx_loc = db.cf_handle("hash_by_tx_loc").unwrap(); let tx_loc_by_hash = db.cf_handle("tx_loc_by_hash").unwrap(); - let CheckpointVerifiedBlock { + let SemanticallyVerifiedBlock { block, hash, height, @@ -554,9 +553,9 @@ impl DiskWriteBatch { pub fn prepare_genesis_batch( &mut self, db: &DiskDb, - finalized: &CheckpointVerifiedBlock, + finalized: &SemanticallyVerifiedBlock, ) -> bool { - let CheckpointVerifiedBlock { block, .. } = finalized; + let SemanticallyVerifiedBlock { block, .. } = finalized; if block.header.previous_block_hash == GENESIS_PREVIOUS_BLOCK_HASH { self.prepare_genesis_note_commitment_tree_batch(db, finalized); diff --git a/zebra-state/src/service/finalized_state/zebra_db/chain.rs b/zebra-state/src/service/finalized_state/zebra_db/chain.rs index b8db8d717..590f609d8 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/chain.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/chain.rs @@ -24,9 +24,8 @@ use crate::{ service::finalized_state::{ disk_db::{DiskDb, DiskWriteBatch, ReadDisk, WriteDisk}, zebra_db::ZebraDb, - CheckpointVerifiedBlock, }, - BoxError, + BoxError, SemanticallyVerifiedBlock, }; impl ZebraDb { @@ -70,12 +69,12 @@ impl DiskWriteBatch { pub fn prepare_history_batch( &mut self, db: &DiskDb, - finalized: &CheckpointVerifiedBlock, + finalized: &SemanticallyVerifiedBlock, history_tree: Arc, ) -> Result<(), BoxError> { let history_tree_cf = db.cf_handle("history_tree").unwrap(); - let CheckpointVerifiedBlock { height, .. } = finalized; + let SemanticallyVerifiedBlock { height, .. } = finalized; // Update the tree in state let current_tip_height = *height - 1; @@ -108,13 +107,13 @@ impl DiskWriteBatch { pub fn prepare_chain_value_pools_batch( &mut self, db: &DiskDb, - finalized: &CheckpointVerifiedBlock, + finalized: &SemanticallyVerifiedBlock, utxos_spent_by_block: HashMap, value_pool: ValueBalance, ) -> Result<(), BoxError> { let tip_chain_value_pool = db.cf_handle("tip_chain_value_pool").unwrap(); - let CheckpointVerifiedBlock { block, .. } = finalized; + let SemanticallyVerifiedBlock { block, .. } = finalized; let new_pool = value_pool.add_block(block.borrow(), &utxos_spent_by_block)?; self.zs_insert(&tip_chain_value_pool, (), new_pool); diff --git a/zebra-state/src/service/finalized_state/zebra_db/shielded.rs b/zebra-state/src/service/finalized_state/zebra_db/shielded.rs index 83a4d36f6..ac306bdfe 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/shielded.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/shielded.rs @@ -23,9 +23,8 @@ use crate::{ service::finalized_state::{ disk_db::{DiskDb, DiskWriteBatch, ReadDisk, WriteDisk}, zebra_db::ZebraDb, - CheckpointVerifiedBlock, }, - BoxError, + BoxError, SemanticallyVerifiedBlock, }; impl ZebraDb { @@ -210,9 +209,9 @@ impl DiskWriteBatch { pub fn prepare_shielded_transaction_batch( &mut self, db: &DiskDb, - finalized: &CheckpointVerifiedBlock, + finalized: &SemanticallyVerifiedBlock, ) -> Result<(), BoxError> { - let CheckpointVerifiedBlock { block, .. } = finalized; + let SemanticallyVerifiedBlock { block, .. } = finalized; // Index each transaction's shielded data for transaction in &block.transactions { @@ -265,7 +264,7 @@ impl DiskWriteBatch { pub fn prepare_note_commitment_batch( &mut self, db: &DiskDb, - finalized: &CheckpointVerifiedBlock, + finalized: &SemanticallyVerifiedBlock, note_commitment_trees: NoteCommitmentTrees, history_tree: Arc, ) -> Result<(), BoxError> { @@ -277,7 +276,7 @@ impl DiskWriteBatch { let sapling_note_commitment_tree_cf = db.cf_handle("sapling_note_commitment_tree").unwrap(); let orchard_note_commitment_tree_cf = db.cf_handle("orchard_note_commitment_tree").unwrap(); - let CheckpointVerifiedBlock { height, .. } = finalized; + let SemanticallyVerifiedBlock { height, .. } = finalized; // Use the cached values that were previously calculated in parallel. let sprout_root = note_commitment_trees.sprout.root(); @@ -328,13 +327,13 @@ impl DiskWriteBatch { pub fn prepare_genesis_note_commitment_tree_batch( &mut self, db: &DiskDb, - finalized: &CheckpointVerifiedBlock, + finalized: &SemanticallyVerifiedBlock, ) { let sprout_note_commitment_tree_cf = db.cf_handle("sprout_note_commitment_tree").unwrap(); let sapling_note_commitment_tree_cf = db.cf_handle("sapling_note_commitment_tree").unwrap(); let orchard_note_commitment_tree_cf = db.cf_handle("orchard_note_commitment_tree").unwrap(); - let CheckpointVerifiedBlock { height, .. } = finalized; + let SemanticallyVerifiedBlock { height, .. } = finalized; // Insert empty note commitment trees. Note that these can't be // used too early (e.g. the Orchard tree before Nu5 activates) diff --git a/zebra-state/src/service/finalized_state/zebra_db/transparent.rs b/zebra-state/src/service/finalized_state/zebra_db/transparent.rs index 91509631d..9eda37a88 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/transparent.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/transparent.rs @@ -35,7 +35,7 @@ use crate::{ }, zebra_db::ZebraDb, }, - BoxError, CheckpointVerifiedBlock, + BoxError, SemanticallyVerifiedBlock, }; impl ZebraDb { @@ -369,13 +369,13 @@ impl DiskWriteBatch { pub fn prepare_transparent_transaction_batch( &mut self, db: &DiskDb, - finalized: &CheckpointVerifiedBlock, + finalized: &SemanticallyVerifiedBlock, new_outputs_by_out_loc: &BTreeMap, spent_utxos_by_outpoint: &HashMap, spent_utxos_by_out_loc: &BTreeMap, mut address_balances: HashMap, ) -> Result<(), BoxError> { - let CheckpointVerifiedBlock { block, height, .. } = finalized; + let SemanticallyVerifiedBlock { block, height, .. } = finalized; // Update created and spent transparent outputs self.prepare_new_transparent_outputs_batch( diff --git a/zebra-state/src/service/non_finalized_state/tests/vectors.rs b/zebra-state/src/service/non_finalized_state/tests/vectors.rs index a8e61e7c0..a7e008bcf 100644 --- a/zebra-state/src/service/non_finalized_state/tests/vectors.rs +++ b/zebra-state/src/service/non_finalized_state/tests/vectors.rs @@ -214,11 +214,11 @@ fn finalize_pops_from_best_chain_for_network(network: Network) -> Result<()> { state.commit_block(child.prepare(), &finalized_state)?; let finalized_with_trees = state.finalize(); - let finalized = finalized_with_trees.checkpoint_verified; + let finalized = finalized_with_trees.block; assert_eq!(block1, finalized.block); let finalized_with_trees = state.finalize(); - let finalized = finalized_with_trees.checkpoint_verified; + let finalized = finalized_with_trees.block; assert_eq!(block2, finalized.block); assert!(state.best_chain().is_none());