From 8f62a08bbcbf47c0d6c02ee211dc3b837bf8506e Mon Sep 17 00:00:00 2001 From: Stefan <30928612+stedfn@users.noreply.github.com> Date: Thu, 30 Jan 2025 14:51:38 +0100 Subject: [PATCH] refactor: remove shard functions from epoch manager (#12843) --- chain/chain/src/chain.rs | 19 +- chain/chain/src/chain_update.rs | 4 +- chain/chain/src/migrations.rs | 3 +- chain/chain/src/runtime/tests.rs | 14 +- .../stateless_validation/chunk_validation.rs | 19 +- chain/chain/src/store/mod.rs | 7 +- chain/chain/src/test_utils/kv_runtime.rs | 129 ----------- chain/chain/src/validate.rs | 5 +- chain/chunks/src/logic.rs | 4 +- chain/client/src/client.rs | 26 ++- .../state_witness_producer.rs | 20 +- chain/client/src/test_utils/test_env.rs | 16 +- chain/client/src/test_utils/test_loop.rs | 23 +- chain/epoch-manager/src/adapter.rs | 171 +-------------- chain/epoch-manager/src/lib.rs | 66 ------ chain/epoch-manager/src/shard_tracker.rs | 202 ++++++++++++++++-- .../test_loop/tests/bandwidth_scheduler.rs | 12 +- .../src/test_loop/tests/max_receipt_size.rs | 12 +- .../src/test_loop/utils/sharding.rs | 4 +- .../src/tests/client/resharding_v2.rs | 21 +- nearcore/src/entity_debug.rs | 15 +- tools/replay-archive/src/cli.rs | 7 +- tools/state-viewer/src/apply_chain_range.rs | 3 +- tools/state-viewer/src/apply_chunk.rs | 7 +- tools/state-viewer/src/commands.rs | 6 +- 25 files changed, 341 insertions(+), 474 deletions(-) diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index bc2dab915fa..130c0e11532 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -53,7 +53,10 @@ use near_async::time::{Clock, Duration, Instant}; use near_chain_configs::{MutableConfigValue, MutableValidatorSigner}; use near_chain_primitives::error::{BlockKnownError, Error, LogTransientStorageError}; use near_epoch_manager::shard_assignment::shard_id_to_uid; -use near_epoch_manager::shard_tracker::ShardTracker; +use near_epoch_manager::shard_tracker::{ + get_prev_shard_id_from_prev_hash, get_prev_shard_ids, get_shard_layout_from_prev_block, + ShardTracker, +}; use near_epoch_manager::EpochManagerAdapter; use near_primitives::bandwidth_scheduler::BandwidthRequests; use near_primitives::block::{genesis_chunks, Block, BlockValidityError, Chunks, MaybeNew, Tip}; @@ -2151,8 +2154,10 @@ impl Chain { let chunk_header = last_final_block_chunks .get(shard_index) .ok_or_else(|| Error::InvalidShardId(shard_uid.shard_id()))?; - let chunk_shard_layout = - self.epoch_manager.get_shard_layout_from_prev_block(chunk_header.prev_block_hash())?; + let chunk_shard_layout = get_shard_layout_from_prev_block( + self.epoch_manager.as_ref(), + chunk_header.prev_block_hash(), + )?; let chunk_shard_uid = ShardUId::from_shard_id_and_layout(chunk_header.shard_id(), &chunk_shard_layout); @@ -3523,7 +3528,7 @@ impl Chain { chunk_header: &ShardChunkHeader, ) -> Result { let shard_layout = - self.epoch_manager.get_shard_layout_from_prev_block(prev_block.hash())?; + get_shard_layout_from_prev_block(self.epoch_manager.as_ref(), prev_block.hash())?; let shard_id = chunk_header.shard_id(); let shard_index = shard_layout.get_shard_index(shard_id)?; let prev_merkle_proofs = @@ -3904,7 +3909,7 @@ impl Chain { let epoch_height = self.epoch_manager.get_epoch_height_from_prev_block(prev_prev_hash)?; let shard_layout = - &self.epoch_manager.get_shard_layout_from_prev_block(prev_prev_hash)?; + &get_shard_layout_from_prev_block(self.epoch_manager.as_ref(), prev_prev_hash)?; let shard_uids = shard_layout.shard_uids().enumerate().collect(); let make_snapshot_callback = &snapshot_callbacks.make_snapshot_callback; @@ -4452,7 +4457,7 @@ impl Chain { let epoch_id = epoch_manager.get_epoch_id_from_prev_block(prev_block.hash())?; let shard_ids = epoch_manager.shard_ids(&epoch_id)?; - let prev_shard_ids = epoch_manager.get_prev_shard_ids(prev_block.hash(), shard_ids)?; + let prev_shard_ids = get_prev_shard_ids(epoch_manager, prev_block.hash(), shard_ids)?; let prev_chunks = prev_block.chunks(); Ok(prev_shard_ids .into_iter() @@ -4466,7 +4471,7 @@ impl Chain { shard_id: ShardId, ) -> Result { let (_, prev_shard_id, prev_shard_index) = - epoch_manager.get_prev_shard_id_from_prev_hash(prev_block.hash(), shard_id)?; + get_prev_shard_id_from_prev_hash(epoch_manager, prev_block.hash(), shard_id)?; Ok(prev_block .chunks() .get(prev_shard_index) diff --git a/chain/chain/src/chain_update.rs b/chain/chain/src/chain_update.rs index 83f5f388bdd..8c0545a1a73 100644 --- a/chain/chain/src/chain_update.rs +++ b/chain/chain/src/chain_update.rs @@ -12,6 +12,7 @@ use crate::{metrics, DoomslugThresholdMode}; use crate::{Chain, Doomslug}; use near_chain_primitives::error::Error; use near_epoch_manager::shard_assignment::shard_id_to_uid; +use near_epoch_manager::shard_tracker::get_shard_layout_from_prev_block; use near_epoch_manager::EpochManagerAdapter; use near_primitives::apply::ApplyChunkReason; use near_primitives::block::{Block, Tip}; @@ -297,7 +298,8 @@ impl<'a> ChainUpdate<'a> { } } - let shard_layout = self.epoch_manager.get_shard_layout_from_prev_block(prev.hash())?; + let shard_layout = + get_shard_layout_from_prev_block(self.epoch_manager.as_ref(), &prev.hash())?; SHARD_LAYOUT_VERSION.set(shard_layout.version() as i64); SHARD_LAYOUT_NUM_SHARDS.set(shard_layout.shard_ids().count() as i64); } diff --git a/chain/chain/src/migrations.rs b/chain/chain/src/migrations.rs index afdbf7f3f43..b50dc9ef422 100644 --- a/chain/chain/src/migrations.rs +++ b/chain/chain/src/migrations.rs @@ -1,5 +1,6 @@ use crate::store::ChainStoreAccess; use near_chain_primitives::error::Error; +use near_epoch_manager::shard_tracker::get_prev_shard_ids; use near_epoch_manager::EpochManagerAdapter; use near_primitives::hash::CryptoHash; use near_primitives::types::ShardId; @@ -29,7 +30,7 @@ pub fn check_if_block_is_first_with_chunk_of_version( if is_first_epoch_with_protocol_version(epoch_manager, prev_block_hash)? { // Compare only epochs because we already know that current epoch is the first one with current protocol version // convert shard id to shard id of previous epoch because number of shards may change - let (shard_id, _) = epoch_manager.get_prev_shard_ids(prev_block_hash, vec![shard_id])?[0]; + let (shard_id, _) = get_prev_shard_ids(epoch_manager, prev_block_hash, vec![shard_id])?[0]; let prev_epoch_id = chain_store.get_epoch_id_of_last_block_with_chunk( epoch_manager, prev_block_hash, diff --git a/chain/chain/src/runtime/tests.rs b/chain/chain/src/runtime/tests.rs index 460a6f82ac6..e895d98f0b1 100644 --- a/chain/chain/src/runtime/tests.rs +++ b/chain/chain/src/runtime/tests.rs @@ -6,7 +6,7 @@ use crate::{Chain, ChainGenesis, ChainStoreAccess, DoomslugThresholdMode}; use assert_matches::assert_matches; use near_chain_configs::test_utils::{TESTING_INIT_BALANCE, TESTING_INIT_STAKE}; use near_epoch_manager::shard_assignment::shard_id_to_uid; -use near_epoch_manager::shard_tracker::ShardTracker; +use near_epoch_manager::shard_tracker::{get_shard_layout_from_prev_block, ShardTracker}; use near_epoch_manager::{EpochManager, RngSeed}; use near_pool::{ InsertTransactionResult, PoolIteratorWrapper, TransactionGroupIteratorWrapper, TransactionPool, @@ -374,7 +374,8 @@ impl TestEnv { .unwrap() .commit() .unwrap(); - let shard_layout = self.epoch_manager.get_shard_layout_from_prev_block(&new_hash).unwrap(); + let shard_layout = + get_shard_layout_from_prev_block(self.epoch_manager.as_ref(), &new_hash).unwrap(); let mut new_receipts = HashMap::<_, Vec>::new(); for receipt in all_receipts { if receipt.send_to_all_shards() { @@ -1459,7 +1460,8 @@ fn test_insufficient_stake() { fn test_flat_state_usage() { let env = TestEnv::new(vec![vec!["test1".parse().unwrap()]], 4, false); let prev_hash = env.head.prev_block_hash; - let shard_layout = env.epoch_manager.get_shard_layout_from_prev_block(&prev_hash).unwrap(); + let shard_layout = + get_shard_layout_from_prev_block(env.epoch_manager.as_ref(), &prev_hash).unwrap(); let shard_id = shard_layout.shard_ids().next().unwrap(); let state_root = Trie::EMPTY_ROOT; @@ -1499,7 +1501,8 @@ fn test_trie_and_flat_state_equality() { // - using state trie, which should use flat state after enabling it in the protocol // - using view state, which should never use flat state let prev_hash = env.head.prev_block_hash; - let shard_layout = env.epoch_manager.get_shard_layout_from_prev_block(&prev_hash).unwrap(); + let shard_layout = + get_shard_layout_from_prev_block(env.epoch_manager.as_ref(), &prev_hash).unwrap(); let shard_id = shard_layout.shard_ids().next().unwrap(); let state_root = env.state_roots[0]; @@ -1644,7 +1647,8 @@ fn prepare_transactions( storage_config: RuntimeStorageConfig, ) -> Result { let prev_hash = env.head.prev_block_hash; - let shard_layout = env.epoch_manager.get_shard_layout_from_prev_block(&prev_hash).unwrap(); + let shard_layout = + get_shard_layout_from_prev_block(env.epoch_manager.as_ref(), &prev_hash).unwrap(); let shard_id = shard_layout.shard_ids().next().unwrap(); let block = chain.get_block(&prev_hash).unwrap(); let congestion_info = block.block_congestion_info(); diff --git a/chain/chain/src/stateless_validation/chunk_validation.rs b/chain/chain/src/stateless_validation/chunk_validation.rs index cd22f7316e9..bfa91cdf333 100644 --- a/chain/chain/src/stateless_validation/chunk_validation.rs +++ b/chain/chain/src/stateless_validation/chunk_validation.rs @@ -18,6 +18,9 @@ use lru::LruCache; use near_async::futures::AsyncComputationSpawnerExt; use near_chain_primitives::Error; use near_epoch_manager::shard_assignment::shard_id_to_uid; +use near_epoch_manager::shard_tracker::{ + get_prev_shard_id_from_prev_hash, get_shard_layout_from_prev_block, +}; use near_epoch_manager::EpochManagerAdapter; use near_pool::TransactionGroupIteratorWrapper; use near_primitives::apply::ApplyChunkReason; @@ -182,8 +185,7 @@ fn get_state_witness_block_range( let initial_prev_hash = *state_witness.chunk_header.prev_block_hash(); let initial_prev_block = store.get_block(&initial_prev_hash)?; - let initial_shard_layout = - epoch_manager.get_shard_layout_from_prev_block(&initial_prev_hash)?; + let initial_shard_layout = get_shard_layout_from_prev_block(epoch_manager, &initial_prev_hash)?; let initial_shard_id = state_witness.chunk_header.shard_id(); // Check that shard id is present in current epoch. // TODO: consider more proper way to validate this. @@ -212,7 +214,7 @@ fn get_state_witness_block_range( implicit_transition_params.push(transition); } let (prev_shard_layout, prev_shard_id, prev_shard_index) = - epoch_manager.get_prev_shard_id_from_prev_hash(prev_hash, position.shard_id)?; + get_prev_shard_id_from_prev_hash(epoch_manager, prev_hash, position.shard_id)?; let new_chunk_seen = block_has_new_chunk(&position.prev_block, prev_shard_index)?; let new_chunks_seen_update = @@ -285,7 +287,7 @@ fn get_resharding_transition( return Ok(None); } - let shard_layout = epoch_manager.get_shard_layout_from_prev_block(prev_header.hash())?; + let shard_layout = get_shard_layout_from_prev_block(epoch_manager, prev_header.hash())?; let prev_epoch_id = epoch_manager.get_prev_epoch_id_from_prev_block(prev_header.hash())?; let prev_shard_layout = epoch_manager.get_shard_layout(&prev_epoch_id)?; let block_has_new_shard_layout = epoch_manager.is_next_block_epoch_start(prev_header.hash())? @@ -553,9 +555,12 @@ fn validate_source_receipt_proofs( receipts_to_apply.extend(proof.0.iter().cloned()); } - current_target_shard_id = epoch_manager - .get_prev_shard_id_from_prev_hash(block.header().prev_hash(), current_target_shard_id)? - .1; + current_target_shard_id = get_prev_shard_id_from_prev_hash( + epoch_manager, + block.header().prev_hash(), + current_target_shard_id, + )? + .1; } // Check that there are no extraneous proofs in source_receipt_proofs. diff --git a/chain/chain/src/store/mod.rs b/chain/chain/src/store/mod.rs index e46833d8757..1a0992c61d4 100644 --- a/chain/chain/src/store/mod.rs +++ b/chain/chain/src/store/mod.rs @@ -7,6 +7,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use chrono::Utc; use near_chain_primitives::error::Error; +use near_epoch_manager::shard_tracker::{get_prev_shard_ids, get_shard_layout_from_prev_block}; use near_epoch_manager::EpochManagerAdapter; use near_primitives::block::Tip; use near_primitives::checked_feature; @@ -243,7 +244,7 @@ pub trait ChainStoreAccess { } let prev_hash = header.prev_hash(); - let prev_shard_layout = epoch_manager.get_shard_layout_from_prev_block(prev_hash)?; + let prev_shard_layout = get_shard_layout_from_prev_block(epoch_manager, prev_hash)?; if prev_shard_layout != current_shard_layout { let parent_shard_id = current_shard_layout.get_parent_shard_id(current_shard_id)?; @@ -362,7 +363,7 @@ pub trait ChainStoreAccess { } candidate_hash = *block_header.prev_hash(); (shard_id, shard_index) = - epoch_manager.get_prev_shard_ids(&candidate_hash, vec![shard_id])?[0]; + get_prev_shard_ids(epoch_manager, &candidate_hash, vec![shard_id])?[0]; } } @@ -492,7 +493,7 @@ impl ChainStore { shard_id: ShardId, last_included_height: BlockHeight, ) -> Result, Error> { - let shard_layout = epoch_manager.get_shard_layout_from_prev_block(&prev_block_hash)?; + let shard_layout = get_shard_layout_from_prev_block(epoch_manager, &prev_block_hash)?; let mut receipts_block_hash = prev_block_hash; loop { let block_header = self.get_block_header(&receipts_block_hash)?; diff --git a/chain/chain/src/test_utils/kv_runtime.rs b/chain/chain/src/test_utils/kv_runtime.rs index 92aabaff042..bf23484ba67 100644 --- a/chain/chain/src/test_utils/kv_runtime.rs +++ b/chain/chain/src/test_utils/kv_runtime.rs @@ -564,45 +564,6 @@ impl EpochManagerAdapter for MockEpochManager { Ok(self.get_epoch_and_valset(*parent_hash)?.2) } - fn get_prev_shard_ids( - &self, - prev_hash: &CryptoHash, - shard_ids: Vec, - ) -> Result, Error> { - let mut prev_shard_ids = vec![]; - let shard_layout = self.get_shard_layout_from_prev_block(prev_hash)?; - for shard_id in shard_ids { - // This is not correct if there was a resharding event in between - // the previous and current block. - let prev_shard_id = shard_id; - let prev_shard_index = shard_layout.get_shard_index(prev_shard_id)?; - prev_shard_ids.push((prev_shard_id, prev_shard_index)); - } - - Ok(prev_shard_ids) - } - - fn get_prev_shard_id_from_prev_hash( - &self, - prev_hash: &CryptoHash, - shard_id: ShardId, - ) -> Result<(ShardLayout, ShardId, ShardIndex), EpochError> { - let shard_layout = self.get_shard_layout_from_prev_block(prev_hash)?; - // This is not correct if there was a resharding event in between - // the previous and current block. - let prev_shard_id = shard_id; - let prev_shard_index = shard_layout.get_shard_index(prev_shard_id)?; - Ok((shard_layout, prev_shard_id, prev_shard_index)) - } - - fn get_shard_layout_from_prev_block( - &self, - _parent_hash: &CryptoHash, - ) -> Result { - #[allow(deprecated)] - Ok(ShardLayout::v0(self.num_shards, 0)) - } - fn get_epoch_id(&self, block_hash: &CryptoHash) -> Result { let (epoch_id, _, _) = self.get_epoch_and_valset(*block_hash)?; Ok(epoch_id) @@ -886,96 +847,6 @@ impl EpochManagerAdapter for MockEpochManager { Ok(true) } - fn cares_about_shard_in_epoch( - &self, - epoch_id: &EpochId, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - // This `unwrap` here tests that in all code paths we check that the epoch exists before - // we check if we care about a shard. Please do not remove the unwrap, fix the logic of - // the calling function. - let epoch_valset = self.get_valset_for_epoch(epoch_id).unwrap(); - let shard_layout = self.get_shard_layout(epoch_id)?; - let shard_index = shard_layout.get_shard_index(shard_id)?; - let chunk_producers = self.get_chunk_producers(epoch_valset, shard_index); - for validator in chunk_producers { - if validator.account_id() == account_id { - return Ok(true); - } - } - Ok(false) - } - - fn cares_about_shard_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - // This `unwrap` here tests that in all code paths we check that the epoch exists before - // we check if we care about a shard. Please do not remove the unwrap, fix the logic of - // the calling function. - let epoch_valset = self.get_epoch_and_valset(*parent_hash).unwrap(); - let shard_layout = self.get_shard_layout_from_prev_block(parent_hash)?; - let shard_index = shard_layout.get_shard_index(shard_id)?; - let chunk_producers = self.get_chunk_producers(epoch_valset.1, shard_index); - for validator in chunk_producers { - if validator.account_id() == account_id { - return Ok(true); - } - } - Ok(false) - } - - fn cares_about_shard_next_epoch_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - // This `unwrap` here tests that in all code paths we check that the epoch exists before - // we check if we care about a shard. Please do not remove the unwrap, fix the logic of - // the calling function. - let epoch_valset = self.get_epoch_and_valset(*parent_hash).unwrap(); - let shard_layout = self.get_shard_layout_from_prev_block(parent_hash)?; - let shard_index = shard_layout.get_shard_index(shard_id)?; - let chunk_producers = self.get_chunk_producers( - (epoch_valset.1 + 1) % self.validators_by_valset.len(), - shard_index, - ); - for validator in chunk_producers { - if validator.account_id() == account_id { - return Ok(true); - } - } - Ok(false) - } - - fn cared_about_shard_prev_epoch_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - // This `unwrap` here tests that in all code paths we check that the epoch exists before - // we check if we care about a shard. Please do not remove the unwrap, fix the logic of - // the calling function. - let epoch_valset = self.get_epoch_and_valset(*parent_hash).unwrap(); - let shard_layout = self.get_shard_layout_from_prev_block(parent_hash)?; - let shard_index = shard_layout.get_shard_index(shard_id)?; - let chunk_producers = self.get_chunk_producers( - (epoch_valset.1.wrapping_sub(1)) % self.validators_by_valset.len(), - shard_index, - ); - for validator in chunk_producers { - if validator.account_id() == account_id { - return Ok(true); - } - } - Ok(false) - } - fn will_shard_layout_change(&self, parent_hash: &CryptoHash) -> Result { // Copied from EpochManager (KeyValueRuntime is deprecated anyway). let epoch_id = self.get_epoch_id_from_prev_block(parent_hash)?; diff --git a/chain/chain/src/validate.rs b/chain/chain/src/validate.rs index a499c690f6b..bb335cc39de 100644 --- a/chain/chain/src/validate.rs +++ b/chain/chain/src/validate.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use borsh::BorshDeserialize; use near_crypto::PublicKey; +use near_epoch_manager::shard_tracker::get_shard_layout_from_prev_block; use near_epoch_manager::EpochManagerAdapter; use near_primitives::bandwidth_scheduler::BandwidthRequests; use near_primitives::block::{Block, BlockHeader}; @@ -65,7 +66,7 @@ pub fn validate_chunk_proofs( } else { let shard_layout = { let prev_block_hash = chunk.prev_block_hash(); - epoch_manager.get_shard_layout_from_prev_block(&prev_block_hash)? + get_shard_layout_from_prev_block(epoch_manager, &prev_block_hash)? }; let outgoing_receipts_hashes = Chain::build_receipts_hashes(receipts, &shard_layout); let (receipts_root, _) = merklize(&outgoing_receipts_hashes); @@ -126,7 +127,7 @@ pub fn validate_chunk_with_chunk_extra( prev_chunk_height_included, )?; let outgoing_receipts_hashes = { - let shard_layout = epoch_manager.get_shard_layout_from_prev_block(prev_block_hash)?; + let shard_layout = get_shard_layout_from_prev_block(epoch_manager, prev_block_hash)?; Chain::build_receipts_hashes(&outgoing_receipts, &shard_layout) }; let (outgoing_receipts_root, _) = merklize(&outgoing_receipts_hashes); diff --git a/chain/chunks/src/logic.rs b/chain/chunks/src/logic.rs index 2687e15fe89..1ea1bee704e 100644 --- a/chain/chunks/src/logic.rs +++ b/chain/chunks/src/logic.rs @@ -3,7 +3,7 @@ use near_chain::{ types::EpochManagerAdapter, validate::validate_chunk_proofs, BlockHeader, Chain, ChainStore, }; use near_chunks_primitives::Error; -use near_epoch_manager::shard_tracker::ShardTracker; +use near_epoch_manager::shard_tracker::{get_shard_layout_from_prev_block, ShardTracker}; use near_primitives::{ errors::EpochError, hash::CryptoHash, @@ -90,7 +90,7 @@ pub fn make_outgoing_receipts_proofs( ) -> Result, EpochError> { let shard_id = chunk_header.shard_id(); let shard_layout = - epoch_manager.get_shard_layout_from_prev_block(chunk_header.prev_block_hash())?; + get_shard_layout_from_prev_block(epoch_manager, chunk_header.prev_block_hash())?; let hashes = Chain::build_receipts_hashes(&outgoing_receipts, &shard_layout); let (root, proofs) = merklize(&hashes); diff --git a/chain/client/src/client.rs b/chain/client/src/client.rs index 19a3aaad747..f9044cbec6d 100644 --- a/chain/client/src/client.rs +++ b/chain/client/src/client.rs @@ -41,7 +41,9 @@ use near_chunks::shards_manager_actor::ShardsManagerActor; use near_client_primitives::debug::ChunkProduction; use near_client_primitives::types::{Error, StateSyncStatus}; use near_epoch_manager::shard_assignment::{account_id_to_shard_id, shard_id_to_uid}; -use near_epoch_manager::shard_tracker::ShardTracker; +use near_epoch_manager::shard_tracker::{ + get_prev_shard_id_from_prev_hash, get_shard_layout_from_prev_block, ShardTracker, +}; use near_epoch_manager::EpochManagerAdapter; use near_network::client::ProcessTxResponse; use near_network::types::{AccountKeys, ChainInfo, PeerManagerMessageRequest, SetChainInfo}; @@ -1023,8 +1025,11 @@ impl Client { .get_chunk_extra(&prev_block_hash, &shard_uid) .map_err(|err| Error::ChunkProducer(format!("No chunk extra available: {}", err)))?; - let (_, prev_shard_id, prev_shard_index) = - self.epoch_manager.get_prev_shard_id_from_prev_hash(prev_block.hash(), shard_id)?; + let (_, prev_shard_id, prev_shard_index) = get_prev_shard_id_from_prev_hash( + self.epoch_manager.as_ref(), + prev_block.hash(), + shard_id, + )?; let last_chunk_header = prev_block.chunks().get(prev_shard_index).cloned().ok_or_else(|| { Error::ChunkProducer(format!( @@ -1605,10 +1610,9 @@ impl Client { // TODO(#10569) We would like a proper error handling here instead of `expect`. let parent_hash = *chunk_header.prev_block_hash(); - let shard_layout = self - .epoch_manager - .get_shard_layout_from_prev_block(&parent_hash) - .expect("Could not obtain shard layout"); + let shard_layout = + get_shard_layout_from_prev_block(self.epoch_manager.as_ref(), &parent_hash) + .expect("Could not obtain shard layout"); let shard_id = partial_chunk.shard_id(); let shard_index = @@ -1804,9 +1808,11 @@ impl Client { // layout after the pool resharding if self.epoch_manager.is_next_block_epoch_start(&block_hash).unwrap_or(false) { let new_shard_layout = - self.epoch_manager.get_shard_layout_from_prev_block(&block_hash); - let old_shard_layout = - self.epoch_manager.get_shard_layout_from_prev_block(block.header().prev_hash()); + get_shard_layout_from_prev_block(self.epoch_manager.as_ref(), &block_hash); + let old_shard_layout = get_shard_layout_from_prev_block( + self.epoch_manager.as_ref(), + block.header().prev_hash(), + ); match (old_shard_layout, new_shard_layout) { (Ok(old_shard_layout), Ok(new_shard_layout)) => { if old_shard_layout != new_shard_layout { diff --git a/chain/client/src/stateless_validation/state_witness_producer.rs b/chain/client/src/stateless_validation/state_witness_producer.rs index 08332019102..f0e1d783be0 100644 --- a/chain/client/src/stateless_validation/state_witness_producer.rs +++ b/chain/client/src/stateless_validation/state_witness_producer.rs @@ -5,6 +5,9 @@ use near_async::messaging::{CanSend, IntoSender}; use near_chain::{BlockHeader, Chain, ChainStoreAccess, ReceiptFilter}; use near_chain_primitives::Error; use near_epoch_manager::shard_assignment::shard_id_to_uid; +use near_epoch_manager::shard_tracker::{ + get_prev_shard_id_from_prev_hash, get_shard_layout_from_prev_block, +}; use near_o11y::log_assert_fail; use near_primitives::checked_feature; use near_primitives::hash::{hash, CryptoHash}; @@ -208,10 +211,12 @@ impl Client { } let current_epoch_id = *header.epoch_id(); - let current_shard_id = self - .epoch_manager - .get_prev_shard_id_from_prev_hash(¤t_block_hash, next_shard_id)? - .1; + let current_shard_id = get_prev_shard_id_from_prev_hash( + self.epoch_manager.as_ref(), + ¤t_block_hash, + next_shard_id, + )? + .1; if current_shard_id != next_shard_id { // If shard id changes, we need to get implicit state // transition from current shard id to the next shard id. @@ -367,9 +372,10 @@ impl Client { // Fetch all incoming receipts for `prev_chunk`. // They will be between `prev_prev_chunk.height_included` (first block containing `prev_prev_chunk`) // and `prev_chunk_original_block` - let shard_layout = self - .epoch_manager - .get_shard_layout_from_prev_block(prev_chunk_original_block.prev_hash())?; + let shard_layout = get_shard_layout_from_prev_block( + self.epoch_manager.as_ref(), + prev_chunk_original_block.prev_hash(), + )?; let incoming_receipt_proofs = self.chain.chain_store().get_incoming_receipts_for_shard( self.epoch_manager.as_ref(), prev_chunk_header.shard_id(), diff --git a/chain/client/src/test_utils/test_env.rs b/chain/client/src/test_utils/test_env.rs index 92dfde6904f..6bee445071a 100644 --- a/chain/client/src/test_utils/test_env.rs +++ b/chain/client/src/test_utils/test_env.rs @@ -14,6 +14,7 @@ use near_chunks::client::ShardsManagerResponse; use near_chunks::test_utils::{MockClientAdapterForShardsManager, SynchronousShardsManagerAdapter}; use near_crypto::{InMemorySigner, Signer}; use near_epoch_manager::shard_assignment::{account_id_to_shard_id, shard_id_to_uid}; +use near_epoch_manager::shard_tracker::cares_about_shard_from_prev_block; use near_network::client::ProcessTxResponse; use near_network::shards_manager::ShardsManagerRequestFromNetwork; use near_network::test_utils::MockPeerManagerAdapter; @@ -567,14 +568,13 @@ impl TestEnv { let last_chunk_header = &last_block.chunks()[shard_index]; for i in 0..self.clients.len() { - let tracks_shard = self.clients[i] - .epoch_manager - .cares_about_shard_from_prev_block( - &head.prev_block_hash, - &self.get_client_id(i), - shard_id, - ) - .unwrap(); + let tracks_shard = cares_about_shard_from_prev_block( + self.clients[i].epoch_manager.as_ref(), + &head.prev_block_hash, + &self.get_client_id(i), + shard_id, + ) + .unwrap(); if tracks_shard { let response = self.clients[i] .runtime_adapter diff --git a/chain/client/src/test_utils/test_loop.rs b/chain/client/src/test_utils/test_loop.rs index 9d77bb2e8c1..bf23bc887cd 100644 --- a/chain/client/src/test_utils/test_loop.rs +++ b/chain/client/src/test_utils/test_loop.rs @@ -1,4 +1,5 @@ use near_epoch_manager::shard_assignment::{account_id_to_shard_id, shard_id_to_uid}; +use near_epoch_manager::shard_tracker::cares_about_shard_from_prev_block; use near_primitives::hash::CryptoHash; use near_primitives::types::{AccountId, Balance, ShardId}; use near_primitives::views::{ @@ -32,10 +33,13 @@ where let client: &Client = self[i].as_ref(); let validator_signer = client.validator_signer.get().unwrap(); let account_id = validator_signer.validator_id(); - let tracks_shard = client - .epoch_manager - .cares_about_shard_from_prev_block(&head.prev_block_hash, account_id, shard_id) - .unwrap(); + let tracks_shard = cares_about_shard_from_prev_block( + client.epoch_manager.as_ref(), + &head.prev_block_hash, + &account_id, + shard_id, + ) + .unwrap(); if tracks_shard { return i; } @@ -118,10 +122,13 @@ where let account_id = validator_signer.validator_id(); let mut tracked_shards = Vec::new(); for shard_id in &all_shard_ids { - let tracks_shard = client - .epoch_manager - .cares_about_shard_from_prev_block(&head.prev_block_hash, account_id, *shard_id) - .unwrap(); + let tracks_shard = cares_about_shard_from_prev_block( + client.epoch_manager.as_ref(), + &head.prev_block_hash, + account_id, + *shard_id, + ) + .unwrap(); if tracks_shard { tracked_shards.push(*shard_id); } diff --git a/chain/epoch-manager/src/adapter.rs b/chain/epoch-manager/src/adapter.rs index c29f3e874ae..c4c03871a2b 100644 --- a/chain/epoch-manager/src/adapter.rs +++ b/chain/epoch-manager/src/adapter.rs @@ -16,8 +16,7 @@ use near_primitives::stateless_validation::validator_assignment::ChunkValidatorA use near_primitives::stateless_validation::ChunkProductionKey; use near_primitives::types::validator_stake::ValidatorStake; use near_primitives::types::{ - AccountId, ApprovalStake, BlockHeight, EpochHeight, EpochId, ShardId, ShardIndex, - ValidatorInfoIdentifier, + AccountId, ApprovalStake, BlockHeight, EpochHeight, EpochId, ShardId, ValidatorInfoIdentifier, }; use near_primitives::version::ProtocolVersion; use near_primitives::views::EpochValidatorInfo; @@ -95,41 +94,6 @@ pub trait EpochManagerAdapter: Send + Sync { parent_hash: &CryptoHash, ) -> Result; - /// For each `ShardId` in the current block, returns its parent `ShardId` - /// from previous block. - /// - /// Most of the times parent of the shard is the shard itself, unless a - /// resharding happened and some shards were split. - /// If there was no resharding, it just returns `shard_ids` as is, without any validation. - /// The resulting Vec will always be of the same length as the `shard_ids` argument. - /// - /// TODO(wacban) - rename to reflect the new return type - fn get_prev_shard_ids( - &self, - prev_hash: &CryptoHash, - shard_ids: Vec, - ) -> Result, Error>; - - /// For a `ShardId` in the current block, returns its parent `ShardId` - /// from previous block. - /// - /// Most of the times parent of the shard is the shard itself, unless a - /// resharding happened and some shards were split. - /// If there was no resharding, it just returns the `shard_id` as is, without any validation. - /// - /// TODO(wacban) - rename to reflect the new return type - fn get_prev_shard_id_from_prev_hash( - &self, - prev_hash: &CryptoHash, - shard_id: ShardId, - ) -> Result<(ShardLayout, ShardId, ShardIndex), EpochError>; - - /// Get shard layout given hash of previous block. - fn get_shard_layout_from_prev_block( - &self, - parent_hash: &CryptoHash, - ) -> Result; - fn get_shard_layout_from_protocol_version( &self, protocol_version: ProtocolVersion, @@ -340,34 +304,6 @@ pub trait EpochManagerAdapter: Send + Sync { request: &ContractCodeRequest, ) -> Result; - fn cares_about_shard_in_epoch( - &self, - epoch_id: &EpochId, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result; - - fn cares_about_shard_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result; - - fn cares_about_shard_next_epoch_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result; - - fn cared_about_shard_prev_epoch_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result; - fn will_shard_layout_change(&self, parent_hash: &CryptoHash) -> Result; /// Tries to estimate in which epoch the given height would reside. @@ -547,61 +483,6 @@ impl EpochManagerAdapter for EpochManagerHandle { epoch_manager.get_next_epoch_id_from_prev_block(parent_hash) } - fn get_prev_shard_ids( - &self, - prev_hash: &CryptoHash, - shard_ids: Vec, - ) -> Result, Error> { - let shard_layout = self.get_shard_layout_from_prev_block(prev_hash)?; - let prev_shard_layout = self.get_shard_layout(&self.get_epoch_id(prev_hash)?)?; - let is_resharding_boundary = - self.is_next_block_epoch_start(prev_hash)? && prev_shard_layout != shard_layout; - - let mut result = vec![]; - if is_resharding_boundary { - for shard_id in shard_ids { - let parent_shard_id = shard_layout.get_parent_shard_id(shard_id)?; - let parent_shard_index = prev_shard_layout.get_shard_index(parent_shard_id)?; - result.push((parent_shard_id, parent_shard_index)); - } - Ok(result) - } else { - for shard_id in shard_ids { - let shard_index = shard_layout.get_shard_index(shard_id)?; - result.push((shard_id, shard_index)); - } - Ok(result) - } - } - - fn get_prev_shard_id_from_prev_hash( - &self, - prev_hash: &CryptoHash, - shard_id: ShardId, - ) -> Result<(ShardLayout, ShardId, ShardIndex), EpochError> { - let shard_layout = self.get_shard_layout_from_prev_block(prev_hash)?; - let prev_shard_layout = self.get_shard_layout(&self.get_epoch_id(prev_hash)?)?; - let is_resharding_boundary = - self.is_next_block_epoch_start(prev_hash)? && prev_shard_layout != shard_layout; - - if is_resharding_boundary { - let parent_shard_id = shard_layout.get_parent_shard_id(shard_id)?; - let parent_shard_index = prev_shard_layout.get_shard_index(parent_shard_id)?; - Ok((prev_shard_layout, parent_shard_id, parent_shard_index)) - } else { - let shard_index = shard_layout.get_shard_index(shard_id)?; - Ok((shard_layout, shard_id, shard_index)) - } - } - - fn get_shard_layout_from_prev_block( - &self, - parent_hash: &CryptoHash, - ) -> Result { - let epoch_id = self.get_epoch_id_from_prev_block(parent_hash)?; - self.get_shard_layout(&epoch_id) - } - fn get_shard_layout_from_protocol_version( &self, protocol_version: ProtocolVersion, @@ -870,46 +751,6 @@ impl EpochManagerAdapter for EpochManagerHandle { Ok(request.verify_signature(validator.public_key())) } - fn cares_about_shard_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - let epoch_manager = self.read(); - epoch_manager.cares_about_shard_from_prev_block(parent_hash, account_id, shard_id) - } - - fn cares_about_shard_next_epoch_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - let epoch_manager = self.read(); - epoch_manager.cares_about_shard_next_epoch_from_prev_block( - parent_hash, - account_id, - shard_id, - ) - } - - // `shard_id` always refers to a shard in the current epoch that the next block from `parent_hash` belongs - // If shard layout changed after the prev epoch, returns true if the account cared about the parent shard - fn cared_about_shard_prev_epoch_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - let (_layout, parent_shard_id, _index) = - self.get_prev_shard_id_from_prev_hash(parent_hash, shard_id)?; - let prev_epoch_id = self.get_prev_epoch_id_from_prev_block(parent_hash)?; - - let epoch_manager = self.read(); - epoch_manager.cares_about_shard_in_epoch(&prev_epoch_id, account_id, parent_shard_id) - } - fn will_shard_layout_change(&self, parent_hash: &CryptoHash) -> Result { let epoch_manager = self.read(); epoch_manager.will_shard_layout_change(parent_hash) @@ -932,14 +773,4 @@ impl EpochManagerAdapter for EpochManagerHandle { let epoch_manager = self.read(); Ok(epoch_manager.get_epoch_info(epoch_id)?.validators_iter().collect::>()) } - - fn cares_about_shard_in_epoch( - &self, - epoch_id: &EpochId, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - let epoch_manager = self.read(); - epoch_manager.cares_about_shard_in_epoch(epoch_id, account_id, shard_id) - } } diff --git a/chain/epoch-manager/src/lib.rs b/chain/epoch-manager/src/lib.rs index 4d841612388..413ea1cdc35 100644 --- a/chain/epoch-manager/src/lib.rs +++ b/chain/epoch-manager/src/lib.rs @@ -1248,72 +1248,6 @@ impl EpochManager { self.get_epoch_info(&epoch_id) } - pub fn cares_about_shard_in_epoch( - &self, - epoch_id: &EpochId, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - let epoch_info = self.get_epoch_info(epoch_id)?; - - let shard_layout = self.get_shard_layout(epoch_id)?; - let shard_index = shard_layout.get_shard_index(shard_id)?; - - let chunk_producers_settlement = epoch_info.chunk_producers_settlement(); - let chunk_producers = chunk_producers_settlement - .get(shard_index) - .ok_or_else(|| EpochError::ShardingError(format!("invalid shard id {shard_id}")))?; - for validator_id in chunk_producers.iter() { - if epoch_info.validator_account_id(*validator_id) == account_id { - return Ok(true); - } - } - Ok(false) - } - - pub fn cares_about_shard_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - let epoch_id = self.get_epoch_id_from_prev_block(parent_hash)?; - self.cares_about_shard_in_epoch(&epoch_id, account_id, shard_id) - } - - // `shard_id` always refers to a shard in the current epoch that the next block from `parent_hash` belongs - // If shard layout will change next epoch, returns true if it cares about any shard - // that `shard_id` will split to - pub fn cares_about_shard_next_epoch_from_prev_block( - &self, - parent_hash: &CryptoHash, - account_id: &AccountId, - shard_id: ShardId, - ) -> Result { - let next_epoch_id = self.get_next_epoch_id_from_prev_block(parent_hash)?; - if self.will_shard_layout_change(parent_hash)? { - let shard_layout = self.get_shard_layout(&next_epoch_id)?; - // The expect below may be triggered when the protocol version - // changes by multiple versions at once and multiple shard layout - // changes are captured. In this case the shards from the original - // shard layout are not valid parents in the final shard layout. - // - // This typically occurs in tests that are pegged to start at a - // certain protocol version and then upgrade to stable. - let split_shards = shard_layout - .get_children_shards_ids(shard_id) - .unwrap_or_else(|| panic!("all shard layouts expect the first one must have a split map, shard_id={shard_id}, shard_layout={shard_layout:?}")); - for next_shard_id in split_shards { - if self.cares_about_shard_in_epoch(&next_epoch_id, account_id, next_shard_id)? { - return Ok(true); - } - } - Ok(false) - } else { - self.cares_about_shard_in_epoch(&next_epoch_id, account_id, shard_id) - } - } - /// Returns true if next block after given block hash is in the new epoch. pub fn is_next_block_epoch_start(&self, parent_hash: &CryptoHash) -> Result { let block_info = self.get_block_info(parent_hash)?; diff --git a/chain/epoch-manager/src/shard_tracker.rs b/chain/epoch-manager/src/shard_tracker.rs index 6e08af9bfc3..6c6bd7420a0 100644 --- a/chain/epoch-manager/src/shard_tracker.rs +++ b/chain/epoch-manager/src/shard_tracker.rs @@ -4,9 +4,11 @@ use crate::EpochManagerAdapter; use itertools::Itertools; use near_cache::SyncLruCache; use near_chain_configs::ClientConfig; +use near_chain_primitives::Error; use near_primitives::errors::EpochError; use near_primitives::hash::CryptoHash; -use near_primitives::types::{AccountId, EpochId, ShardId}; +use near_primitives::shard_layout::ShardLayout; +use near_primitives::types::{AccountId, EpochId, ShardId, ShardIndex}; #[derive(Clone)] pub enum TrackedConfig { @@ -101,9 +103,12 @@ impl ShardTracker { let subset = &schedule[index as usize]; Ok(subset.contains(&shard_id)) } - TrackedConfig::ShadowValidator(account_id) => { - self.epoch_manager.cares_about_shard_in_epoch(epoch_id, account_id, shard_id) - } + TrackedConfig::ShadowValidator(account_id) => cares_about_shard_in_epoch( + self.epoch_manager.as_ref(), + epoch_id, + account_id, + shard_id, + ), } } @@ -149,10 +154,13 @@ impl ShardTracker { // TODO: fix these unwrap_or here and handle error correctly. The current behavior masks potential errors and bugs // https://github.com/near/nearcore/issues/4936 if let Some(account_id) = account_id { - let account_cares_about_shard = self - .epoch_manager - .cared_about_shard_prev_epoch_from_prev_block(parent_hash, account_id, shard_id) - .unwrap_or(false); + let account_cares_about_shard = cared_about_shard_prev_epoch_from_prev_block( + self.epoch_manager.as_ref(), + parent_hash, + account_id, + shard_id, + ) + .unwrap_or(false); if account_cares_about_shard { // An account has to track this shard because of its validation duties. return true; @@ -192,10 +200,13 @@ impl ShardTracker { // TODO: fix these unwrap_or here and handle error correctly. The current behavior masks potential errors and bugs // https://github.com/near/nearcore/issues/4936 if let Some(account_id) = account_id { - let account_cares_about_shard = self - .epoch_manager - .cares_about_shard_from_prev_block(parent_hash, account_id, shard_id) - .unwrap_or(false); + let account_cares_about_shard = cares_about_shard_from_prev_block( + self.epoch_manager.as_ref(), + parent_hash, + account_id, + shard_id, + ) + .unwrap_or(false); if account_cares_about_shard { // An account has to track this shard because of its validation duties. return true; @@ -235,9 +246,13 @@ impl ShardTracker { ) -> bool { if let Some(account_id) = account_id { let account_cares_about_shard = { - self.epoch_manager - .cares_about_shard_next_epoch_from_prev_block(parent_hash, account_id, shard_id) - .unwrap_or(false) + cares_about_shard_next_epoch_from_prev_block( + self.epoch_manager.as_ref(), + parent_hash, + account_id, + shard_id, + ) + .unwrap_or(false) }; if account_cares_about_shard { // An account has to track this shard because of its validation duties. @@ -280,6 +295,163 @@ impl ShardTracker { } } +/// For each `ShardId` in the current block, returns its parent `ShardId` +/// from previous block. +/// +/// Most of the times parent of the shard is the shard itself, unless a +/// resharding happened and some shards were split. +/// If there was no resharding, it just returns `shard_ids` as is, without any validation. +/// The resulting Vec will always be of the same length as the `shard_ids` argument. +/// +/// TODO(wacban) - rename to reflect the new return type +pub fn get_prev_shard_ids( + epoch_manager: &dyn EpochManagerAdapter, + prev_hash: &CryptoHash, + shard_ids: Vec, +) -> Result, Error> { + let shard_layout = get_shard_layout_from_prev_block(epoch_manager, prev_hash)?; + let prev_shard_layout = + epoch_manager.get_shard_layout(&epoch_manager.get_epoch_id(prev_hash)?)?; + let is_resharding_boundary = + epoch_manager.is_next_block_epoch_start(prev_hash)? && prev_shard_layout != shard_layout; + + let mut result = vec![]; + if is_resharding_boundary { + for shard_id in shard_ids { + let parent_shard_id = shard_layout.get_parent_shard_id(shard_id)?; + let parent_shard_index = prev_shard_layout.get_shard_index(parent_shard_id)?; + result.push((parent_shard_id, parent_shard_index)); + } + Ok(result) + } else { + for shard_id in shard_ids { + let shard_index = shard_layout.get_shard_index(shard_id)?; + result.push((shard_id, shard_index)); + } + Ok(result) + } +} + +/// For a `ShardId` in the current block, returns its parent `ShardId` +/// from previous block. +/// +/// Most of the times parent of the shard is the shard itself, unless a +/// resharding happened and some shards were split. +/// If there was no resharding, it just returns the `shard_id` as is, without any validation. +/// +/// TODO(wacban) - rename to reflect the new return type +pub fn get_prev_shard_id_from_prev_hash( + epoch_manager: &dyn EpochManagerAdapter, + prev_hash: &CryptoHash, + shard_id: ShardId, +) -> Result<(ShardLayout, ShardId, ShardIndex), EpochError> { + let shard_layout = get_shard_layout_from_prev_block(epoch_manager, prev_hash)?; + let prev_shard_layout = + epoch_manager.get_shard_layout(&epoch_manager.get_epoch_id(prev_hash)?)?; + let is_resharding_boundary = + epoch_manager.is_next_block_epoch_start(prev_hash)? && prev_shard_layout != shard_layout; + + if is_resharding_boundary { + let parent_shard_id = shard_layout.get_parent_shard_id(shard_id)?; + let parent_shard_index = prev_shard_layout.get_shard_index(parent_shard_id)?; + Ok((prev_shard_layout, parent_shard_id, parent_shard_index)) + } else { + let shard_index = shard_layout.get_shard_index(shard_id)?; + Ok((shard_layout, shard_id, shard_index)) + } +} + +/// Get shard layout given hash of previous block. +pub fn get_shard_layout_from_prev_block( + epoch_manager: &dyn EpochManagerAdapter, + parent_hash: &CryptoHash, +) -> Result { + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(parent_hash)?; + epoch_manager.get_shard_layout(&epoch_id) +} + +pub fn cares_about_shard_in_epoch( + epoch_manager: &dyn EpochManagerAdapter, + epoch_id: &EpochId, + account_id: &AccountId, + shard_id: ShardId, +) -> Result { + let epoch_info = epoch_manager.get_epoch_info(epoch_id)?; + + let shard_layout = epoch_manager.get_shard_layout(epoch_id)?; + let shard_index = shard_layout.get_shard_index(shard_id)?; + + let chunk_producers_settlement = epoch_info.chunk_producers_settlement(); + let chunk_producers = chunk_producers_settlement + .get(shard_index) + .ok_or_else(|| EpochError::ShardingError(format!("invalid shard id {shard_id}")))?; + for validator_id in chunk_producers.iter() { + if epoch_info.validator_account_id(*validator_id) == account_id { + return Ok(true); + } + } + Ok(false) +} + +// `shard_id` always refers to a shard in the current epoch that the next block from `parent_hash` belongs +// If shard layout changed after the prev epoch, returns true if the account cared about the parent shard +pub fn cared_about_shard_prev_epoch_from_prev_block( + epoch_manager: &dyn EpochManagerAdapter, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, +) -> Result { + let (_layout, parent_shard_id, _index) = + get_prev_shard_id_from_prev_hash(epoch_manager, parent_hash, shard_id)?; + let prev_epoch_id = epoch_manager.get_prev_epoch_id_from_prev_block(parent_hash)?; + + cares_about_shard_in_epoch(epoch_manager, &prev_epoch_id, account_id, parent_shard_id) +} + +pub fn cares_about_shard_from_prev_block( + epoch_manager: &dyn EpochManagerAdapter, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, +) -> Result { + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(parent_hash)?; + cares_about_shard_in_epoch(epoch_manager, &epoch_id, account_id, shard_id) +} + +// `shard_id` always refers to a shard in the current epoch that the next block from `parent_hash` belongs +// If shard layout will change next epoch, returns true if it cares about any shard +// that `shard_id` will split to +pub fn cares_about_shard_next_epoch_from_prev_block( + epoch_manager: &dyn EpochManagerAdapter, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, +) -> Result { + let next_epoch_id = epoch_manager.get_next_epoch_id_from_prev_block(parent_hash)?; + if epoch_manager.will_shard_layout_change(parent_hash)? { + let shard_layout = epoch_manager.get_shard_layout(&next_epoch_id)?; + // The expect below may be triggered when the protocol version + // changes by multiple versions at once and multiple shard layout + // changes are captured. In this case the shards from the original + // shard layout are not valid parents in the final shard layout. + // + // This typically occurs in tests that are pegged to start at a + // certain protocol version and then upgrade to stable. + let split_shards = shard_layout + .get_children_shards_ids(shard_id) + .unwrap_or_else(|| panic!("all shard layouts expect the first one must have a split map, shard_id={shard_id}, shard_layout={shard_layout:?}")); + for next_shard_id in split_shards { + if cares_about_shard_in_epoch(epoch_manager, &next_epoch_id, account_id, next_shard_id)? + { + return Ok(true); + } + } + Ok(false) + } else { + cares_about_shard_in_epoch(epoch_manager, &next_epoch_id, account_id, shard_id) + } +} + #[cfg(test)] mod tests { use super::ShardTracker; diff --git a/integration-tests/src/test_loop/tests/bandwidth_scheduler.rs b/integration-tests/src/test_loop/tests/bandwidth_scheduler.rs index 29691a60891..46eda497b21 100644 --- a/integration-tests/src/test_loop/tests/bandwidth_scheduler.rs +++ b/integration-tests/src/test_loop/tests/bandwidth_scheduler.rs @@ -30,6 +30,7 @@ use near_chain_configs::test_genesis::{ use near_client::client_actor::ClientActorInner; use near_client::Client; use near_crypto::Signer; +use near_epoch_manager::shard_tracker::get_prev_shard_id_from_prev_hash; use near_o11y::testonly::init_test_logger; use near_primitives::account::{AccessKey, AccessKeyPermission}; use near_primitives::action::{Action, FunctionCallAction}; @@ -282,10 +283,13 @@ fn analyze_workload_blocks( let shard_id = new_chunk.shard_id(); let shard_index = cur_shard_layout.get_shard_index(shard_id).unwrap(); let shard_uid = ShardUId::new(cur_shard_layout.version(), shard_id); - let prev_shard_index = epoch_manager - .get_prev_shard_id_from_prev_hash(block.header().prev_hash(), shard_id) - .unwrap() - .2; + let prev_shard_index = get_prev_shard_id_from_prev_hash( + epoch_manager, + block.header().prev_hash(), + shard_id, + ) + .unwrap() + .2; let prev_height_included = prev_block.chunks().get(prev_shard_index).unwrap().height_included(); diff --git a/integration-tests/src/test_loop/tests/max_receipt_size.rs b/integration-tests/src/test_loop/tests/max_receipt_size.rs index 80fc0497e05..93c16bce5fa 100644 --- a/integration-tests/src/test_loop/tests/max_receipt_size.rs +++ b/integration-tests/src/test_loop/tests/max_receipt_size.rs @@ -1,6 +1,7 @@ use assert_matches::assert_matches; use near_async::time::Duration; use near_chain::{ChainStoreAccess, ReceiptFilter}; +use near_epoch_manager::shard_tracker::get_prev_shard_id_from_prev_hash; use near_o11y::testonly::init_test_logger; use near_primitives::action::{Action, FunctionCallAction}; use near_primitives::block::MaybeNew; @@ -397,10 +398,13 @@ fn assert_oversized_receipt_occurred(env: &TestLoopEnv) { continue; }; let shard_id = new_chunk.shard_id(); - let prev_shard_index = epoch_manager - .get_prev_shard_id_from_prev_hash(block.header().prev_hash(), shard_id) - .unwrap() - .2; + let prev_shard_index = get_prev_shard_id_from_prev_hash( + epoch_manager, + block.header().prev_hash(), + shard_id, + ) + .unwrap() + .2; let prev_height_included = prev_block.chunks().get(prev_shard_index).unwrap().height_included(); diff --git a/integration-tests/src/test_loop/utils/sharding.rs b/integration-tests/src/test_loop/utils/sharding.rs index 7e4ef35be71..5d529b65454 100644 --- a/integration-tests/src/test_loop/utils/sharding.rs +++ b/integration-tests/src/test_loop/utils/sharding.rs @@ -1,6 +1,6 @@ use near_chain::types::Tip; use near_client::Client; -use near_epoch_manager::EpochManagerAdapter; +use near_epoch_manager::{shard_tracker::get_shard_layout_from_prev_block, EpochManagerAdapter}; use near_primitives::hash::CryptoHash; use near_primitives::shard_layout::ShardLayout; use near_primitives::state_record::StateRecord; @@ -127,7 +127,7 @@ pub fn get_tracked_shards_from_prev_block( let signer = client.validator_signer.get(); let account_id = signer.as_ref().map(|s| s.validator_id()); let shard_layout = - client.epoch_manager.get_shard_layout_from_prev_block(prev_block_hash).unwrap(); + get_shard_layout_from_prev_block(client.epoch_manager.as_ref(), prev_block_hash).unwrap(); let mut tracked_shards = vec![]; for shard_uid in shard_layout.shard_uids() { if client.shard_tracker.care_about_shard( diff --git a/integration-tests/src/tests/client/resharding_v2.rs b/integration-tests/src/tests/client/resharding_v2.rs index 4cdebcdc444..39eff453fbd 100644 --- a/integration-tests/src/tests/client/resharding_v2.rs +++ b/integration-tests/src/tests/client/resharding_v2.rs @@ -6,6 +6,7 @@ use near_chain_configs::{Genesis, NEAR_BASE}; use near_client::test_utils::{run_catchup, TestEnv}; use near_client::ProcessTxResponse; use near_crypto::InMemorySigner; +use near_epoch_manager::shard_tracker::get_shard_layout_from_prev_block; use near_o11y::testonly::init_test_logger; use near_primitives::account::id::AccountId; use near_primitives::block::{Block, Tip}; @@ -397,10 +398,11 @@ impl TestReshardingEnv { vec![shard_id] } else { // different shard layout between block and last_block - let shard_layout = client - .epoch_manager - .get_shard_layout_from_prev_block(&last_block_hash) - .unwrap(); + let shard_layout = get_shard_layout_from_prev_block( + client.epoch_manager.as_ref(), + &last_block_hash, + ) + .unwrap(); shard_layout.get_children_shards_ids(shard_id).unwrap() }; @@ -429,10 +431,11 @@ impl TestReshardingEnv { let head = env.clients[0].chain.head().unwrap(); let block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); // check execution outcomes - let shard_layout = env.clients[0] - .epoch_manager - .get_shard_layout_from_prev_block(&head.last_block_hash) - .unwrap(); + let shard_layout = get_shard_layout_from_prev_block( + env.clients[0].epoch_manager.as_ref(), + &head.last_block_hash, + ) + .unwrap(); let mut txs_to_check = vec![]; txs_to_check.extend(&self.init_txs); for (_, txs) in self.txs_by_height.iter() { @@ -521,7 +524,7 @@ fn check_account(env: &TestEnv, account_id: &AccountId, block: &Block) { tracing::trace!(target: "test", ?account_id, block_height=block.header().height(), "checking account"); let prev_hash = block.header().prev_hash(); let shard_layout = - env.clients[0].epoch_manager.get_shard_layout_from_prev_block(prev_hash).unwrap(); + get_shard_layout_from_prev_block(env.clients[0].epoch_manager.as_ref(), prev_hash).unwrap(); let shard_uid = shard_layout.account_id_to_shard_uid(account_id); let shard_id = shard_uid.shard_id(); let shard_index = shard_layout.get_shard_index(shard_id).unwrap(); diff --git a/nearcore/src/entity_debug.rs b/nearcore/src/entity_debug.rs index d8f3eaf6ea5..d27c96afa84 100644 --- a/nearcore/src/entity_debug.rs +++ b/nearcore/src/entity_debug.rs @@ -5,6 +5,7 @@ use borsh::BorshDeserialize; use near_chain::types::{LatestKnown, RuntimeAdapter}; use near_chain::{Block, BlockHeader}; use near_epoch_manager::shard_assignment::{account_id_to_shard_id, shard_id_to_uid}; +use near_epoch_manager::shard_tracker::get_shard_layout_from_prev_block; use near_epoch_manager::types::EpochInfoAggregator; use near_epoch_manager::EpochManagerAdapter; use near_jsonrpc_primitives::errors::RpcError; @@ -264,9 +265,10 @@ impl EntityDebugHandlerImpl { let chunk = store .get_ser::(DBCol::Chunks, &borsh::to_vec(&chunk_hash).unwrap())? .ok_or_else(|| anyhow!("Chunk not found"))?; - let shard_layout = self - .epoch_manager - .get_shard_layout_from_prev_block(&chunk.cloned_header().prev_block_hash())?; + let shard_layout = get_shard_layout_from_prev_block( + self.epoch_manager.as_ref(), + &chunk.cloned_header().prev_block_hash(), + )?; let shard_id = chunk.shard_id(); let shard_index = shard_layout.get_shard_index(shard_id).map_err(Into::::into)?; @@ -396,9 +398,10 @@ impl EntityDebugHandlerImpl { let chunk = store .get_ser::(DBCol::Chunks, &borsh::to_vec(&chunk_hash).unwrap())? .ok_or_else(|| anyhow!("Chunk not found"))?; - let shard_layout = self - .epoch_manager - .get_shard_layout_from_prev_block(&chunk.cloned_header().prev_block_hash())?; + let shard_layout = get_shard_layout_from_prev_block( + self.epoch_manager.as_ref(), + &chunk.cloned_header().prev_block_hash(), + )?; let shard_id = chunk.shard_id(); let shard_index = shard_layout.get_shard_index(shard_id).map_err(Into::::into)?; diff --git a/tools/replay-archive/src/cli.rs b/tools/replay-archive/src/cli.rs index 0d5eb6ca53b..b12d89f3385 100644 --- a/tools/replay-archive/src/cli.rs +++ b/tools/replay-archive/src/cli.rs @@ -21,6 +21,7 @@ use near_chain::{ use near_chain_configs::GenesisValidationMode; use near_chunks::logic::make_outgoing_receipts_proofs; use near_epoch_manager::shard_assignment::shard_id_to_uid; +use near_epoch_manager::shard_tracker::get_shard_layout_from_prev_block; use near_epoch_manager::EpochManagerAdapter; use near_epoch_manager::{EpochManager, EpochManagerHandle}; use near_primitives::epoch_block_info::BlockInfo; @@ -391,8 +392,10 @@ impl ReplayController { shard_id: ShardId, prev_chunk_height_included: BlockHeight, ) -> Result> { - let shard_layout = - self.epoch_manager.get_shard_layout_from_prev_block(block_header.prev_hash())?; + let shard_layout = get_shard_layout_from_prev_block( + self.epoch_manager.as_ref(), + block_header.prev_hash(), + )?; let receipt_response = &self.chain_store.get_incoming_receipts_for_shard( self.epoch_manager.as_ref(), shard_id, diff --git a/tools/state-viewer/src/apply_chain_range.rs b/tools/state-viewer/src/apply_chain_range.rs index bfbfafb2c36..7d04ea88e3d 100644 --- a/tools/state-viewer/src/apply_chain_range.rs +++ b/tools/state-viewer/src/apply_chain_range.rs @@ -9,6 +9,7 @@ use near_chain::types::{ use near_chain::{ChainStore, ChainStoreAccess, ChainStoreUpdate, ReceiptFilter}; use near_chain_configs::Genesis; use near_epoch_manager::shard_assignment::{shard_id_to_index, shard_id_to_uid}; +use near_epoch_manager::shard_tracker::get_shard_layout_from_prev_block; use near_epoch_manager::{EpochManagerAdapter, EpochManagerHandle}; use near_primitives::apply::ApplyChunkReason; use near_primitives::receipt::DelayedReceiptIndices; @@ -153,7 +154,7 @@ fn apply_block_from_range( .compute_transaction_validity(protocol_version, prev_block.header(), &chunk) .expect("valid transaction calculation"); let shard_layout = - epoch_manager.get_shard_layout_from_prev_block(block.header().prev_hash()).unwrap(); + get_shard_layout_from_prev_block(epoch_manager, block.header().prev_hash()).unwrap(); let receipt_proof_response = chain_store_update .get_incoming_receipts_for_shard( epoch_manager, diff --git a/tools/state-viewer/src/apply_chunk.rs b/tools/state-viewer/src/apply_chunk.rs index f9405a91a2f..998f3647d94 100644 --- a/tools/state-viewer/src/apply_chunk.rs +++ b/tools/state-viewer/src/apply_chunk.rs @@ -7,6 +7,7 @@ use near_chain::types::{ }; use near_chain::{ChainStore, ChainStoreAccess, ReceiptFilter}; use near_epoch_manager::shard_assignment::shard_id_to_uid; +use near_epoch_manager::shard_tracker::get_shard_layout_from_prev_block; use near_epoch_manager::{EpochManagerAdapter, EpochManagerHandle}; use near_primitives::apply::ApplyChunkReason; use near_primitives::bandwidth_scheduler::BlockBandwidthRequests; @@ -75,7 +76,7 @@ fn get_incoming_receipts( receipt_proofs.shuffle(&mut rng); } let mut responses = vec![ReceiptProofResponse(CryptoHash::default(), Arc::new(receipt_proofs))]; - let shard_layout = epoch_manager.get_shard_layout_from_prev_block(prev_hash)?; + let shard_layout = get_shard_layout_from_prev_block(epoch_manager, prev_hash)?; responses.extend_from_slice(&chain_store.store_update().get_incoming_receipts_for_shard( epoch_manager, shard_id, @@ -249,7 +250,7 @@ fn find_tx_or_receipt( for receipt in chunk.prev_outgoing_receipts() { if &receipt.get_hash() == hash { let shard_layout = - epoch_manager.get_shard_layout_from_prev_block(chunk.prev_block())?; + get_shard_layout_from_prev_block(epoch_manager, chunk.prev_block())?; let to_shard = shard_layout.account_id_to_shard_id(receipt.receiver_id()); return Ok(Some((HashType::Receipt, to_shard))); } @@ -438,7 +439,7 @@ fn apply_receipt_in_chunk( for receipt in chunk.prev_outgoing_receipts().iter() { if receipt.get_hash() == *id { let shard_layout = - epoch_manager.get_shard_layout_from_prev_block(chunk.prev_block())?; + get_shard_layout_from_prev_block(epoch_manager, chunk.prev_block())?; let to_shard = shard_layout.account_id_to_shard_id(receipt.receiver_id()); to_apply.insert((height, to_shard)); println!( diff --git a/tools/state-viewer/src/commands.rs b/tools/state-viewer/src/commands.rs index 38c8b2464ab..58f4709f660 100644 --- a/tools/state-viewer/src/commands.rs +++ b/tools/state-viewer/src/commands.rs @@ -26,6 +26,7 @@ use near_chain::{ }; use near_chain_configs::GenesisChangeConfig; use near_epoch_manager::shard_assignment::{shard_id_to_index, shard_id_to_uid}; +use near_epoch_manager::shard_tracker::get_shard_layout_from_prev_block; use near_epoch_manager::{EpochManager, EpochManagerAdapter}; use near_primitives::account::id::AccountId; use near_primitives::apply::ApplyChunkReason; @@ -84,7 +85,7 @@ pub(crate) fn apply_block( let prev_block = chain_store.get_block(block.header().prev_hash()).unwrap(); let chain_store_update = ChainStoreUpdate::new(chain_store); let shard_layout = - epoch_manager.get_shard_layout_from_prev_block(block.header().prev_hash()).unwrap(); + get_shard_layout_from_prev_block(epoch_manager, block.header().prev_hash()).unwrap(); let receipt_proof_response = chain_store_update .get_incoming_receipts_for_shard( epoch_manager, @@ -1268,7 +1269,8 @@ pub(crate) fn print_state_stats(home_dir: &Path, store: Store, near_config: Near load_trie(store.clone(), home_dir, &near_config); let block_hash = *block_header.hash(); - let shard_layout = epoch_manager.get_shard_layout_from_prev_block(&block_hash).unwrap(); + let shard_layout = + get_shard_layout_from_prev_block(epoch_manager.as_ref(), &block_hash).unwrap(); let flat_storage_manager = runtime.get_flat_storage_manager(); for shard_uid in shard_layout.shard_uids() {