Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(state-snapshots): fix skipped state snapshot after header sync #12920

Merged
merged 2 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions chain/chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3633,11 +3633,7 @@ impl Chain {
Ok(SnapshotAction::None)
}
} else {
let is_sync_prev = crate::state_sync::is_sync_prev_hash(
&self.chain_store.store(),
&head.last_block_hash,
&head.prev_block_hash,
)?;
let is_sync_prev = self.state_sync_adapter.is_sync_prev_hash(&head)?;
if is_sync_prev {
// Here the head block is the prev block of what the sync hash will be, and the previous
// block is the point in the chain we want to snapshot state for
Expand Down
6 changes: 6 additions & 0 deletions chain/chain/src/state_sync/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{byzantine_assert, metrics, ReceiptFilter};
use near_async::time::{Clock, Instant};
use near_chain_primitives::error::{Error, LogTransientStorageError};
use near_epoch_manager::EpochManagerAdapter;
use near_primitives::block::Tip;
use near_primitives::hash::CryptoHash;
use near_primitives::merkle::{merklize, verify_path};
use near_primitives::sharding::{
Expand Down Expand Up @@ -545,4 +546,9 @@ impl ChainStateSyncAdapter {
pub fn get_requested_state_parts(&self) -> Vec<RequestedStatePartsView> {
self.requested_state_parts.get_requested_state_parts()
}

/// Returns whether `tip.last_block_hash` is the block that will appear immediately before the "sync_hash" block.
pub fn is_sync_prev_hash(&self, tip: &Tip) -> Result<bool, Error> {
crate::state_sync::utils::is_sync_prev_hash(&self.chain_store, tip)
}
}
2 changes: 1 addition & 1 deletion chain/chain/src/state_sync/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub(crate) use adapter::ChainStateSyncAdapter;
pub(crate) use utils::{is_sync_prev_hash, update_sync_hashes};
pub(crate) use utils::update_sync_hashes;

mod adapter;
mod state_request_tracker;
Expand Down
24 changes: 14 additions & 10 deletions chain/chain/src/state_sync/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use near_chain_primitives::error::Error;
use near_primitives::block::Tip;
use near_primitives::hash::CryptoHash;
use near_primitives::types::EpochId;
use near_primitives::version::ProtocolFeature;
use near_store::adapter::chain_store::ChainStoreAdapter;
use near_store::adapter::StoreAdapter;
use near_store::{DBCol, Store, StoreUpdate};

use borsh::BorshDeserialize;
Expand Down Expand Up @@ -203,30 +206,31 @@ pub(crate) fn update_sync_hashes<T: ChainStoreAccess>(
on_new_header(chain_store, store_update, header)
}

///. Returns whether `block_hash` is the block that will appear immediately before the "sync_hash" block. That is,
/// whether it is going to be the prev_hash of the "sync_hash" block, when it is found.
/// Returns whether `tip.last_block_hash` is the block that will appear immediately before the "sync_hash" block.
/// That is, whether it is going to be the prev_hash of the "sync_hash" block, when it is found.
///
/// `block_hash` is the prev_hash of the future "sync_hash" block iff it is the first block for which the
/// `tip.last_block_hash` is the prev_hash of the future "sync_hash" block iff it is the first block for which the
/// number of new chunks in the epoch in each shard is at least 2
///
/// This function can only return true before we save the "sync_hash" block to the `StateSyncHashes` column,
/// because it relies on data stored in the `StateSyncNewChunks` column, which is cleaned up after that.
///
/// This is used when making state snapshots, because in that case we don't need to wait for the "sync_hash"
/// block to be finalized to take a snapshot of the state as of its prev prev block
pub(crate) fn is_sync_prev_hash(
store: &Store,
block_hash: &CryptoHash,
prev_hash: &CryptoHash,
) -> Result<bool, Error> {
let Some(new_chunks) = get_state_sync_new_chunks(store, block_hash)? else {
pub(crate) fn is_sync_prev_hash(chain_store: &ChainStoreAdapter, tip: &Tip) -> Result<bool, Error> {
if let Some(sync_hash) = chain_store.get_current_epoch_sync_hash(&tip.epoch_id)? {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a short comment about why this is important? Otherwise it looks like just an optimization but it's actually about correctness.

let sync_header = chain_store.get_block_header(&sync_hash)?;
return Ok(sync_header.prev_hash() == &tip.last_block_hash);
}
let store = chain_store.store_ref();
let Some(new_chunks) = get_state_sync_new_chunks(store, &tip.last_block_hash)? else {
return Ok(false);
};
let done = new_chunks.iter().all(|num_chunks| *num_chunks >= 2);
if !done {
return Ok(false);
}
let Some(prev_new_chunks) = get_state_sync_new_chunks(store, prev_hash)? else {
let Some(prev_new_chunks) = get_state_sync_new_chunks(store, &tip.prev_block_hash)? else {
return Ok(false);
};
let prev_done = prev_new_chunks.iter().all(|num_chunks| *num_chunks >= 2);
Expand Down
Loading