From 84b2cd17eece70906ccb6a1226f00fd39bcdbda8 Mon Sep 17 00:00:00 2001 From: Elijah Hampton Date: Sat, 25 Jan 2025 19:38:18 -0500 Subject: [PATCH 1/5] Adds new invalidate_block method to non finalized state. Adds test case in vectors.rs. Updates non finalized state to track invalidated_blocks --- zebra-state/src/request.rs | 15 +++- .../src/service/non_finalized_state.rs | 59 ++++++++++++- .../src/service/non_finalized_state/chain.rs | 4 +- .../non_finalized_state/tests/vectors.rs | 87 ++++++++++++++++++- 4 files changed, 159 insertions(+), 6 deletions(-) diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index 0eeb9b940b2..ad5b834c4f2 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -1,9 +1,10 @@ //! State [`tower::Service`] request types. use std::{ - collections::{HashMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet}, ops::{Deref, DerefMut, RangeInclusive}, sync::Arc, + time::SystemTime, }; use zebra_chain::{ @@ -234,6 +235,18 @@ pub struct ContextuallyVerifiedBlock { pub(crate) chain_value_pool_change: ValueBalance, } +/// Data related to an invalidated block including the [`ContextuallyVerifiedBlock`] and +/// descendants. +#[derive(Clone, Debug)] +pub struct InvalidatedBlockData { + /// The block that was invalidated. + pub block: ContextuallyVerifiedBlock, + /// All descendant blocks that were also removed in order by [`block::Height`]. + pub descendants: BTreeMap, + /// Timestamp in which the block was invalidated. + pub timestamp: SystemTime, +} + /// Wraps note commitment trees and the history tree together. /// /// The default instance represents the treestate that corresponds to the genesis block. diff --git a/zebra-state/src/service/non_finalized_state.rs b/zebra-state/src/service/non_finalized_state.rs index 08d64455024..452b930afe4 100644 --- a/zebra-state/src/service/non_finalized_state.rs +++ b/zebra-state/src/service/non_finalized_state.rs @@ -6,17 +6,19 @@ use std::{ collections::{BTreeSet, HashMap}, mem, sync::Arc, + time::SystemTime, }; +use chain::UpdateWith; use zebra_chain::{ - block::{self, Block}, + block::{self, Block, Hash}, parameters::Network, sprout, transparent, }; use crate::{ constants::MAX_NON_FINALIZED_CHAIN_FORKS, - request::{ContextuallyVerifiedBlock, FinalizableBlock}, + request::{ContextuallyVerifiedBlock, FinalizableBlock, InvalidatedBlockData}, service::{check, finalized_state::ZebraDb}, SemanticallyVerifiedBlock, ValidateContextError, }; @@ -45,6 +47,9 @@ pub struct NonFinalizedState { /// callers should migrate to `chain_iter().next()`. chain_set: BTreeSet>, + /// Invalidated blocks and descendants + invalidated_blocks: HashMap, + // Configuration // /// The configured Zcash network. @@ -92,6 +97,7 @@ impl Clone for NonFinalizedState { Self { chain_set: self.chain_set.clone(), network: self.network.clone(), + invalidated_blocks: self.invalidated_blocks.clone(), #[cfg(feature = "getblocktemplate-rpcs")] should_count_metrics: self.should_count_metrics, @@ -112,6 +118,7 @@ impl NonFinalizedState { NonFinalizedState { chain_set: Default::default(), network: network.clone(), + invalidated_blocks: Default::default(), #[cfg(feature = "getblocktemplate-rpcs")] should_count_metrics: true, #[cfg(feature = "progress-bar")] @@ -264,6 +271,49 @@ impl NonFinalizedState { Ok(()) } + /// Invalidate block with has `block_hash` and all descendants from the non-finalized state. Insert + /// the new chain into the chain_set and discard the previous. + pub fn invalidate_block(&mut self, block_hash: Hash) { + if !self.any_chain_contains(&block_hash) { + return; + } + + let mut chain = self + .find_chain(|chain| chain.contains_block_hash(block_hash)) + .expect("block hash exist in a chain"); + + let new_chain = Arc::make_mut(&mut chain); + let block_height = new_chain.height_by_hash(block_hash).unwrap(); + + // Split the new chain at the the `block_hash` and invalidate the block with the + // block_hash along with the block's descendants + let mut invalidated_blocks = new_chain.blocks.split_off(&block_height); + for (_, ctx_block) in invalidated_blocks.iter().rev() { + new_chain.revert_chain_with(ctx_block, chain::RevertPosition::Tip); + } + + self.invalidated_blocks.insert( + block_hash, + InvalidatedBlockData { + block: invalidated_blocks.remove(&block_height).unwrap(), + descendants: invalidated_blocks, + timestamp: SystemTime::now(), + }, + ); + + // If the new chain still contains blocks: + // - add the new chain fork or updated chain to the set of recent chains + // - remove the chain containing the hash of the block from the chain set + if !new_chain.is_empty() { + self.insert_with(Arc::new(new_chain.clone()), |chain_set| { + chain_set.retain(|c| !c.contains_block_hash(block_hash)) + }); + + self.update_metrics_for_chains(); + self.update_metrics_bars(); + } + } + /// 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))] @@ -578,6 +628,11 @@ impl NonFinalizedState { self.chain_set.len() } + /// Return the invalidated blocks. + pub fn invalidated_blocks(&self) -> HashMap { + self.invalidated_blocks.clone() + } + /// Return the chain whose tip block hash is `parent_hash`. /// /// The chain can be an existing chain in the non-finalized state, or a freshly diff --git a/zebra-state/src/service/non_finalized_state/chain.rs b/zebra-state/src/service/non_finalized_state/chain.rs index 0dfcd585c12..7497f0a6e8d 100644 --- a/zebra-state/src/service/non_finalized_state/chain.rs +++ b/zebra-state/src/service/non_finalized_state/chain.rs @@ -1565,7 +1565,7 @@ impl DerefMut for Chain { /// The revert position being performed on a chain. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -enum RevertPosition { +pub(crate) enum RevertPosition { /// The chain root is being reverted via [`Chain::pop_root`], when a block /// is finalized. Root, @@ -1584,7 +1584,7 @@ enum RevertPosition { /// and [`Chain::pop_tip`] functions, and fear that it would be easy to /// introduce bugs when updating them, unless the code was reorganized to keep /// related operations adjacent to each other. -trait UpdateWith { +pub(crate) trait UpdateWith { /// When `T` is added to the chain tip, /// update [`Chain`] cumulative data members to add data that are derived from `T`. fn update_chain_tip_with(&mut self, _: &T) -> Result<(), ValidateContextError>; 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 b489d6f94f0..1791f8b07f2 100644 --- a/zebra-state/src/service/non_finalized_state/tests/vectors.rs +++ b/zebra-state/src/service/non_finalized_state/tests/vectors.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use zebra_chain::{ amount::NonNegative, - block::{Block, Height}, + block::{self, Block, Height}, history_tree::NonEmptyHistoryTree, parameters::{Network, NetworkUpgrade}, serialization::ZcashDeserializeInto, @@ -216,6 +216,91 @@ fn finalize_pops_from_best_chain_for_network(network: Network) -> Result<()> { Ok(()) } +fn invalidate_block_removes_block_and_descendants_from_chain_for_network( + network: Network, +) -> Result<()> { + let block1: Arc = Arc::new(network.test_block(653599, 583999).unwrap()); + let block2 = block1.make_fake_child().set_work(10); + let block3 = block2.make_fake_child().set_work(1); + + let mut state = NonFinalizedState::new(&network); + let finalized_state = FinalizedState::new( + &Config::ephemeral(), + &network, + #[cfg(feature = "elasticsearch")] + false, + ); + + let fake_value_pool = ValueBalance::::fake_populated_pool(); + finalized_state.set_finalized_value_pool(fake_value_pool); + + state.commit_new_chain(block1.clone().prepare(), &finalized_state)?; + state.commit_block(block2.clone().prepare(), &finalized_state)?; + state.commit_block(block3.clone().prepare(), &finalized_state)?; + + assert_eq!( + state + .best_chain() + .unwrap_or(&Arc::new(Chain::default())) + .blocks + .len(), + 3 + ); + + state.invalidate_block(block2.hash()); + + let post_invalidated_chain = state.best_chain().unwrap(); + + assert_eq!(post_invalidated_chain.blocks.len(), 1); + assert!( + post_invalidated_chain.contains_block_hash(block1.hash()), + "the new modified chain should contain block1" + ); + + assert!( + !post_invalidated_chain.contains_block_hash(block2.hash()), + "the new modified chain should not contain block2" + ); + assert!( + !post_invalidated_chain.contains_block_hash(block3.hash()), + "the new modified chain should not contain block3" + ); + + let invalidated_blocks_state = &state.invalidated_blocks; + assert!( + invalidated_blocks_state.contains_key(&block2.hash()), + "invalidated blocks map should reference the hash of block2" + ); + + let invalidated_blocks_state_descendants = &invalidated_blocks_state + .get(&block2.hash()) + .unwrap() + .descendants; + match network { + Network::Mainnet => assert!( + invalidated_blocks_state_descendants.contains_key(&block::Height(653601)), + "invalidated blocks map should reference block3's height in descendants" + ), + Network::Testnet(_parameters) => assert!( + invalidated_blocks_state_descendants.contains_key(&block::Height(584001)), + "invalidated blocks map should reference block3's height in descendants" + ), + } + + Ok(()) +} + +#[test] +fn invalidate_block_removes_block_and_descendants_from_chain() -> Result<()> { + let _init_guard = zebra_test::init(); + + for network in Network::iter() { + invalidate_block_removes_block_and_descendants_from_chain_for_network(network)?; + } + + Ok(()) +} + #[test] // This test gives full coverage for `take_chain_if` fn commit_block_extending_best_chain_doesnt_drop_worst_chains() -> Result<()> { From fa9f4c8d910d50a74e9b3349058c07e3579720aa Mon Sep 17 00:00:00 2001 From: Elijah Hampton Date: Wed, 5 Feb 2025 08:54:40 -0500 Subject: [PATCH 2/5] Removes InvalidatedBlockData struct. Wraps invalidated_blocks HashMap values with Arc. Optimizies invalidate_block fn to return early if root has hash of desired block. --- zebra-state/src/request.rs | 15 +------ .../src/service/non_finalized_state.rs | 43 +++++++++---------- .../src/service/non_finalized_state/chain.rs | 14 ++++++ .../non_finalized_state/tests/vectors.rs | 21 +++++---- 4 files changed, 49 insertions(+), 44 deletions(-) diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index ad5b834c4f2..0eeb9b940b2 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -1,10 +1,9 @@ //! State [`tower::Service`] request types. use std::{ - collections::{BTreeMap, HashMap, HashSet}, + collections::{HashMap, HashSet}, ops::{Deref, DerefMut, RangeInclusive}, sync::Arc, - time::SystemTime, }; use zebra_chain::{ @@ -235,18 +234,6 @@ pub struct ContextuallyVerifiedBlock { pub(crate) chain_value_pool_change: ValueBalance, } -/// Data related to an invalidated block including the [`ContextuallyVerifiedBlock`] and -/// descendants. -#[derive(Clone, Debug)] -pub struct InvalidatedBlockData { - /// The block that was invalidated. - pub block: ContextuallyVerifiedBlock, - /// All descendant blocks that were also removed in order by [`block::Height`]. - pub descendants: BTreeMap, - /// Timestamp in which the block was invalidated. - pub timestamp: SystemTime, -} - /// Wraps note commitment trees and the history tree together. /// /// The default instance represents the treestate that corresponds to the genesis block. diff --git a/zebra-state/src/service/non_finalized_state.rs b/zebra-state/src/service/non_finalized_state.rs index 452b930afe4..994051d118e 100644 --- a/zebra-state/src/service/non_finalized_state.rs +++ b/zebra-state/src/service/non_finalized_state.rs @@ -6,10 +6,8 @@ use std::{ collections::{BTreeSet, HashMap}, mem, sync::Arc, - time::SystemTime, }; -use chain::UpdateWith; use zebra_chain::{ block::{self, Block, Hash}, parameters::Network, @@ -18,9 +16,9 @@ use zebra_chain::{ use crate::{ constants::MAX_NON_FINALIZED_CHAIN_FORKS, - request::{ContextuallyVerifiedBlock, FinalizableBlock, InvalidatedBlockData}, + request::{ContextuallyVerifiedBlock, FinalizableBlock}, service::{check, finalized_state::ZebraDb}, - SemanticallyVerifiedBlock, ValidateContextError, + HashOrHeight, SemanticallyVerifiedBlock, ValidateContextError, }; mod chain; @@ -47,8 +45,9 @@ pub struct NonFinalizedState { /// callers should migrate to `chain_iter().next()`. chain_set: BTreeSet>, - /// Invalidated blocks and descendants - invalidated_blocks: HashMap, + /// Blocks that have been invalidated in, and removed from, the non finalized + /// state. + invalidated_blocks: HashMap>>, // Configuration // @@ -282,24 +281,24 @@ impl NonFinalizedState { .find_chain(|chain| chain.contains_block_hash(block_hash)) .expect("block hash exist in a chain"); + // If the non-finalized chain root has the intended hash drop the chain + // and return early + let root = chain + .block(HashOrHeight::Height(chain.non_finalized_root_height())) + .unwrap(); + if root.hash == block_hash { + self.chain_set.remove(&chain); + self.update_metrics_for_chains(); + self.update_metrics_bars(); + return; + } + let new_chain = Arc::make_mut(&mut chain); let block_height = new_chain.height_by_hash(block_hash).unwrap(); - // Split the new chain at the the `block_hash` and invalidate the block with the - // block_hash along with the block's descendants - let mut invalidated_blocks = new_chain.blocks.split_off(&block_height); - for (_, ctx_block) in invalidated_blocks.iter().rev() { - new_chain.revert_chain_with(ctx_block, chain::RevertPosition::Tip); - } - - self.invalidated_blocks.insert( - block_hash, - InvalidatedBlockData { - block: invalidated_blocks.remove(&block_height).unwrap(), - descendants: invalidated_blocks, - timestamp: SystemTime::now(), - }, - ); + let invalidated_descendants = new_chain.invalidate_block_and_descendants(&block_height); + self.invalidated_blocks + .insert(block_hash, Arc::new(invalidated_descendants)); // If the new chain still contains blocks: // - add the new chain fork or updated chain to the set of recent chains @@ -629,7 +628,7 @@ impl NonFinalizedState { } /// Return the invalidated blocks. - pub fn invalidated_blocks(&self) -> HashMap { + pub fn invalidated_blocks(&self) -> HashMap>> { self.invalidated_blocks.clone() } diff --git a/zebra-state/src/service/non_finalized_state/chain.rs b/zebra-state/src/service/non_finalized_state/chain.rs index 7497f0a6e8d..d4aadeecab6 100644 --- a/zebra-state/src/service/non_finalized_state/chain.rs +++ b/zebra-state/src/service/non_finalized_state/chain.rs @@ -343,6 +343,20 @@ impl Chain { (block, treestate) } + pub fn invalidate_block_and_descendants( + &mut self, + height: &block::Height, + ) -> Vec { + // Split the new chain at the the `block_hash` and invalidate the block with the + // block_hash along with the block's descendants + let invalidated_blocks = self.blocks.split_off(height); + for (_, ctx_block) in invalidated_blocks.iter().rev() { + self.revert_chain_with(ctx_block, RevertPosition::Tip); + } + + invalidated_blocks.into_values().collect() + } + /// Returns the height of the chain root. pub fn non_finalized_root_height(&self) -> block::Height { self.blocks 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 1791f8b07f2..96585328876 100644 --- a/zebra-state/src/service/non_finalized_state/tests/vectors.rs +++ b/zebra-state/src/service/non_finalized_state/tests/vectors.rs @@ -272,18 +272,23 @@ fn invalidate_block_removes_block_and_descendants_from_chain_for_network( "invalidated blocks map should reference the hash of block2" ); - let invalidated_blocks_state_descendants = &invalidated_blocks_state - .get(&block2.hash()) - .unwrap() - .descendants; + let invalidated_blocks_state_descendants = + invalidated_blocks_state.get(&block2.hash()).unwrap(); + match network { Network::Mainnet => assert!( - invalidated_blocks_state_descendants.contains_key(&block::Height(653601)), - "invalidated blocks map should reference block3's height in descendants" + invalidated_blocks_state_descendants + .iter() + .find(|block| block.height == block::Height(653601)) + .is_some(), + "invalidated descendants vec should contain block3" ), Network::Testnet(_parameters) => assert!( - invalidated_blocks_state_descendants.contains_key(&block::Height(584001)), - "invalidated blocks map should reference block3's height in descendants" + invalidated_blocks_state_descendants + .iter() + .find(|block| block.height == block::Height(584001)) + .is_some(), + "invalidated descendants vec should contain block3" ), } From 8cf06c6ab74b349310ad4d1649974fb52986d8cb Mon Sep 17 00:00:00 2001 From: Elijah Hampton Date: Wed, 5 Feb 2025 08:58:25 -0500 Subject: [PATCH 3/5] Update metrics whenever chain_set is modified --- zebra-state/src/service/non_finalized_state.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zebra-state/src/service/non_finalized_state.rs b/zebra-state/src/service/non_finalized_state.rs index 994051d118e..43a2a471ad8 100644 --- a/zebra-state/src/service/non_finalized_state.rs +++ b/zebra-state/src/service/non_finalized_state.rs @@ -307,10 +307,10 @@ impl NonFinalizedState { self.insert_with(Arc::new(new_chain.clone()), |chain_set| { chain_set.retain(|c| !c.contains_block_hash(block_hash)) }); - - self.update_metrics_for_chains(); - self.update_metrics_bars(); } + + self.update_metrics_for_chains(); + self.update_metrics_bars(); } /// Commit block to the non-finalized state as a new chain where its parent From 76536c0c0493ff5f162f9ce12afc2cb21eb983a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:47:58 +0000 Subject: [PATCH 4/5] build(deps): bump the devops group with 5 updates Bumps the devops group with 5 updates: | Package | From | To | | --- | --- | --- | | [google-github-actions/auth](https://github.com/google-github-actions/auth) | `2.1.7` | `2.1.8` | | [google-github-actions/setup-gcloud](https://github.com/google-github-actions/setup-gcloud) | `2.1.2` | `2.1.4` | | [tj-actions/changed-files](https://github.com/tj-actions/changed-files) | `45.0.6` | `45.0.7` | | [jontze/action-mdbook](https://github.com/jontze/action-mdbook) | `3.0.1` | `4.0.0` | | [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) | `6.0.0` | `6.1.0` | Updates `google-github-actions/auth` from 2.1.7 to 2.1.8 - [Release notes](https://github.com/google-github-actions/auth/releases) - [Changelog](https://github.com/google-github-actions/auth/blob/main/CHANGELOG.md) - [Commits](https://github.com/google-github-actions/auth/compare/v2.1.7...v2.1.8) Updates `google-github-actions/setup-gcloud` from 2.1.2 to 2.1.4 - [Release notes](https://github.com/google-github-actions/setup-gcloud/releases) - [Changelog](https://github.com/google-github-actions/setup-gcloud/blob/main/CHANGELOG.md) - [Commits](https://github.com/google-github-actions/setup-gcloud/compare/v2.1.2...v2.1.4) Updates `tj-actions/changed-files` from 45.0.6 to 45.0.7 - [Release notes](https://github.com/tj-actions/changed-files/releases) - [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md) - [Commits](https://github.com/tj-actions/changed-files/compare/v45.0.6...v45.0.7) Updates `jontze/action-mdbook` from 3.0.1 to 4.0.0 - [Release notes](https://github.com/jontze/action-mdbook/releases) - [Changelog](https://github.com/jontze/action-mdbook/blob/master/CHANGELOG.md) - [Commits](https://github.com/jontze/action-mdbook/compare/v3.0.1...v4.0.0) Updates `release-drafter/release-drafter` from 6.0.0 to 6.1.0 - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/v6.0.0...v6.1.0) --- updated-dependencies: - dependency-name: google-github-actions/auth dependency-type: direct:production update-type: version-update:semver-patch dependency-group: devops - dependency-name: google-github-actions/setup-gcloud dependency-type: direct:production update-type: version-update:semver-patch dependency-group: devops - dependency-name: tj-actions/changed-files dependency-type: direct:production update-type: version-update:semver-patch dependency-group: devops - dependency-name: jontze/action-mdbook dependency-type: direct:production update-type: version-update:semver-major dependency-group: devops - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-minor dependency-group: devops ... Signed-off-by: dependabot[bot] --- .github/workflows/cd-deploy-nodes-gcp.yml | 8 ++++---- .github/workflows/chore-delete-gcp-resources.yml | 6 +++--- .github/workflows/ci-lint.yml | 4 ++-- .github/workflows/docs-deploy-firebase.yml | 6 +++--- .github/workflows/manual-zcashd-deploy.yml | 4 ++-- .github/workflows/release-drafter.yml | 2 +- .github/workflows/sub-build-docker-image.yml | 2 +- .../workflows/sub-deploy-integration-tests-gcp.yml | 12 ++++++------ .github/workflows/sub-find-cached-disks.yml | 4 ++-- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/cd-deploy-nodes-gcp.yml b/.github/workflows/cd-deploy-nodes-gcp.yml index cccdd4af542..315a7dc4464 100644 --- a/.github/workflows/cd-deploy-nodes-gcp.yml +++ b/.github/workflows/cd-deploy-nodes-gcp.yml @@ -269,13 +269,13 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.2 + uses: google-github-actions/setup-gcloud@v2.1.4 - name: Create instance template for ${{ matrix.network }} run: | @@ -384,13 +384,13 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.2 + uses: google-github-actions/setup-gcloud@v2.1.4 # Create instance template from container image - name: Manual deploy of a single ${{ inputs.network }} instance running zebrad diff --git a/.github/workflows/chore-delete-gcp-resources.yml b/.github/workflows/chore-delete-gcp-resources.yml index 962442fc8d8..661c8c05093 100644 --- a/.github/workflows/chore-delete-gcp-resources.yml +++ b/.github/workflows/chore-delete-gcp-resources.yml @@ -50,13 +50,13 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.2 + uses: google-github-actions/setup-gcloud@v2.1.4 # Deletes all mainnet and testnet instances older than $DELETE_INSTANCE_DAYS days. # @@ -121,7 +121,7 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml index 0e40daa7c7e..df13ae1b1f7 100644 --- a/.github/workflows/ci-lint.yml +++ b/.github/workflows/ci-lint.yml @@ -44,7 +44,7 @@ jobs: - name: Rust files id: changed-files-rust - uses: tj-actions/changed-files@v45.0.6 + uses: tj-actions/changed-files@v45.0.7 with: files: | **/*.rs @@ -56,7 +56,7 @@ jobs: - name: Workflow files id: changed-files-workflows - uses: tj-actions/changed-files@v45.0.6 + uses: tj-actions/changed-files@v45.0.7 with: files: | .github/workflows/*.yml diff --git a/.github/workflows/docs-deploy-firebase.yml b/.github/workflows/docs-deploy-firebase.yml index 0154ffe1bd7..eca70c4d98b 100644 --- a/.github/workflows/docs-deploy-firebase.yml +++ b/.github/workflows/docs-deploy-firebase.yml @@ -92,7 +92,7 @@ jobs: - uses: r7kamura/rust-problem-matchers@v1.5.0 - name: Setup mdBook - uses: jontze/action-mdbook@v3.0.1 + uses: jontze/action-mdbook@v4.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} mdbook-version: '~0.4' @@ -106,7 +106,7 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_FIREBASE_SA }}' @@ -164,7 +164,7 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_FIREBASE_SA }}' diff --git a/.github/workflows/manual-zcashd-deploy.yml b/.github/workflows/manual-zcashd-deploy.yml index 8fc5951d142..8d6541ff370 100644 --- a/.github/workflows/manual-zcashd-deploy.yml +++ b/.github/workflows/manual-zcashd-deploy.yml @@ -52,13 +52,13 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.2 + uses: google-github-actions/setup-gcloud@v2.1.4 # Create instance template from container image - name: Create instance template diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 6b1e21364d3..b5025a4b463 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-latest steps: # Drafts your next Release notes - - uses: release-drafter/release-drafter@v6.0.0 + - uses: release-drafter/release-drafter@v6.1.0 with: config-name: release-drafter.yml commitish: main diff --git a/.github/workflows/sub-build-docker-image.yml b/.github/workflows/sub-build-docker-image.yml index 743b3e1565c..c5142babe31 100644 --- a/.github/workflows/sub-build-docker-image.yml +++ b/.github/workflows/sub-build-docker-image.yml @@ -127,7 +127,7 @@ jobs: - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_ARTIFACTS_SA }}' diff --git a/.github/workflows/sub-deploy-integration-tests-gcp.yml b/.github/workflows/sub-deploy-integration-tests-gcp.yml index bd23da1f31b..1a8854febd0 100644 --- a/.github/workflows/sub-deploy-integration-tests-gcp.yml +++ b/.github/workflows/sub-deploy-integration-tests-gcp.yml @@ -172,13 +172,13 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.2 + uses: google-github-actions/setup-gcloud@v2.1.4 # Create a Compute Engine virtual machine and attach a cached state disk using the # $CACHED_DISK_NAME env as the source image to populate the disk cached state @@ -429,13 +429,13 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.2 + uses: google-github-actions/setup-gcloud@v2.1.4 # Sets the $UPDATE_SUFFIX env var to "-u" if updating a previous cached state, # and the empty string otherwise. @@ -695,13 +695,13 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.2 + uses: google-github-actions/setup-gcloud@v2.1.4 # Deletes the instances that has been recently deployed in the actual commit after all # previous jobs have run, no matter the outcome of the job. diff --git a/.github/workflows/sub-find-cached-disks.yml b/.github/workflows/sub-find-cached-disks.yml index a45e3f731fa..d0dd52d6c1e 100644 --- a/.github/workflows/sub-find-cached-disks.yml +++ b/.github/workflows/sub-find-cached-disks.yml @@ -67,13 +67,13 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.7 + uses: google-github-actions/auth@v2.1.8 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.2 + uses: google-github-actions/setup-gcloud@v2.1.4 # Performs formatting on disk name components. # From a4af5d14716bbf07e51135a531197b522231753e Mon Sep 17 00:00:00 2001 From: Elijah Hampton Date: Thu, 6 Feb 2025 21:02:12 -0500 Subject: [PATCH 5/5] Adds child_blocks method and refactors invalidate_block in chain.rs. Refactors invalidate_block in NonFinalizedState. --- .../src/service/non_finalized_state.rs | 50 +++++++------------ .../src/service/non_finalized_state/chain.rs | 28 +++++++---- .../non_finalized_state/tests/vectors.rs | 6 +-- 3 files changed, 38 insertions(+), 46 deletions(-) diff --git a/zebra-state/src/service/non_finalized_state.rs b/zebra-state/src/service/non_finalized_state.rs index 43a2a471ad8..2fbc3f35e55 100644 --- a/zebra-state/src/service/non_finalized_state.rs +++ b/zebra-state/src/service/non_finalized_state.rs @@ -18,7 +18,7 @@ use crate::{ constants::MAX_NON_FINALIZED_CHAIN_FORKS, request::{ContextuallyVerifiedBlock, FinalizableBlock}, service::{check, finalized_state::ZebraDb}, - HashOrHeight, SemanticallyVerifiedBlock, ValidateContextError, + SemanticallyVerifiedBlock, ValidateContextError, }; mod chain; @@ -270,45 +270,33 @@ impl NonFinalizedState { Ok(()) } - /// Invalidate block with has `block_hash` and all descendants from the non-finalized state. Insert + /// Invalidate block with hash `block_hash` and all descendants from the non-finalized state. Insert /// the new chain into the chain_set and discard the previous. pub fn invalidate_block(&mut self, block_hash: Hash) { - if !self.any_chain_contains(&block_hash) { + let Some(chain) = self.find_chain(|chain| chain.contains_block_hash(block_hash)) else { return; - } - - let mut chain = self - .find_chain(|chain| chain.contains_block_hash(block_hash)) - .expect("block hash exist in a chain"); + }; - // If the non-finalized chain root has the intended hash drop the chain - // and return early - let root = chain - .block(HashOrHeight::Height(chain.non_finalized_root_height())) - .unwrap(); - if root.hash == block_hash { + let invalidated_blocks = if chain.non_finalized_root_hash() == block_hash { self.chain_set.remove(&chain); - self.update_metrics_for_chains(); - self.update_metrics_bars(); - return; - } + chain.blocks.values().cloned().collect() + } else { + let (new_chain, invalidated_blocks) = chain + .invalidate_block(block_hash) + .expect("already checked that chain contains hash"); + + // Add the new chain fork or updated chain to the set of recent chains, and + // remove the chain containing the hash of the block from chain set + self.insert_with(Arc::new(new_chain.clone()), |chain_set| { + chain_set.retain(|c| !c.contains_block_hash(block_hash)) + }); - let new_chain = Arc::make_mut(&mut chain); - let block_height = new_chain.height_by_hash(block_hash).unwrap(); + invalidated_blocks + }; - let invalidated_descendants = new_chain.invalidate_block_and_descendants(&block_height); self.invalidated_blocks - .insert(block_hash, Arc::new(invalidated_descendants)); + .insert(block_hash, Arc::new(invalidated_blocks)); - // If the new chain still contains blocks: - // - add the new chain fork or updated chain to the set of recent chains - // - remove the chain containing the hash of the block from the chain set - if !new_chain.is_empty() { - self.insert_with(Arc::new(new_chain.clone()), |chain_set| { - chain_set.retain(|c| !c.contains_block_hash(block_hash)) - }); - } - self.update_metrics_for_chains(); self.update_metrics_bars(); } diff --git a/zebra-state/src/service/non_finalized_state/chain.rs b/zebra-state/src/service/non_finalized_state/chain.rs index d4aadeecab6..7d7efe22ce8 100644 --- a/zebra-state/src/service/non_finalized_state/chain.rs +++ b/zebra-state/src/service/non_finalized_state/chain.rs @@ -343,18 +343,24 @@ impl Chain { (block, treestate) } - pub fn invalidate_block_and_descendants( - &mut self, - height: &block::Height, - ) -> Vec { - // Split the new chain at the the `block_hash` and invalidate the block with the - // block_hash along with the block's descendants - let invalidated_blocks = self.blocks.split_off(height); - for (_, ctx_block) in invalidated_blocks.iter().rev() { - self.revert_chain_with(ctx_block, RevertPosition::Tip); - } + // Returns the block at the provided height and all of its descendant blocks. + pub fn child_blocks(&self, block_height: &block::Height) -> Vec { + self.blocks + .range(block_height..) + .map(|(_h, b)| b.clone()) + .collect() + } - invalidated_blocks.into_values().collect() + // Returns a new chain without the invalidated block or its descendants. + pub fn invalidate_block( + &self, + block_hash: block::Hash, + ) -> Option<(Self, Vec)> { + let block_height = self.height_by_hash(block_hash)?; + let mut new_chain = self.fork(block_hash)?; + new_chain.pop_tip(); + new_chain.last_fork_height = None; + Some((new_chain, self.child_blocks(&block_height))) } /// Returns the height of the chain root. 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 96585328876..5b392e4a0b9 100644 --- a/zebra-state/src/service/non_finalized_state/tests/vectors.rs +++ b/zebra-state/src/service/non_finalized_state/tests/vectors.rs @@ -279,15 +279,13 @@ fn invalidate_block_removes_block_and_descendants_from_chain_for_network( Network::Mainnet => assert!( invalidated_blocks_state_descendants .iter() - .find(|block| block.height == block::Height(653601)) - .is_some(), + .any(|block| block.height == block::Height(653601)), "invalidated descendants vec should contain block3" ), Network::Testnet(_parameters) => assert!( invalidated_blocks_state_descendants .iter() - .find(|block| block.height == block::Height(584001)) - .is_some(), + .any(|block| block.height == block::Height(584001)), "invalidated descendants vec should contain block3" ), }