diff --git a/zebra-chain/Cargo.toml b/zebra-chain/Cargo.toml index 70f31bbae..2905018a6 100644 --- a/zebra-chain/Cargo.toml +++ b/zebra-chain/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" [features] default = [] -proptest-impl = ["proptest", "proptest-derive", "itertools", "zebra-test", "rand", "rand_chacha"] +proptest-impl = ["proptest", "proptest-derive", "zebra-test", "rand", "rand_chacha"] bench = ["zebra-test"] [dependencies] @@ -31,6 +31,7 @@ group = "0.11.0" halo2 = "=0.1.0-beta.1" hex = "0.4" incrementalmerkletree = "0.1.0" +itertools = "0.10.1" jubjub = "0.8.0" lazy_static = "1.4.0" rand_core = "0.6" @@ -56,7 +57,7 @@ zcash_history = { git = "https://github.com/ZcashFoundation/librustzcash.git", r proptest = { version = "0.10", optional = true } proptest-derive = { version = "0.3.0", optional = true } -itertools = { version = "0.10.1", optional = true } + rand = { version = "0.8", optional = true } rand_chacha = { version = "0.3", optional = true } diff --git a/zebra-chain/src/sapling/shielded_data.rs b/zebra-chain/src/sapling/shielded_data.rs index 3eed97c35..6eee9bce8 100644 --- a/zebra-chain/src/sapling/shielded_data.rs +++ b/zebra-chain/src/sapling/shielded_data.rs @@ -4,6 +4,12 @@ //! The `value_balance` change is handled using the default zero value. //! The anchor change is handled using the `AnchorVariant` type trait. +use std::{ + cmp::{max, Eq, PartialEq}, + fmt::{self, Debug}, +}; + +use itertools::Itertools; #[cfg(any(test, feature = "proptest-impl"))] use proptest_derive::Arbitrary; use serde::{de::DeserializeOwned, Serialize}; @@ -21,11 +27,6 @@ use crate::{ serialization::{AtLeastOne, TrustedPreallocate}, }; -use std::{ - cmp::{max, Eq, PartialEq}, - fmt::{self, Debug}, -}; - /// Per-Spend Sapling anchors, used in Transaction V4 and the /// `spends_per_anchor` method. #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] @@ -205,6 +206,16 @@ where AnchorV: AnchorVariant + Clone, Spend: From<(Spend, AnchorV::Shared)>, { + /// Iterate over the [`Spend`]s for this transaction, returning deduplicated + /// [`tree::Root`]s, regardless of the underlying transaction version. + pub fn anchors(&self) -> impl Iterator + '_ { + // TODO: use TransferData::shared_anchor to improve performance for V5 transactions + self.spends_per_anchor() + .map(|spend| spend.per_spend_anchor) + .sorted() + .dedup() + } + /// Iterate over the [`Spend`]s for this transaction, returning /// `Spend` regardless of the underlying transaction version. /// diff --git a/zebra-chain/src/sapling/tree.rs b/zebra-chain/src/sapling/tree.rs index 7649ef35f..1f02d6f46 100644 --- a/zebra-chain/src/sapling/tree.rs +++ b/zebra-chain/src/sapling/tree.rs @@ -81,7 +81,7 @@ pub struct Position(pub(crate) u64); /// commitment tree corresponding to the final Sapling treestate of /// this block. A root of a note commitment tree is associated with /// each treestate. -#[derive(Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize, Hash)] +#[derive(Clone, Copy, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct Root(pub [u8; 32]); impl fmt::Debug for Root { diff --git a/zebra-chain/src/transaction.rs b/zebra-chain/src/transaction.rs index 24a15a00a..2f58e4fe4 100644 --- a/zebra-chain/src/transaction.rs +++ b/zebra-chain/src/transaction.rs @@ -515,6 +515,29 @@ impl Transaction { // sprout + /// Returns the Sprout `JoinSplit`s in this transaction, regardless of version. + pub fn sprout_groth16_joinsplits( + &self, + ) -> Box> + '_> { + match self { + // JoinSplits with Groth16 Proofs + Transaction::V4 { + joinsplit_data: Some(joinsplit_data), + .. + } => Box::new(joinsplit_data.joinsplits()), + + // No JoinSplits / JoinSplits with BCTV14 proofs + Transaction::V1 { .. } + | Transaction::V2 { .. } + | Transaction::V3 { .. } + | Transaction::V4 { + joinsplit_data: None, + .. + } + | Transaction::V5 { .. } => Box::new(std::iter::empty()), + } + } + /// Returns the number of `JoinSplit`s in this transaction, regardless of version. pub fn joinsplit_count(&self) -> usize { match self { @@ -591,6 +614,37 @@ impl Transaction { // sapling + /// Access the deduplicated [`sapling::tree::Root`]s in this transaction, + /// regardless of version. + pub fn sapling_anchors(&self) -> Box + '_> { + // This function returns a boxed iterator because the different + // transaction variants end up having different iterator types + match self { + Transaction::V4 { + sapling_shielded_data: Some(sapling_shielded_data), + .. + } => Box::new(sapling_shielded_data.anchors()), + + Transaction::V5 { + sapling_shielded_data: Some(sapling_shielded_data), + .. + } => Box::new(sapling_shielded_data.anchors()), + + // No Spends + Transaction::V1 { .. } + | Transaction::V2 { .. } + | Transaction::V3 { .. } + | Transaction::V4 { + sapling_shielded_data: None, + .. + } + | Transaction::V5 { + sapling_shielded_data: None, + .. + } => Box::new(std::iter::empty()), + } + } + /// Iterate over the sapling [`Spend`](sapling::Spend)s for this transaction, /// returning `Spend` regardless of the underlying /// transaction version. diff --git a/zebra-state/src/error.rs b/zebra-state/src/error.rs index de940a3e0..4b5169ff2 100644 --- a/zebra-state/src/error.rs +++ b/zebra-state/src/error.rs @@ -228,6 +228,18 @@ pub enum ValidateContextError { #[error("block contains an invalid commitment")] InvalidBlockCommitment(#[from] block::CommitmentError), + + #[error("unknown Sprout anchor: {anchor:?}")] + #[non_exhaustive] + UnknownSproutAnchor { anchor: sprout::tree::Root }, + + #[error("unknown Sapling anchor: {anchor:?}")] + #[non_exhaustive] + UnknownSaplingAnchor { anchor: sapling::tree::Root }, + + #[error("unknown Orchard anchor: {anchor:?}")] + #[non_exhaustive] + UnknownOrchardAnchor { anchor: orchard::tree::Root }, } /// Trait for creating the corresponding duplicate nullifier error from a nullifier. diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index 8bc75d28c..ff0cc883e 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -225,6 +225,7 @@ impl StateService { /// Run contextual validation on the prepared block and add it to the /// non-finalized state if it is contextually valid. + #[tracing::instrument(level = "debug", skip(self, prepared))] fn validate_and_commit(&mut self, prepared: PreparedBlock) -> Result<(), CommitBlockError> { self.check_contextual_validity(&prepared)?; let parent_hash = prepared.block.header.previous_block_hash; @@ -245,6 +246,7 @@ impl StateService { /// Attempt to validate and commit all queued blocks whose parents have /// recently arrived starting from `new_parent`, in breadth-first ordering. + #[tracing::instrument(level = "debug", skip(self, new_parent))] fn process_queued(&mut self, new_parent: block::Hash) { let mut new_parents: Vec<(block::Hash, Result<(), CloneError>)> = vec![(new_parent, Ok(()))]; diff --git a/zebra-state/src/service/check.rs b/zebra-state/src/service/check.rs index 88cc4ba26..f8262041f 100644 --- a/zebra-state/src/service/check.rs +++ b/zebra-state/src/service/check.rs @@ -18,6 +18,7 @@ use super::check; use difficulty::{AdjustedDifficulty, POW_MEDIAN_BLOCK_SPAN}; +pub(crate) mod anchors; pub(crate) mod difficulty; pub(crate) mod nullifier; pub(crate) mod utxo; diff --git a/zebra-state/src/service/check/anchors.rs b/zebra-state/src/service/check/anchors.rs new file mode 100644 index 000000000..2297c7db6 --- /dev/null +++ b/zebra-state/src/service/check/anchors.rs @@ -0,0 +1,104 @@ +//! Checks for whether cited anchors are previously-computed note commitment +//! tree roots. + +use crate::{ + service::{finalized_state::FinalizedState, non_finalized_state::Chain}, + PreparedBlock, ValidateContextError, +}; + +/// Check that all the Sprout, Sapling, and Orchard anchors specified by +/// transactions in this block have been computed previously within the context +/// of its parent chain. +/// +/// Sprout anchors may refer to some earlier block's final treestate (like +/// Sapling and Orchard do exclusively) _or_ to the interstisial output +/// treestate of any prior `JoinSplit` _within the same transaction_. +/// +/// > For the first JoinSplit description of a transaction, the anchor MUST be +/// > the output Sprout treestate of a previous block.[^sprout] +/// +/// > The anchor of each JoinSplit description in a transaction MUST refer to +/// > either some earlier block’s final Sprout treestate, or to the interstitial +/// > output treestate of any prior JoinSplit description in the same transaction.[^sprout] +/// +/// > The anchor of each Spend description MUST refer to some earlier +/// > block’s final Sapling treestate. The anchor is encoded separately in +/// > each Spend description for v4 transactions, or encoded once and +/// > shared between all Spend descriptions in a v5 transaction.[^sapling] +/// +/// > The anchorOrchard field of the transaction, whenever it exists (i.e. when +/// > there are any Action descriptions), MUST refer to some earlier block’s +/// > final Orchard treestate.[^orchard] +/// +/// [^sprout]: +/// [^sapling]: +/// [^orchard]: +#[tracing::instrument(skip(finalized_state, parent_chain, prepared))] +pub(crate) fn anchors_refer_to_earlier_treestates( + finalized_state: &FinalizedState, + parent_chain: &Chain, + prepared: &PreparedBlock, +) -> Result<(), ValidateContextError> { + for transaction in prepared.block.transactions.iter() { + // Sprout JoinSplits, with interstitial treestates to check as well + // + // The FIRST JOINSPLIT in a transaction MUST refer to the output treestate + // of a previous block. + + // if let Some(sprout_shielded_data) = transaction.joinsplit_data { + // for joinsplit in transaction.sprout_groth16_joinsplits() { + // if !parent_chain.sprout_anchors.contains(joinsplit.anchor) + // && !finalized_state.contains_sprout_anchor(&joinsplit.anchor) + // { + // if !(joinsplit == &sprout_shielded_data.first) { + // // TODO: check interstitial treestates of the earlier JoinSplits + // // in this transaction against this anchor + // unimplemented!() + // } else { + // return Err(ValidateContextError::UnknownSproutAnchor { + // anchor: joinsplit.anchor, + // }); + // } + // } + // } + // } + + // Sapling Spends + // + // MUST refer to some earlier block’s final Sapling treestate. + if transaction.has_sapling_shielded_data() { + for anchor in transaction.sapling_anchors() { + tracing::debug!(?anchor, "observed sapling anchor"); + + if !parent_chain.sapling_anchors.contains(&anchor) + && !finalized_state.contains_sapling_anchor(&anchor) + { + return Err(ValidateContextError::UnknownSaplingAnchor { anchor }); + } + + tracing::debug!(?anchor, "validated sapling anchor"); + } + } + + // Orchard Actions + // + // MUST refer to some earlier block’s final Orchard treestate. + if let Some(orchard_shielded_data) = transaction.orchard_shielded_data() { + tracing::debug!(?orchard_shielded_data.shared_anchor, "observed orchard anchor"); + + if !parent_chain + .orchard_anchors + .contains(&orchard_shielded_data.shared_anchor) + && !finalized_state.contains_orchard_anchor(&orchard_shielded_data.shared_anchor) + { + return Err(ValidateContextError::UnknownOrchardAnchor { + anchor: orchard_shielded_data.shared_anchor, + }); + } + + tracing::debug!(?orchard_shielded_data.shared_anchor, "validated orchard anchor"); + } + } + + Ok(()) +} diff --git a/zebra-state/src/service/check/tests.rs b/zebra-state/src/service/check/tests.rs index 42ab35384..960810576 100644 --- a/zebra-state/src/service/check/tests.rs +++ b/zebra-state/src/service/check/tests.rs @@ -1,5 +1,6 @@ //! Tests for state contextual validation checks. +mod anchors; mod nullifier; mod utxo; mod vectors; diff --git a/zebra-state/src/service/check/tests/anchors.rs b/zebra-state/src/service/check/tests/anchors.rs new file mode 100644 index 000000000..0fa264feb --- /dev/null +++ b/zebra-state/src/service/check/tests/anchors.rs @@ -0,0 +1,116 @@ +use std::{convert::TryInto, ops::Deref, sync::Arc}; + +use zebra_chain::{ + block::{Block, Height}, + serialization::ZcashDeserializeInto, + transaction::{LockTime, Transaction}, +}; + +use crate::{ + arbitrary::Prepare, + tests::setup::{new_state_with_mainnet_genesis, transaction_v4_from_coinbase}, +}; + +// sapling + +/// Check that, when primed with the first Sapling blocks, a Sapling Spend's referenced anchor is +/// validated. +#[test] +fn check_sapling_anchors() { + zebra_test::init(); + + let (mut state, _genesis) = new_state_with_mainnet_genesis(); + + // Bootstrap a block at height == 1 that has the first Sapling note commitments + let mut block1 = zebra_test::vectors::BLOCK_MAINNET_1_BYTES + .zcash_deserialize_into::() + .expect("block should deserialize"); + + // convert the coinbase transaction to a version that the non-finalized state will accept + block1.transactions[0] = transaction_v4_from_coinbase(&block1.transactions[0]).into(); + + // Prime finalized state with the Sapling start + 1, which has the first + // Sapling note commitment + let block_419201 = zebra_test::vectors::BLOCK_MAINNET_419201_BYTES + .zcash_deserialize_into::() + .expect("block should deserialize"); + + block_419201 + .transactions + .into_iter() + .filter(|tx| tx.has_sapling_shielded_data()) + .for_each(|tx| { + let sapling_shielded_data = match tx.deref() { + Transaction::V4 { + sapling_shielded_data, + .. + } => sapling_shielded_data.clone(), + _ => unreachable!("These are known v4 transactions"), + }; + + // set value balance to 0 to pass the chain value pool checks + let sapling_shielded_data = sapling_shielded_data.map(|mut s| { + s.value_balance = 0.try_into().expect("unexpected invalid zero amount"); + s + }); + + block1.transactions.push(Arc::new(Transaction::V4 { + inputs: Vec::new(), + outputs: Vec::new(), + lock_time: LockTime::min_lock_time(), + expiry_height: Height(0), + joinsplit_data: None, + sapling_shielded_data, + })) + }); + + let block1 = Arc::new(block1).prepare(); + assert!(state.validate_and_commit(block1).is_ok()); + + // Bootstrap a block at height == 2 that references the Sapling note commitment tree state + // from earlier block + let mut block2 = zebra_test::vectors::BLOCK_MAINNET_2_BYTES + .zcash_deserialize_into::() + .expect("block should deserialize"); + + // convert the coinbase transaction to a version that the non-finalized state will accept + block2.transactions[0] = transaction_v4_from_coinbase(&block2.transactions[0]).into(); + + // Exercise Sapling anchor checking with Sapling start + 2, which refers to the note commitment + // tree as of the last transaction of the previous block + let block_419202 = zebra_test::vectors::BLOCK_MAINNET_419202_BYTES + .zcash_deserialize_into::() + .expect("block should deserialize"); + + block_419202 + .transactions + .into_iter() + .filter(|tx| tx.has_sapling_shielded_data()) + .for_each(|tx| { + let sapling_shielded_data = match tx.deref() { + Transaction::V4 { + sapling_shielded_data, + .. + } => (sapling_shielded_data.clone()), + _ => unreachable!("These are known v4 transactions"), + }; + + // set value balance to 0 to pass the chain value pool checks + let sapling_shielded_data = sapling_shielded_data.map(|mut s| { + s.value_balance = 0.try_into().expect("unexpected invalid zero amount"); + s + }); + + block2.transactions.push(Arc::new(Transaction::V4 { + inputs: Vec::new(), + outputs: Vec::new(), + lock_time: LockTime::min_lock_time(), + expiry_height: Height(0), + joinsplit_data: None, + sapling_shielded_data, + })) + }); + + let block2 = Arc::new(block2).prepare(); + assert_eq!(state.validate_and_commit(block2), Ok(())); +} diff --git a/zebra-state/src/service/check/tests/nullifier.rs b/zebra-state/src/service/check/tests/nullifier.rs index 0c29b46c1..2217f0b78 100644 --- a/zebra-state/src/service/check/tests/nullifier.rs +++ b/zebra-state/src/service/check/tests/nullifier.rs @@ -73,6 +73,10 @@ proptest! { block1.transactions.push(transaction.into()); let (mut state, _genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let previous_mem = state.mem.clone(); // randomly choose to commit the block to the finalized or non-finalized state @@ -141,6 +145,10 @@ proptest! { block1.transactions.push(transaction.into()); let (mut state, genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let previous_mem = state.mem.clone(); let block1 = Arc::new(block1).prepare(); @@ -194,6 +202,10 @@ proptest! { block1.transactions.push(transaction.into()); let (mut state, genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let previous_mem = state.mem.clone(); let block1 = Arc::new(block1).prepare(); @@ -247,6 +259,10 @@ proptest! { .extend([transaction1.into(), transaction2.into()]); let (mut state, genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let previous_mem = state.mem.clone(); let block1 = Arc::new(block1).prepare(); @@ -305,6 +321,10 @@ proptest! { block2.transactions.push(transaction2.into()); let (mut state, _genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let mut previous_mem = state.mem.clone(); let block1_hash; @@ -386,6 +406,10 @@ proptest! { block1.transactions.push(transaction.into()); let (mut state, _genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let previous_mem = state.mem.clone(); // randomly choose to commit the block to the finalized or non-finalized state @@ -438,6 +462,10 @@ proptest! { block1.transactions.push(transaction.into()); let (mut state, genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let previous_mem = state.mem.clone(); let block1 = Arc::new(block1).prepare(); @@ -486,6 +514,10 @@ proptest! { .extend([transaction1.into(), transaction2.into()]); let (mut state, genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let previous_mem = state.mem.clone(); let block1 = Arc::new(block1).prepare(); @@ -538,6 +570,11 @@ proptest! { block2.transactions.push(transaction2.into()); let (mut state, _genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + state.disk.populate_with_anchors(&block2); + let mut previous_mem = state.mem.clone(); let block1_hash; @@ -613,6 +650,10 @@ proptest! { block1.transactions.push(transaction.into()); let (mut state, _genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let previous_mem = state.mem.clone(); // randomly choose to commit the block to the finalized or non-finalized state @@ -665,6 +706,10 @@ proptest! { block1.transactions.push(transaction.into()); let (mut state, genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let previous_mem = state.mem.clone(); let block1 = Arc::new(block1).prepare(); @@ -717,6 +762,10 @@ proptest! { .extend([transaction1.into(), transaction2.into()]); let (mut state, genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + let previous_mem = state.mem.clone(); let block1 = Arc::new(block1).prepare(); @@ -773,6 +822,11 @@ proptest! { block2.transactions.push(transaction2.into()); let (mut state, _genesis) = new_state_with_mainnet_genesis(); + + // Allows anchor checks to pass + state.disk.populate_with_anchors(&block1); + state.disk.populate_with_anchors(&block2); + let mut previous_mem = state.mem.clone(); let block1_hash; diff --git a/zebra-state/src/service/finalized_state.rs b/zebra-state/src/service/finalized_state.rs index 05cf8f607..d3e025aeb 100644 --- a/zebra-state/src/service/finalized_state.rs +++ b/zebra-state/src/service/finalized_state.rs @@ -632,6 +632,24 @@ impl FinalizedState { self.db.zs_contains(orchard_nullifiers, &orchard_nullifier) } + // /// Returns `true` if the finalized state contains `sprout_anchor`. + // pub fn contains_sprout_anchor(&self, sprout_anchor: &sprout::tree::Root) -> bool { + // let sprout_anchors = self.db.cf_handle("sprout_anchors").unwrap(); + // self.db.zs_contains(sprout_anchors, &sprout_anchor) + // } + + /// Returns `true` if the finalized state contains `sapling_anchor`. + pub fn contains_sapling_anchor(&self, sapling_anchor: &sapling::tree::Root) -> bool { + let sapling_anchors = self.db.cf_handle("sapling_anchors").unwrap(); + self.db.zs_contains(sapling_anchors, &sapling_anchor) + } + + /// Returns `true` if the finalized state contains `orchard_anchor`. + pub fn contains_orchard_anchor(&self, orchard_anchor: &orchard::tree::Root) -> bool { + let orchard_anchors = self.db.cf_handle("orchard_anchors").unwrap(); + self.db.zs_contains(orchard_anchors, &orchard_anchor) + } + /// Returns the finalized hash for a given `block::Height` if it is present. pub fn hash(&self, height: block::Height) -> Option { let hash_by_height = self.db.cf_handle("hash_by_height").unwrap(); @@ -766,6 +784,36 @@ impl FinalizedState { batch.zs_insert(value_pool_cf, (), fake_value_pool); self.db.write(batch).unwrap(); } + + /// Artificially prime the note commitment tree anchor sets with anchors + /// referenced in a block, for testing purposes _only_. + #[cfg(test)] + pub fn populate_with_anchors(&self, block: &Block) { + let mut batch = rocksdb::WriteBatch::default(); + + // let sprout_anchors = self.db.cf_handle("sprout_anchors").unwrap(); + let sapling_anchors = self.db.cf_handle("sapling_anchors").unwrap(); + let orchard_anchors = self.db.cf_handle("orchard_anchors").unwrap(); + + for transaction in block.transactions.iter() { + // Sprout + // for joinsplit in transaction.sprout_groth16_joinsplits() { + // batch.zs_insert(sprout_anchors, joinsplit.anchor, ()); + // } + + // Sapling + for anchor in transaction.sapling_anchors() { + batch.zs_insert(sapling_anchors, anchor, ()); + } + + // Orchard + if let Some(orchard_shielded_data) = transaction.orchard_shielded_data() { + batch.zs_insert(orchard_anchors, orchard_shielded_data.shared_anchor, ()); + } + } + + self.db.write(batch).unwrap(); + } } // Drop isn't guaranteed to run, such as when we panic, or if someone stored diff --git a/zebra-state/src/service/non_finalized_state.rs b/zebra-state/src/service/non_finalized_state.rs index a592bf3de..b7252b2f2 100644 --- a/zebra-state/src/service/non_finalized_state.rs +++ b/zebra-state/src/service/non_finalized_state.rs @@ -27,7 +27,7 @@ use crate::{ ValidateContextError, }; -use self::chain::Chain; +pub(crate) use self::chain::Chain; use super::{check, finalized_state::FinalizedState}; @@ -122,6 +122,7 @@ impl NonFinalizedState { /// Commit block to the non-finalized state, on top of: /// - an existing chain's tip, or /// - a newly forked chain. + #[tracing::instrument(level = "debug", skip(self, finalized_state, prepared))] pub fn commit_block( &mut self, prepared: PreparedBlock, @@ -162,6 +163,7 @@ impl NonFinalizedState { /// Commit block to the non-finalized state as a new chain where its parent /// is the finalized tip. + #[tracing::instrument(level = "debug", skip(self, finalized_state, prepared))] pub fn commit_new_chain( &mut self, prepared: PreparedBlock, @@ -186,6 +188,7 @@ impl NonFinalizedState { /// Contextually validate `prepared` using `finalized_state`. /// If validation succeeds, push `prepared` onto `parent_chain`. + #[tracing::instrument(level = "debug", skip(self, finalized_state, parent_chain))] fn validate_and_commit( &self, parent_chain: Chain, @@ -198,12 +201,19 @@ impl NonFinalizedState { &parent_chain.spent_utxos, finalized_state, )?; + check::prepared_block_commitment_is_valid_for_chain_history( &prepared, self.network, &parent_chain.history_tree, )?; + check::anchors::anchors_refer_to_earlier_treestates( + finalized_state, + &parent_chain, + &prepared, + )?; + let contextual = ContextuallyValidBlock::with_block_and_spent_utxos( prepared.clone(), spent_utxos.clone(), diff --git a/zebra-state/src/service/non_finalized_state/chain.rs b/zebra-state/src/service/non_finalized_state/chain.rs index dd4a289ad..5180a07bd 100644 --- a/zebra-state/src/service/non_finalized_state/chain.rs +++ b/zebra-state/src/service/non_finalized_state/chain.rs @@ -64,13 +64,13 @@ pub struct Chain { /// The Sprout anchors created by each block in `blocks`. pub(super) sprout_anchors_by_height: BTreeMap, /// The Sapling anchors created by `blocks`. - pub(super) sapling_anchors: HashMultiSet, + pub(crate) sapling_anchors: HashMultiSet, /// The Sapling anchors created by each block in `blocks`. - pub(super) sapling_anchors_by_height: BTreeMap, + pub(crate) sapling_anchors_by_height: BTreeMap, /// The Orchard anchors created by `blocks`. - pub(super) orchard_anchors: HashMultiSet, + pub(crate) orchard_anchors: HashMultiSet, /// The Orchard anchors created by each block in `blocks`. - pub(super) orchard_anchors_by_height: BTreeMap, + pub(crate) orchard_anchors_by_height: BTreeMap, /// The Sprout nullifiers revealed by `blocks`. pub(super) sprout_nullifiers: HashSet, @@ -523,6 +523,7 @@ impl UpdateWith for Chain { let sapling_root = self.sapling_note_commitment_tree.root(); self.sapling_anchors.insert(sapling_root); self.sapling_anchors_by_height.insert(height, sapling_root); + let orchard_root = self.orchard_note_commitment_tree.root(); self.orchard_anchors.insert(orchard_root); self.orchard_anchors_by_height.insert(height, orchard_root); diff --git a/zebra-state/src/service/non_finalized_state/tests/prop.rs b/zebra-state/src/service/non_finalized_state/tests/prop.rs index 0eaf732ef..48203103c 100644 --- a/zebra-state/src/service/non_finalized_state/tests/prop.rs +++ b/zebra-state/src/service/non_finalized_state/tests/prop.rs @@ -476,6 +476,9 @@ fn rejection_restores_internal_state_genesis() -> Result<()> { prop_assert!(state.eq_internal_state(&state)); if let Some(first_block) = chain.next() { + // Allows anchor checks to pass + finalized_state.populate_with_anchors(&first_block.block); + let result = state.commit_new_chain(first_block, &finalized_state); prop_assert_eq!( result, @@ -486,6 +489,9 @@ fn rejection_restores_internal_state_genesis() -> Result<()> { } for block in chain { + // Allows anchor checks to pass + finalized_state.populate_with_anchors(&block.block); + let result = state.commit_block(block.clone(), &finalized_state); prop_assert_eq!( result, diff --git a/zebra-test/src/vectors/block-main-0-419-202.txt b/zebra-test/src/vectors/block-main-0-419-202.txt new file mode 100644 index 000000000..634273eb7 --- /dev/null +++ b/zebra-test/src/vectors/block-main-0-419-202.txt @@ -0,0 +1 @@ +04000000a8e2c1b3b95e3327bcc45b2ea6c6a664d36142b201a72eaa7f114d01000000004a847014db6f2e191d194851601add0c413a28f03c329cff349c2fe84883ad9c07a1272df3baacfd63abf64b97d26d444c985a8f3985b91ef08a3c29893f3954c06dd65bb2da031c000000000f000838132a2d00000000000000000000000000000000002d4ca99cfd400500332a3a2c419754d70f75abb0ec9e55ba7c134d963ceaa3568365146d7497d51b97afc1d1f817f4e76505d4232c29e66f1b59abe22c9fb06b26d47cf94f6c21a50e8340a78589cace323cc369a37a90827e5da01155a4ff77911645251604c2fab293355e76b8bb5d66769473cbda0999e0f747d8acc9001ba04f9f70c11f367a57e7b8d097e19dd322daf9e232639bdfe7062329aaa1200e49f7c89c027b3cd753260b047e9d890160228be48bbb83c2d7339bdc3c0e11d1ec752b7804a38e601d64d295fcbf947476c3d8fd20d90f5a900b90ae24a004953f5e5d232a5cd9012976bb34c01215fab41fca2c9dc5bba491a55e1802f9068933ec4e03f0579132605e697dcd807692c8faad2f922b3f5b17056ccab99047c0f15f462758fbb1e638c91af2d5065c3465d3020e729d633295a03e9d529c145d271e313b4782584ce426e330f7a1c26867ef515b7e85d301c2a5b4089e6e1133d371d1a2f5d8a192385f71bc3f94f24af66167e9b27fa5d8f9c234e34c01fa93f506754c3d6cd0b5137460d71d7dd08dc2ca18572b5263945d6e4fda6481de03c710a863f2bf30f95fefea0ae4a5a560492a91b86c741f9dee08099e2093ef58124bdb5d800d9cdabddc44b63fc4c212b526977a9d18c72a08b25fca8afef8e2a309465853348f3c5fba30c352c1dc30109989d5f3d492dd5c8620231a2ca80a8de98cf48ef1db8613046be65f9eb13b4eb825ca1d77a4bf2bcfe8c4e8a4a2eb636fee02ec17bde21f312c1d7a9870d25fb63865dd736fd32da68314c34853558551bad6b3b30828f5f796b4a00d97e270104e1d79fa0405102ed7bf3e23ae7e39900524f5b22f10650b176ae95cc1390996f7abd0fc4e6a0a20ba2b2520a5672320214b2d8a1a57ba03ebb88af5925c3093487b77472c1dafa2fdfc4798e1ef733b59d93efa8400c8ef642044e6035727920bb7bf7a4d73c1d1f4d01030b37d26a44b5f4d1112b3d6b14aca5c5b3b935010b54d8d12eee3d9b06212e756e774fa6a43b48d3027a9dac0b9543956fb96c50d82eb467d60f7912b0f00d87cb236da0349dc28bad40dfda59b07fa1ca64d1c448aeefa4b540b5f0004f3fbc6a8cbb3223f33162eb866df72a60aa7665c03cb802fbaa397f19f20c24c6b33e7abad0227781579a2156f43ff29799aab74016b420293440ccbbce19400d72359517b204e5b0f0aef64777515464f4c0b317fb6e445111438b680b504657abfa506324e6dafe1d428b6eca205005eb0f514079763b7c7669d8e17e3e2a54fc464fdb410b32809e021d6a9827871b69fe71b94da16422ed3197d851688ee9cc90ec6ccd32e4316a9deb1e5a91e754d721d6d44a8e10ced731a40ca441b620fb7751f5caaf050e07be9b1f1d5379c85d930237f1863154c9ee8d1065326ae4c4faf30969fd2fd3198b95a47e298bc6c5b2e17856d75ad61af0cab5c2ae69e3b8bf25d9fe10e18a2003b471c33006331d3ae2e0c525d455af59a433925a1c3a4df1b96ea36400eeb8c9db8b3d117980f04394764d30154d17748e9966a9122d3091c51a72b5784119614190b6c3e876982bb85fa5180f9e61424f4556584ebf5ebaf44f3d70c7dd79ea808f7746d26c2071aad1c9c99ff8264bf3f69faf613fcd28bd60774342e2425d0476e514554aeb4a02d5d064c5a6b16c347d4d597edf8ec9ad47628d0d415b7d1dc2d641b4cc4a5dc4eec32c1bd1ac16c6a55db67439ccd79367ff7a0256795979da3456d4b47e9797c682c24430ae9d69c132cedf98716b436a84e80f95030ecd2f71eb71236a1a2da13279d2962e467a29329049fa95c31eaa7248923764deeff87a67d7ba2d244acbb7e2c3eaca3daabda448b69c5857fbffe88b9ff7fd38555040400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff2a038265060004c06dd65be8fac706e1f7159b0c99adaa87f84cdc55e86cd0e4b5f9ec54cd169cf327df07ffffffff0220189b3b000000001976a914de2757e612dee78a681ace9b8e623570dce776f588ac80b2e60e0000000017a914e0a5ea1340cc6b1d6a82c06c0a9c60b9898b6ae987000000000000000000000000000000000000000400008085202f890598e050d6ec0b13e0cba86c730f60c4fd41d9be95d2a3fb7c220c736d181e92451d0000006a473044022001d13c2495c5c0d77c4ee3f8bdbe0cac989503d54b53d01877d12e456eca8c1202201a169ca27f1e886133696abb9ab031e98a7f8c48068c7d55e8300db3a7a1c192012102d716a0036c7929f1aef43ddc205b14a77bc6aeb7c3fef940637bc3043959c79dffffffff991e162c0275732906b593ae8bbe67d8228aadc4fee177cbf2c207b034d636622c0000006a47304402200727d8870e659c147fbc45680ec503f82b0462a18599fe823d3de563c2b514df0220091c7ff586d372c5af812d899064b69e6107959d6c085e71d9b9fb9c011cac3b012102d716a0036c7929f1aef43ddc205b14a77bc6aeb7c3fef940637bc3043959c79dffffffff66554c04740174ebf6386f92633cc6a51b50fd6627fe7678147ce5b5818f3f99240000006b483045022100fb3943d60194e9f8d42adddd5459ee446345a72a4d1c8b83f27c3f933f62accb02204e6a47318af6bd0f97e44b9c9c3c8db2604b51f289da760d40175e072a895532012102d716a0036c7929f1aef43ddc205b14a77bc6aeb7c3fef940637bc3043959c79dffffffffab15e39afe4ac2cecf8574b0a565e38ebea972b599ca51186369411b102a9f57250000006b483045022100dc777ad65a93379c5a75121c817a45138c810ee0b7846186263a314432bd43dc022054566fb73f6b8dbd10fe736fa2092853c0f440b236941e9d35e33b3de9692b58012102d716a0036c7929f1aef43ddc205b14a77bc6aeb7c3fef940637bc3043959c79dffffffffa3660773b08ef27ca83ee2d49da9e414f63d81034f7d40f302e286d69fd70bcd4f0000006a47304402205502b7e0cdba83d5cad5c68cf6948d5a156e9e074f45012d4adfcc1932c786fb02200b59b7f78305479596a745bbb03c64489119e991e6eddbe3120fe9f44bff225f012102d716a0036c7929f1aef43ddc205b14a77bc6aeb7c3fef940637bc3043959c79dffffffff0262e6fb02000000001976a91418e9ebaf5c31b9c52545e166cd9b999da30eb21988ac64000000000000001976a91438fef011da24095f9fcaf2a8be83af8d478c753b88ac000000009665060000000000000000000000000400008085202f8928efb99eb485e0d66d8dee937c66b76f7b82da910e3f04196bb7d45e065ad6d514980000006a473044022070bbbbf018ac713df9ae6caa0d3c01588a9187a565ed54e495925c190ba14fb802203c2847e11dea9821c5a19a49d400beceb5397b9319b176bd437952496ecf444d012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff9c2e294261577234e786a1ab1cdd9005a3ac65e196593d964c7b8d66a2bd6344cc0000006a4730440220663d0e741f56b93d37d07d17e369c5033b798ce1a2092a7dd444d5494cf9ba0c02203f954e93f3fe5b63878803b7e3a102561e6816718ec44ac4c58957db72c9b0fd012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff0333dc7fe26b87d9e56282ea3ab4cea4d84e6316afc715eaf9bf7e94abcc1b21a80000006a47304402202e8799407b8889d736b0b2d35a9dea6492bc909a087ac0708e050c43428a006902202c46aa502dc4573899984c4b2a65f0433e0627163e0af4b646b538f0e7dc33ca012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff01af4f514d6c7e8eb78f9f0c6d321b247d95b7bec89a44eebe61779c8ba7290a9e0000006a473044022056ec2d4d20ff0823190d84c06ac63087335fa2db2d6a14cc5e2f096aebf8f85c02207de68a64d7540da4f4f81be276fed949dd46945b6e1edf557623c9f7ce980783012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff94e58e5aca877996299a1d83b02397c77768adc6561d599d78478d968d2a9566be0000006a47304402203d3887b570cb3bb25e79755f4df3f373ad7e1990f411937c3ef40eb5323783d9022069cc758fbaa1424f48dbc843dac9d082752197119b4599826de10a77a0e54d45012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff79d92be1608033bfbff267ec4f15e01d3a32ad22541976d0f9f7600ba228e711b40000006a4730440220469858b1706bbd75e4040b966c921dc865118ca591701f879c52e9e841103f2002202a95324fd166363b1bad11f6161f2b5be7a5a99c80af8e5cc25c757aecb10a81012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffffb1922e84e0cb91fbd0bae2c92c79ad9c7de4d92cf5046525278d9ae0af9dd795bd0000006b48304502210082ea752ad601b8aa41cb1ee1580587a926b6fcecfdb0da6a8023aa0a78ae3f7c022079f059cdf6a28fa41e67454e3646a04a05555ad7eeddf979e35cb2159feb7345012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffffe587e860a070364484e91578162d12c25736f36f482b5c159caeea45a74a7588b80000006a47304402203050f9373496eae04ad5f9079454e483c00dacdcc2c4e2f09ecb8faf61cdeb9b022051df4849cc7a3ee41e610b9cd32d0d06173697a518a5498b9af9be9648ca0ab7012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff9bf11b99b422b902e5c8d82b8189590ebfb61685336acbb1413c3bb5d6e9e527ca0000006b483045022100ed65a7015b84d2ffb32005d03b4542ce8e3746cb97e77ab58f9127f8ca9a01ca0220549c93ea0c3c445bd8d749e6950e0139326c076ef7e1ef3df566f45b94b75d5e012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff46dd061975bc773741d7291b654bd1d17834d888deb601974108f92a00ddf70cad0000006a47304402206a72a13350bc24eff7792b38e12bee498203a3a449c224879bfcd6148c8123e70220363b810ae4689699cad7389cc72583073e20af17768029c0dcfa0c5cd55458b6012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff7d67eca0fdce81405b6a576fe57a48f3d5c22cf65377437d21e2dc2b3b33f6e4bc0000006b4830450221009dd6e3c1950304f52a52185095a6e23513c0126f597c0481884a50409e02e2dd0220031a46a348798ed8dbb20f39d4b9ad4a0a8e96a6c82f9fc47d56f974d9925d42012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffffc13c103577f5db5d02d8d1761a9c1273318708dac42fb9ec8c1cff3155bf8db5a40000006a4730440220457377d670ea6695c87020511d3b57244dec808795bcb93c6c4a9bdb662bf24b02205639a72e254f9c66cf28db510777a5ab81b15bf9ac84251e5382df0e44d8a318012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff7ae9590c82060fe31ea81cdc70a8d60e54f1d2bd82679ac764c4cebd4fdbb1a7d80000006a4730440220690707817bdf4b2f829077204d94f383aa2d4abf2b08c34953feb8f789113ab502204e15dd85908600d769d4a11410d052054a37f314ca5f875e1c4a59a5c4fec100012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffffc6aa0974cb83b7b399bd91a6150ca6ceacddae4a19f672e12f7286a9453f5953b00000006a473044022019ee8bf721f858aad86b750c61a158d7cd16babb0b8e82974a8ae746b751f37a0220101c222d8ffe53f13452e02185b91fcded99f9113cecf208fae5b170a5718cca012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441fffffffff91e095c0d4d9de7fea852f7d03ba1c45c94acd781d508336cdebd4635211d65a60000006b483045022100e3313da36c619c1453822047402649ea44e82c9a65e1b9c9b2f3f090b26d529f0220568d759607315af44d4f426948d277813fc522fc7e103f63cc7bf74a77a6f70d012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff8af28b1ddcfc73d39e2cda3011a16694b184d0e7176d24746909381c46abd980a20000006a473044022032900e577da6f4083c41be56a1ec7c744f74b5f4e6b6d977c031fd5614938353022002122c840e617b3e6b0c64748eb3c411638de351dcb1e049883b850634b38e6f012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff8dbd34737170c7b2ab51d3cf2a24cc898711e5fdcc32614caddfcf15e8e9fab1a30000006b483045022100c26dd7af9e4ce22fe61251a3579c0ff44cb125a9ea5e98fb64907f107165ef950220493a1d063e9b9a8385e8c31d2518123492d424baa4df28c61d47b96d637cbe3a012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff2f106cd2db629706c39720d691c0a23f333fb78552d5802b5b2628c8637b269fbb0000006a47304402204e8b0fd677dfca337e36afc35995b1c05a8bdb592406bdba213af6fc43173af60220551e3e6574401f9dfaefb5d91a4de1626d9c20991f5ac32cc635f3d0f0dcb5c2012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff36f1da5c83b79d87eccf5e17dc82a86c1223c42bb5398c4fdcada2f7b3be0321af0000006a47304402202320e8824637797ad92c0882c81c2b55f9f3bd1719b75ec34251a54e47d1e05202207108711762ab278fdc8a9fa8719d206e0c7545a0ec2a8ad0ffc598d489d804b7012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff6e6acee14caf4078d4632f748c65d95c8bdc4c22519ad35c0b065b14c1cfcf06a90000006b483045022100d336281a241dd330b1a5918b4a041889b553e289670f199ee713d782ab83ca9e02202597e950771c0d9cf0a39df7ccd1386433a8725b9378f6c985b013ed6e6d5416012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff13a5e2fff34236f319f3a426e55f260d6887086d2f388597c14d316eac1df2219e0000006a47304402203be9cb2348f65ed835d4d8071903ca51ebcc6f45d911f3deeda391317a9114fb02201c6a6c27ec812f8a0de6e8bdfc1238ed73da8771db9c24329f30475133161e51012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff6dcbe8982824d523b5b892e7ec11c466dfd08da1f8db8964719d8dcdb4e3c1ce960000006a473044022074ba99fe2576bf143ac635a2d067c7f34817874d4e3cceeada345f0ac3767f97022030835a9a9327ef9a723da53892968af1839038fedb9c8eac3df0dcd0a690c594012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff658a7ee92072d40f8f10b62aad3495a307c321b920ec987a419d5b1d13c19c3da40000006b483045022100f4b944bedb95bdf9183dfdbd1a3cc1abe3240341e422e689bd0c89ea01c3260a02203026a6cd94b3ac108a4937012e67e10d82b961a3cf0b5889afaf8ed9471fe405012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff7fd7793d1f05427a2a31805b8028060c9c1cf4caf3be701f1e5532705cd4a92caa0000006a473044022044c2343d048eaa8f9dbf3d8bc1bfd280391792e2bf5b2afa302f9f6a4aab3ad6022014ab1bb0ac7df2e16ca212bbb38d8f8093c63823cdbb84aed3a73f71043acb73012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff0858aeee9033fe471f76d54278c61abd0d77fb596f80c9c2d0a487318355dee5aa0000006a47304402207e2988fad2932002ef551ba8662c3a48350f24fbde98bd47d7335d5c4c653bf902203dea4e9f30ef7267b9a22984fa1914ea8148811db897e0ba0a5ca261d0923076012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff6c113cdb2981597164b26c9cbc8a0a16d3e6cc804bf5a6cf2cf99e6649fae936800000006a47304402200e82aa729f29b34d428b11c84ca46b32d1f5c3e2b36d6627a324adb27820718802200893e7215d925f4b3d576378e824e65cdd6420f780ff1b73464d151fdecf4f56012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffffb0e652ea04e43a57c93cd3ec42508b1b9c433b03e62158778cee861f81069b60b30000006a473044022036d137feeaba6d16da17a3d30cab0738d7b2342eeddcd538d65f6bcdd2b80cd0022015cb995c09ddf113b64fb2597fcb585eb235bb52ade9749c06a180244be08502012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff2f05bc54a7e65587123832d72550b634c46c7a92bdffa79af3fe80698ad13edd9e0000006a47304402204684fff5f3e48e72669784a92887bfb450092a258d814ce3838140164281e65b022051be838ae002500f1694d7f43062ceef3cf9030da1c902d7b37698b6e4a9a2bf012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff25e306063315306ae1352d88dbc12d4e4f53fd0b550171dd696fb1c98880a445b10000006b483045022100a710d66faee0aeb6ec1b1ddabb12353c3f70914390d3dbb933a862b695c9ea6d0220711334c94d4b28c9d053f8b4b23bcdb54dfbcc84f5bbdcdcd0adc8113f146c2b012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffffc119228c08f1298ad27198c846421b89aac57e60b003088eb74104c029705110980000006b483045022100fd4f0837afd1aee16174b083639939768f42e311c491c3c68f7640f655ba118502206a7b84bf91de8a37958344ccd50932afe258ab6ca1327bb7757aaaa8a15b5756012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff67195acb2f2a24bf8ef5e38150504a1a87644b95902b33ed48c8727713beac04ba0000006b483045022100bbc84ba528d74b6014d1634bb2b418d1ba464dfc13fdaad491784ae6673d9a9c02205c27616b728e3692ab156c3a886d1686fd2566b09db76e7dfcecc18cef4fab7d012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff5c51ff192ec5be9701430c1ed7c568cd3b0026e9f507e6f75ae9d4dcb75c6cabb40000006a4730440220695976660d686250e007f2a670befed65313f50b0284b1f678c31c03be181b7502203f2dffa8889bd035144b2a027aec8745b0286f3c678f79e4d6d265ecd976e66f012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffffc966caa0e58a4403ad43ce85ce79b97cc2a7ee84b2fb7864ee4614bcdaf8d50ea50000006a473044022049d37d327d7fa6c459fd51ee98c0fe606ec85d6cfc8425fd88643fea7ad7cf6e02202c4be6617b14cb38ea6371d93ed8b5b51d4aede4fc17e55beb82970cce3f4e5b012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff87437fcf10efef14d3c946fea62f1cf7c9232df549e14ac8b81d95fca55c8931a80000006b483045022100f1a90137ce83debfa2de6d9d98c16dd5dea17d6ac4e41ceff3d54b5725c209f602200a6d1a45ae9836657aa12680a7d63a143d98467d3d07ed05d559a67fa1e022e8012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff5f4c5bf124ffa6e4d521db95fc289511cb98f14379b4c2bc056b673c6fb25804a20000006b483045022100c66ad370517204faf74c048e84ddced276fbcd203883f469777692fe1ade450e02201bc91eddc38a8688a9979d8f60933dc81aaaae2cba66c877e391b82fd9e645eb012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff80497f128ba33b5feb9d5573e065443c7c042e319f31406d7384d0dcaa4db2baa90000006b4830450221008f468ae67e220331733d14bc6027780b13d0016ae748f59033b4b0273d904bc902201705f3cebab02e1fe6cf57e94981c59fef38226ea940a34768f20a4ddefdc5c0012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffffe79cea6e8b0b437bfb8cef2bc46f51dfe0a6ab09201120d64ceb01a2a21e76729d0000006a473044022072d9b9a8e76c3ffa072cd8f1d4e421a0fe3a1ba5526b3208b99c30e10dcaf8e602204d238759fc01c11b056b0aaeaefdbabb89fe3a4442235e99edf75f7a5b1bab06012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff3051cbaf0e5aac43c8c2b3dcd0ba90278a5e4c78bcda66e3c4a0b1e3b136080df10000006a47304402205572a516a6ac9e7f9eea5b53512742052bdfbd5ee222cffc5ef7cb609f8a4ccd02205a13916ba5248760e0b81669e90d0a1ad2e76be71031ad11f5f3357860747b44012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff517f649bb4752193d2090704732efad98fab76f0c7d87d36b3f026934a40b333c50000006a4730440220695f5fa2385e123bb8d6191bec941ce0c853e66c8cd127d5f307cdfb673e09150220377812b9430cb90fd0b1a28d202091f8131acb6f4bcb1072f6103e344ae25fda012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffffb680d4c633c3bc314f7f8e8db328c68d490cd778607bcea41467fa85d30680e1da0000006b4830450221008b3005c2ff4f032d4b9a9e708c24c8517182fa00df8f9d54473e54e73de04ac3022029c6f5e7457a8f64c0be8713e6e19e8093be3b4919a1204ea76346e3b1797b1e012103c91f93e3af970a8343c3bcb05826c57cd6e092af08661e0695cf603bb1179441ffffffff000000000095650600000000000000000000000102c7de090000000000000000000000003e55eebe5c60dd6aafb9fef2fdca323e67226e64cefdf9f514ad714cdae9f6d436fac2079827f5cbf6402d5a7815f7a3e36df5bbfd5191569bb3dbd9408d82273b44387b7346dae88ed11df219fccc32ed40489e710983b0ca4b775243c3a3bc151a894936509445cb74e8e172549c2635b7e9c243bc77c9b0e49668583e5fd10586087b51ddf633f582a9a6ce04a7f11683eac0ece1e5a8f3a842a3decc442dbe6fe6828630ea644fdd82dc94f65e3b0da5b1f64d96c43837b7745a7b7beb12787276a39932a88e00b94fb95651af41b307b26328d43fb19aab97480e713a8493a8b337e3000b1480eab3fbfb311d90c8ce439fb8288fc112bbe70fcaaeb79e2866bbecd61ed5c744d53d644b5d7805889736280cccdd1535446a4f07f8d4d0a234b504aac13cbfa6c2159cda97df03278caca9b44b482f88dbced1ed8ca0ba14043a8456e438aaadcd0b872047afb580772d182fa5231dbd7f8ac15cbd16147a70b327054491cd7c1d10af3d8a065813e64bd6fdb2ebcd075e0a798863e5c116bc219fdb29e05685f314f106beb8a5e814cae90a61c561a8fcd7a9eaa002a8c46b2db9fbfbe7b83b59de6056614e658304f109f018cb14a18b589e571fecbc21746e79d1a037c502bc6dd9f3a0f35b70f07d91bb4789eac84dfaaa6bf9f64faea09043c1a39b26e313c94d8d0f2f259ce671680d32d3cdd4678eec8f8917e5d52ef9055f54f5ac34a55be0dbbdd5408335ee2cf5306b5996f0f94433921e941dc1af0798b7636b8a743d104b10fe330f0c3bb5218182163f1439830b25643bb3d2f309ca5e6456577c5ced96922d9bd7f1652f20c3d572bb2bb24298970620d7bffc0ce437ca6521c51cd2cb7f917b6a638ce8dbbd367f7cc1ddbcaafd26773ae451c9568ce4a8625c708f4e15994228ef467f046289503e582a934f21b3a07b230aca5d7b1dac1b797e1a7d33cb740377290af429f40ad520d6da6f1253edb274b61b73be5531be45a49eb21fe664272df7faa95c0d2bdc3bc45af4f9131513df5d6d7c0afaac692e2e5105c79dfe5baf44883e86a5e31b8c9ed87e456b459dbc60626a8ae48c0014861afce9f2285288f7b09c09e960eab1fa25a91d1bc0f3cc6c65a0c3b08a0daf04e89a8e93eb5f305dcce14d15edab034268af31008bc36c262baf0dd25583a36a57284c4d3822c41fd9656456e192a4b5a590336e4f8d0ed5180858eac5548c4e74663fb57dbc00ebb56ae8a2f89fcee28202d84072ad85bf6512cb120850c6707141537b3a2f5cff6d9a988c953e2b67ec8f9d8b905e4313a237d7e35c06497eecb1ff9c671888c7938b5294a123c7584d846bfad2052c549fd5c56a26a09c537a06ac5136f34130ae553f44ee046b46d45aed6eb0f16232a8a9659cfba6802c527c602b1a8a28ae475449e3fc35a9a12a12afc110608084980e32a3bb874e30bcb166620132b7a1972f59c7495e4a3e1ef9b698ee9c2edcd899f0a24ca5eca756fa2768c6d58e835a85e0cefb6a9da7ba6f88c017be726fc6dffe423c8f9debb2aeed0955a68ff9164ddc9483a4a7910c3e329877e8a4ad6726890a34aa6677198dae3c018cfbb23f7869a6e90adcff2acc15811a1444409d11e3fb9913bb2294781224cc634488a9d93ad38e47320024b962884715c4ce1a378f13450e8997fd85c162c7d808b1f04da5ebb2eeb9d084184dda74c636fe477497d9d25c5e69ba30bc108000ffdaa4b806eb3b5f54edada3abecfc1ee3aa43273a539a0002f451e05f520b85a28f2c2813dff61f42b614246043a57da250a6ed04d471c99f87896c4183dbb2c7c35ce13e812cd81f3e16ebc2a7101f7652f5644338b97a1d03f2119217b113ad8fc6a957e97209cc75376eb8f70204b092cab833d78f5fe7e1038b398ebe2f248f378ba92adc8e0bf3e3caecaf4ed2110273c76e1f6cb04843c395ea1a1d25f7ea2c5336cdbeb29b4f5138b4089c018f3a951d5d5c3e584d59dad3dcc018c42f4886e7fee14d3c3c30f831933a0b40b3f57b958cf926bdfac99d876b63a5b44493debf00c3edfa56b275b341997f081ebd56b4944e6c1cb057ef1994b2c2d47ea88af0d959d33d7de1dd8367cabf250fe4640a7ff4523861a6fe1c0ebddb4f56f132d53f39db82f3a427e67836d65fceddeae1f8442428aedabf28a56d035e5415aab1b890f81248cbd1ca7040f039530ebbb6a4a175ae761a6f5be57f8aa23f60a72d4061c47c002e1a402721a40a45b124e507815f46cc964a4c358f21666c08b2918aeacc45a724627ba8e9efe93bc2e8244b5846061eccfbcf868597c8fc0f9b8b5ac74420d8549a3c00d455c58b6940c0f921eb2236b3f3ddf23212d7eac5d555a4c1b45aee6e0f5c025ac605aaea0eae1c803bffbe68f6db18ba5cc1a3fb72cc9ed7d038c3a1b70d89b90c180f9b982e2f34ac893335b3c3de2290ead64a89cac174190a0bc1daf6351b8c665986ed008496491c0ccc2e684838f6100e0400008085202f890000000000009665060010270000000000000148eb5e67d68a2e9bedc32cc9dc492fbe9c750dbc2f064bcc1f068fc677c67bc01b42f737a61181927774bed90e5a601cd7321baef3a4511c92b77aa35b7e8d632a4f54d76b11b6373ca54731acfea1194d71b951a68b31c8f41998a180cdc60173e6808bdd9adb8be2a35609cc58924f5f96d67fe4fb47ccb10b110ad0b8f90c81583c189bc2afca5b48d05ee30b0fb4973d8fa2d1d3b5c89ed4be2059a2145da3d7b885da05d0b712dacd6f1dacf7c296d4b53ba55a590c12307d978298bd244253e3ff4276451e9132c320395dc6a78ad52d1a8605e9b6e6848958b8c2d62c0bdd157ef87bdab1b199814dd150070dccd15765a572e5a84b19535a5d833b9c98165845b4b459a12bff67b9e45e88a9b314dcadcda2955ec8bbe17d37775cd0eb43478e5785d10c037383a1fbc84d70bc637ef9f5d1671dd13e0a7c69dcaa90889fa4ed1e2eec57060341cce90dc8feee7542728a856fc4df622f38f1e0c6ab736a1d6f43efffcdeb4d09a053b89b7f0b778a9591315bfb154e62a752a1800702183a0a498e53e0c3c69ab979cd0012219ef119f2ad1e7c1cc3c8cd71a1fc4c8c02fae8542d3df178421981d14b12a853d0b022b7c6035d716116e4fc3f7b7847cbd670a76de0d1ed11a21620489d97c5d01bce855c3dcbc925f3f998a805d78eaf11266bc2b44356da0dc2547b6576e6e3f43585d814942fd5bf3e5dc6ccd4c65e7dde3ad6c6745580956423d999b1dc4055d338a6ce93fa3fff649fd93fdfdbfda48108268dbe9e92362486835bf204eaf1b02c8b79dcb754ac3a571ca6f28b11edf616aba60f6ccf6a4a06b0fceb4cbcc764f4f9ae0c1661ea9cb69b8f6b4fd3cdca0f9c2d2f6de998810ee03792d6404ca5668a970811cb03fdccca3d60acc79279678040468e77ee5818cb7e400dba077f36620b5921d0274685c54406edd0059fb127230ea15b6dbed4198c6d996c92334bf8551edc80e7c87413fc1630f1fbd4b47a2d1d5b8b8579e5a30f39a98c993494904543618b33b977c251636e62a94fbd4b142d6e05ad11c36cfbce59941dd5e677aade47e194a464d3accd4f329244aacd5a9364773d59f93e0281096afda2576e27cab2d276aa07220c26bac60e2630ff3c858a8e3d742ec56e23422f5e57e67fd0eca5b7c4c721f06424d0606118768b41be62c5701994d144c44a3a291463823b49f0c221d5285faf5bf1ae73727e0fa53eb1468bb634a3646be7135f94f2fcaa9153fffc1a15746de474e10c1ba39383a6c7390cb5176dd3c0ec709519d16afd7083c608be44800337266b79441ea295a2ba40f2689dcfe06459a23ad3e97ed0973d4321e3147a0e53772fdab88ee28529b92f991ce0c948942c61eccf06e4000965a9f6d5d67a40ad5af06853cc690faba950416e169baa609f615c7055f1b53b6dbe9e129e9845cf64574f96a9145baf3a738e1196fcbcf7131417f5bcac3292530b0e4fbc700a8c99160e3717c2c1d4affcbcf8c35a5919aab910d328e8a21c852a4a299d5ec2f4522e29784a492908e03c839bb610e2f5275b853938fb48e49b5574fa7b99aa61d6cc3c804f7bd619abd1d4e75a938e2b4833f0cb0a8d2f0e553edf5108350c79db3156bbdc746943c11e4780c7596b77d362922eb2e9d557b772071c99c88256bbaab0beda89e1225b040976a810c38766a75e9939990337469307663b2dc018fcca679801eac103c5690b8c6714ce372eeec935f60980cb9f7ae2e7b6410c12f09af8b131f6a15851e9684b976a10a48bdb35beb19fffef1a768c1e054a4aa24436931f278348132df46538d89c574ca9ef6ab8ec7611cf7d6c63cb874daa8aa70eea4ac9406f21adb82979cfb2bc25a3f5ec0e0aad8b8477d4154a9e3f6453ef736f5699e24cdcb0c4ec556f4e4aa428477bef86d9bacf5f5cf90f15e1ca8e285d72f3d43bdb0acae57254b678f74980b0bfa9441c74e1d70498c96c3083b53bf218c0677342584cc70f396d022deed187a9ca4e2b8c77d4041c4ea0e4235500f5ed7d2f25f835a0d09d9defcac86f9462ec12cecfe560172f0458bcd0024c1c5ec4be5a3441c7ede9f761223e9b9b4f64bd233157de997d34b29146fca07e82f8b63c5ba85b1824cd50c8e3459c8812a6c0070b5d053bd6de5fe5601b4fece154c5d0ee7d48e5d851210f94a3d31c1b848bdae7fb5da1e4541f7cd1d3c91c7d54b8c73d1d66eceaab8f9cf0ea70833a68daf2f29128ee89be67331581228c02238fdd02734a6bdc6019335305b952a6410e20f426eea9a432b7f47802d0182a00a4d18a35ec1f9abd40e35f7d3a256fa416e62a2c383e331ae8585bdbbd1b783c5adba7db043293dd45f84771814b48b76b910873863f41e432b8962dce3706192ec75bcf82fe6e4ac8e810a07fea54faf2e464eec20cd4826777b3dbc1c7c788e1687acddcc9b5a5bf6a2d23b4b053bbc198de4f81688c7e95766d86f2d2607bc4e9a1fec79f6c7c4793f9b595e6dd5314defcd401de1bdc1df6fcfa0fa9883768c2ee6daaa0304384c356311531de4d19deb140e3f965e4d42d790c0159b0a3bedd7a147874217b9869c754f5ed46fc8e16fbd38e06606aaa75314fb5463757c4e34641d19fafb6c7fbf35547b6e91a863d011d9a30a3ea484316e52797615ff7996736c2da40b723d18087f8ccc61cb5dd867b90e1502f087423a818a4e8dec438e19a89534bfcb235b3695e134529c883100aecacfbdc391805a9892036fb78b41f3d4fb428906f7d1908bc42341a820c3ca8529d2ed79463cd4fe39ac7d29202ea2ab551032f5d2893dd54ee35e0c5ab6ff59b602d5da5cd0018b20194921b56679a92d7a4bde1b394d665747a38cc3d93b184675a10328a395b81f8f0a49c847fed7cb50b47da1ce701da30871f2c5a12ecec0164d6f7c2a041906f4f30985c7dac23692f8da87f654296bbd2ed934a22730c1eff41261eff60ce716b0592bba24c3ae64ed11b5ddc128cef6cf4d4e0528481015b8c2d1362ac2a18cc54ea0f52bf940a78b0118695e83d2a2fb0b8dc776a413bfcbc308db479d57ca5c359c2fdd2e2dade152227f91e0dad43b00a4c7b9e5badaee1349018f9b9a3816e47bac480112776cab43ad78a62429d4d6447cde5d80d94b3f0cdb4c6d405698829932ea6f58b4b1519f8b50e4c81f2047299a3c6448be6d4832b115de898b128d12f54d43fc89a000eaa60bc89ad5da4e09f1908555e78828533e2b7b87759d3bd4c427cefd43f25d8154988aa0a775a41572c1a7b4bdd8d699da372298e99e076710beb13f9bd04 diff --git a/zebra-test/src/vectors/block.rs b/zebra-test/src/vectors/block.rs index e52e26e5b..deb60379e 100644 --- a/zebra-test/src/vectors/block.rs +++ b/zebra-test/src/vectors/block.rs @@ -29,8 +29,8 @@ lazy_static! { /// Continuous mainnet blocks, indexed by height /// - /// Contains the continuous blockchain from genesis onwards. - /// Stops at the first gap in the chain. + /// Contains the continuous blockchain from genesis onwards. Stops at the + /// first gap in the chain. pub static ref CONTINUOUS_MAINNET_BLOCKS: BTreeMap = MAINNET_BLOCKS .iter() .enumerate() @@ -40,8 +40,8 @@ lazy_static! { /// Continuous testnet blocks, indexed by height /// - /// Contains the continuous blockchain from genesis onwards. - /// Stops at the first gap in the chain. + /// Contains the continuous blockchain from genesis onwards. Stops at the + /// first gap in the chain. pub static ref CONTINUOUS_TESTNET_BLOCKS: BTreeMap = TESTNET_BLOCKS .iter() .enumerate() @@ -86,6 +86,7 @@ lazy_static! { // Sapling (419_200, BLOCK_MAINNET_419200_BYTES.as_ref()), (419_201, BLOCK_MAINNET_419201_BYTES.as_ref()), + (419_202, BLOCK_MAINNET_419202_BYTES.as_ref()), // A bad version field (434_873, BLOCK_MAINNET_434873_BYTES.as_ref()), @@ -116,7 +117,8 @@ lazy_static! { /// Mainnet final Sprout roots, indexed by height. /// - /// If there are no Sprout inputs or outputs in a block, the final Sprout root is the same as the previous block. + /// If there are no Sprout inputs or outputs in a block, the final Sprout root is the same as + /// the previous block. pub static ref MAINNET_FINAL_SPROUT_ROOTS: BTreeMap = [ // Genesis (0, SPROUT_FINAL_ROOT_MAINNET_0_BYTES.as_ref().try_into().unwrap()), @@ -129,14 +131,15 @@ lazy_static! { (347_501, SPROUT_FINAL_ROOT_MAINNET_347501_BYTES.as_ref().try_into().unwrap()), ].iter().cloned().collect(); - /// Mainnet final sapling roots, indexed by height + /// Mainnet final Sapling roots, indexed by height /// - /// Pre-sapling roots are all-zeroes. - /// If there are no sapling inputs or outputs in a block, the final sapling root is the same as the previous block. + /// Pre-Sapling roots are all-zeroes. If there are no Sapling Outputs in a block, the final + /// Sapling root is the same as the previous block. pub static ref MAINNET_FINAL_SAPLING_ROOTS: BTreeMap = [ // Sapling (419_200, SAPLING_FINAL_ROOT_MAINNET_419200_BYTES.as_ref().try_into().unwrap()), (419_201, SAPLING_FINAL_ROOT_MAINNET_419201_BYTES.as_ref().try_into().unwrap()), + (419_202, SAPLING_FINAL_ROOT_MAINNET_419202_BYTES.as_ref().try_into().unwrap()), // A bad version field (434_873, SAPLING_FINAL_ROOT_MAINNET_434873_BYTES.as_ref().try_into().unwrap()), (653_599, SAPLING_FINAL_ROOT_MAINNET_653599_BYTES.as_ref().try_into().unwrap()), @@ -228,7 +231,8 @@ lazy_static! { /// Testnet final Sprout roots, indexed by height. /// - /// If there are no Sprout inputs or outputs in a block, the final Sprout root is the same as the previous block. + /// If there are no Sprout inputs or outputs in a block, the final Sprout root is the same as + /// the previous block. pub static ref TESTNET_FINAL_SPROUT_ROOTS: BTreeMap = [ // Genesis (0, SPROUT_FINAL_ROOT_TESTNET_0_BYTES.as_ref().try_into().unwrap()), @@ -236,10 +240,10 @@ lazy_static! { (2259, SPROUT_FINAL_ROOT_TESTNET_2259_BYTES.as_ref().try_into().unwrap()), ].iter().cloned().collect(); - /// Testnet final sapling roots, indexed by height + /// Testnet final Sapling roots, indexed by height /// - /// Pre-sapling roots are all-zeroes. - /// If there are no sapling inputs or outputs in a block, the final sapling root is the same as the previous block. + /// Pre-sapling roots are all-zeroes. If there are no Sapling Outputs in a block, the final + /// sapling root is the same as the previous block. pub static ref TESTNET_FINAL_SAPLING_ROOTS: BTreeMap = [ // Sapling (280_000, SAPLING_FINAL_ROOT_TESTNET_280000_BYTES.as_ref().try_into().unwrap()), @@ -390,7 +394,7 @@ lazy_static! { .expect("Block bytes are in valid hex representation"); // Sapling transition - // for i in 419199 419200 419201; do + // for i in 419199 419200 419201 419202; do // zcash-cli getblock $i 0 > block-main-$[i/1000000]-$[i/1000%1000]-$[i%1000].txt // done // @@ -412,12 +416,18 @@ lazy_static! { pub static ref BLOCK_MAINNET_419201_BYTES: Vec = >::from_hex(include_str!("block-main-0-419-201.txt").trim()) .expect("Block bytes are in valid hex representation"); + pub static ref BLOCK_MAINNET_419202_BYTES: Vec = + >::from_hex(include_str!("block-main-0-419-202.txt").trim()) + .expect("Block bytes are in valid hex representation"); pub static ref SAPLING_FINAL_ROOT_MAINNET_419200_BYTES: [u8; 32] = <[u8; 32]>::from_hex("3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb") .expect("final root bytes are in valid hex representation").rev(); pub static ref SAPLING_FINAL_ROOT_MAINNET_419201_BYTES: [u8; 32] = <[u8; 32]>::from_hex("638d7e5ba37ab7921c51a4f3ae1b32d71c605a0ed9be7477928111a637f7421b") .expect("final root bytes are in valid hex representation").rev(); + pub static ref SAPLING_FINAL_ROOT_MAINNET_419202_BYTES: [u8; 32] = + <[u8; 32]>::from_hex("54393f89293c8af01eb985398f5a984c446dd2974bf6ab63fdacbaf32d27a107") + .expect("final root bytes are in valid hex representation").rev(); // this one has a bad version field // zcash-cli getblock 434873 0 > block-main-0-434-873.txt