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`.
This commit is contained in:
parent
343a683cea
commit
006c2ae42b
|
@ -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,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<NegativeAllowed>,
|
||||
}
|
||||
|
||||
/// 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<Block>,
|
||||
/// 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<transparent::OutPoint, transparent::OrderedUtxo>,
|
||||
/// 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<Treestate>,
|
||||
}
|
||||
|
||||
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<Arc<Block>> for ContextuallyVerifiedBlockWithTrees {
|
||||
fn from(block: Arc<Block>) -> Self {
|
||||
Self::from(CheckpointVerifiedBlock::from(block))
|
||||
Self::from(SemanticallyVerifiedBlock::from(block))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SemanticallyVerifiedBlock> for ContextuallyVerifiedBlockWithTrees {
|
||||
fn from(semantically_verified: SemanticallyVerifiedBlock) -> Self {
|
||||
Self {
|
||||
block: semantically_verified,
|
||||
treestate: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CheckpointVerifiedBlock> 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<Block>, 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<Block>, 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<Arc<Block>> for CheckpointVerifiedBlock {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ContextuallyVerifiedBlock> for CheckpointVerifiedBlock {
|
||||
impl From<Arc<Block>> for SemanticallyVerifiedBlock {
|
||||
fn from(block: Arc<Block>) -> Self {
|
||||
let hash = block.hash();
|
||||
|
||||
SemanticallyVerifiedBlock::with_hash(block, hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ContextuallyVerifiedBlock> for SemanticallyVerifiedBlock {
|
||||
fn from(contextually_valid: ContextuallyVerifiedBlock) -> Self {
|
||||
let ContextuallyVerifiedBlock {
|
||||
block,
|
||||
|
@ -411,6 +413,19 @@ impl From<ContextuallyVerifiedBlock> 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).
|
||||
|
|
|
@ -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<ContextuallyVerifiedBlock> for ChainTipBlock {
|
|||
|
||||
impl From<CheckpointVerifiedBlock> for ChainTipBlock {
|
||||
fn from(finalized: CheckpointVerifiedBlock) -> Self {
|
||||
let CheckpointVerifiedBlock {
|
||||
let CheckpointVerifiedBlock(SemanticallyVerifiedBlock {
|
||||
block,
|
||||
hash,
|
||||
height,
|
||||
transaction_hashes,
|
||||
..
|
||||
} = finalized;
|
||||
}) = finalized;
|
||||
|
||||
Self {
|
||||
hash,
|
||||
|
|
|
@ -228,7 +228,7 @@ impl FinalizedState {
|
|||
contextually_verified_with_trees: ContextuallyVerifiedBlockWithTrees,
|
||||
source: &str,
|
||||
) -> Result<block::Hash, BoxError> {
|
||||
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();
|
||||
|
||||
|
|
|
@ -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<HistoryTree>,
|
||||
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<OutputLocation, transparent::Utxo>,
|
||||
spent_utxos_by_outpoint: HashMap<transparent::OutPoint, transparent::Utxo>,
|
||||
spent_utxos_by_out_loc: BTreeMap<OutputLocation, transparent::Utxo>,
|
||||
|
@ -439,7 +438,7 @@ impl DiskWriteBatch {
|
|||
note_commitment_trees: NoteCommitmentTrees,
|
||||
value_pool: ValueBalance<NonNegative>,
|
||||
) -> 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);
|
||||
|
|
|
@ -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<HistoryTree>,
|
||||
) -> 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<transparent::OutPoint, transparent::Utxo>,
|
||||
value_pool: ValueBalance<NonNegative>,
|
||||
) -> 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);
|
||||
|
|
|
@ -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<HistoryTree>,
|
||||
) -> 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)
|
||||
|
|
|
@ -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<OutputLocation, transparent::Utxo>,
|
||||
spent_utxos_by_outpoint: &HashMap<transparent::OutPoint, transparent::Utxo>,
|
||||
spent_utxos_by_out_loc: &BTreeMap<OutputLocation, transparent::Utxo>,
|
||||
mut address_balances: HashMap<transparent::Address, AddressBalanceLocation>,
|
||||
) -> Result<(), BoxError> {
|
||||
let CheckpointVerifiedBlock { block, height, .. } = finalized;
|
||||
let SemanticallyVerifiedBlock { block, height, .. } = finalized;
|
||||
|
||||
// Update created and spent transparent outputs
|
||||
self.prepare_new_transparent_outputs_batch(
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue