diff --git a/.github/workflows/ci_nightly.yml b/.github/workflows/ci_nightly.yml index 64bde2d387c..61b6854f4df 100644 --- a/.github/workflows/ci_nightly.yml +++ b/.github/workflows/ci_nightly.yml @@ -1,4 +1,4 @@ -name: CI +name: CI Nightly concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -36,6 +36,20 @@ jobs: - uses: taiki-e/install-action@9b5b983efc779f85e5e5d11539f005e85ccb27ff with: tool: just,cargo-nextest - # Run the tests: - - run: just nextest-slow ${{ matrix.type }} + - uses: mathiasvr/command-output@34408ea3d0528273faff3d9e201761ae96106cd0 + with: + run: just nextest-slow ${{ matrix.type }} + id: run_nextest + + - name: Notify in Zulip about failures + uses: zulip/github-actions-zulip/send-message@08b6fbd07f5834e5b930a85bc7740e9fd44ab2e7 + if: always() && steps.run_nextest.conclusion == 'failure' + with: + api-key: ${{ secrets.ZULIP_API_KEY }} + email: "gha-bot@near.zulipchat.com" + organization-url: "https://near.zulipchat.com" + to: "nearone/private" + type: "stream" + topic: "GHA failures" + content: "Cargo Nextest ${{matrix.name}} [failed](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}): \n > ${{ steps.run_nextest.outputs.stderr }} " diff --git a/Cargo.lock b/Cargo.lock index e7b1bbbb6d2..538079b3602 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2113,6 +2113,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "dissimilar" version = "1.0.4" @@ -2582,9 +2593,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -3080,6 +3091,124 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "id-arena" version = "2.2.1" @@ -3094,12 +3223,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.3.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -3571,6 +3711,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "local-channel" version = "0.1.3" @@ -6005,9 +6151,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -7499,9 +7645,9 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "smallvec" -version = "1.10.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ "serde", ] @@ -7756,6 +7902,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "sysinfo" version = "0.24.5" @@ -7976,6 +8133,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -8390,27 +8557,12 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bidi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" - [[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" -[[package]] -name = "unicode-normalization" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-width" version = "0.1.9" @@ -8443,9 +8595,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -8458,6 +8610,18 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.1" @@ -9389,6 +9553,18 @@ dependencies = [ "wasmparser 0.218.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -9434,6 +9610,30 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.31" @@ -9454,6 +9654,27 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -9487,6 +9708,28 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "zstd" version = "0.13.1" diff --git a/chain/chain/src/flat_storage_resharder.rs b/chain/chain/src/flat_storage_resharder.rs index 862e5839f7d..272c81199d9 100644 --- a/chain/chain/src/flat_storage_resharder.rs +++ b/chain/chain/src/flat_storage_resharder.rs @@ -34,6 +34,7 @@ use near_store::adapter::flat_store::{FlatStoreAdapter, FlatStoreUpdateAdapter}; use near_store::adapter::StoreAdapter; use near_store::flat::{ BlockInfo, FlatStateChanges, FlatStorageError, FlatStorageReadyStatus, + FlatStorageReshardingShardCatchUpMetrics, FlatStorageReshardingShardSplitMetrics, FlatStorageReshardingStatus, FlatStorageStatus, ParentSplitParameters, }; use near_store::{ShardUId, StorageError}; @@ -261,6 +262,14 @@ impl FlatStorageResharder { TaskExecutionStatus::NotStarted, ); self.set_resharding_event(event); + + let metrics = FlatStorageReshardingShardSplitMetrics::new( + parent_shard, + split_params.left_child_shard, + split_params.right_child_shard, + ); + metrics.update_shards_status(&self.runtime.get_flat_storage_manager()); + info!(target: "resharding", ?parent_shard, ?split_params,"scheduling flat storage shard split"); let resharder = self.clone(); // Send a request to schedule the execution of `split_shard_task`, to do the bulk of the @@ -344,8 +353,17 @@ impl FlatStorageResharder { } // We know that the resharding block has become final so let's start the real work. - let task_status = self.split_shard_task_impl(); - self.split_shard_task_postprocessing(task_status); + let (parent_shard, split_params) = self + .get_parent_shard_and_split_params() + .expect("flat storage resharding event must be Split!"); + let metrics = FlatStorageReshardingShardSplitMetrics::new( + parent_shard, + split_params.left_child_shard, + split_params.right_child_shard, + ); + + let task_status = self.split_shard_task_impl(parent_shard, &split_params, &metrics); + self.split_shard_task_postprocessing(parent_shard, split_params, &metrics, task_status); info!(target: "resharding", ?task_status, "flat storage shard split task finished"); task_status } @@ -353,7 +371,12 @@ impl FlatStorageResharder { /// Performs the bulk of [split_shard_task]. /// /// Returns `true` if the routine completed successfully. - fn split_shard_task_impl(&self) -> FlatStorageReshardingSchedulableTaskResult { + fn split_shard_task_impl( + &self, + parent_shard: ShardUId, + split_params: &ParentSplitParameters, + metrics: &FlatStorageReshardingShardSplitMetrics, + ) -> FlatStorageReshardingSchedulableTaskResult { self.set_resharding_event_execution_status(TaskExecutionStatus::Started); // Exit early if the task has already been cancelled. @@ -364,12 +387,10 @@ impl FlatStorageResharder { // Determines after how many bytes worth of key-values the process stops to commit changes // and to check cancellation. let batch_size = self.resharding_config.get().batch_size.as_u64() as usize; + metrics.set_split_shard_batch_size(batch_size); // Delay between every batch. let batch_delay = self.resharding_config.get().batch_delay.unsigned_abs(); - let (parent_shard, split_params) = self - .get_parent_shard_and_split_params() - .expect("flat storage resharding event must be Split!"); info!(target: "resharding", ?parent_shard, ?split_params, ?batch_delay, ?batch_size, "flat storage shard split task: starting key-values copy"); // Prepare the store object for commits and the iterator over parent's flat storage. @@ -387,6 +408,7 @@ impl FlatStorageResharder { }; let mut num_batches_done: usize = 0; + metrics.set_split_shard_processed_bytes(0); let mut iter_exhausted = false; loop { @@ -432,6 +454,8 @@ impl FlatStorageResharder { } num_batches_done += 1; + metrics.set_split_shard_processed_batches(num_batches_done); + metrics.inc_split_shard_processed_bytes_by(processed_size); // If `iter`` is exhausted we can exit after the store commit. if iter_exhausted { @@ -457,11 +481,11 @@ impl FlatStorageResharder { )] fn split_shard_task_postprocessing( &self, + parent_shard: ShardUId, + split_params: ParentSplitParameters, + metrics: &FlatStorageReshardingShardSplitMetrics, task_status: FlatStorageReshardingSchedulableTaskResult, ) { - let (parent_shard, split_params) = self - .get_parent_shard_and_split_params() - .expect("flat storage resharding event must be Split!"); let ParentSplitParameters { left_child_shard, right_child_shard, @@ -524,6 +548,7 @@ impl FlatStorageResharder { } store_update.commit().unwrap(); self.remove_resharding_event(); + metrics.update_shards_status(&self.runtime.get_flat_storage_manager()); } /// Returns an iterator over a shard's flat storage at the given block hash. This @@ -591,14 +616,15 @@ impl FlatStorageResharder { chain_store: &ChainStore, ) -> FlatStorageReshardingTaskResult { info!(target: "resharding", ?shard_uid, ?flat_head_block_hash, "flat storage shard catchup task started"); + let metrics = FlatStorageReshardingShardCatchUpMetrics::new(&shard_uid); // Apply deltas and then create the flat storage. let apply_result = - self.shard_catchup_apply_deltas(shard_uid, flat_head_block_hash, chain_store); + self.shard_catchup_apply_deltas(shard_uid, flat_head_block_hash, chain_store, &metrics); let Ok((num_batches_done, flat_head)) = apply_result else { error!(target: "resharding", ?shard_uid, err = ?apply_result.unwrap_err(), "flat storage shard catchup delta application failed!"); return FlatStorageReshardingTaskResult::Failed; }; - match self.shard_catchup_finalize_storage(shard_uid, &flat_head) { + match self.shard_catchup_finalize_storage(shard_uid, &flat_head, &metrics) { Ok(_) => { let task_status = FlatStorageReshardingTaskResult::Successful { num_batches_done }; info!(target: "resharding", ?shard_uid, ?task_status, "flat storage shard catchup task finished"); @@ -621,6 +647,7 @@ impl FlatStorageResharder { shard_uid: ShardUId, mut flat_head_block_hash: CryptoHash, chain_store: &ChainStore, + metrics: &FlatStorageReshardingShardCatchUpMetrics, ) -> Result<(usize, Tip), Error> { // How many block heights of deltas are applied in a single commit. let catch_up_blocks = self.resharding_config.get().catch_up_blocks; @@ -684,7 +711,9 @@ impl FlatStorageResharder { )), ); store_update.commit()?; + num_batches_done += 1; + metrics.set_head_height(chain_store.get_block_height(&flat_head_block_hash)?); // Sleep between batches in order to throttle resharding and leave some resource for the // regular node operation. @@ -704,6 +733,7 @@ impl FlatStorageResharder { &self, shard_uid: ShardUId, flat_head: &Tip, + metrics: &FlatStorageReshardingShardCatchUpMetrics, ) -> Result<(), Error> { // GC deltas from forks which could have appeared on chain during catchup. let store = self.runtime.store().flat_store(); @@ -720,17 +750,16 @@ impl FlatStorageResharder { } } // Set the flat storage status to `Ready`. - store_update.set_flat_storage_status( - shard_uid, - FlatStorageStatus::Ready(FlatStorageReadyStatus { - flat_head: BlockInfo { - hash: flat_head.last_block_hash, - prev_hash: flat_head.prev_block_hash, - height: flat_head.height, - }, - }), - ); + let flat_storage_status = FlatStorageStatus::Ready(FlatStorageReadyStatus { + flat_head: BlockInfo { + hash: flat_head.last_block_hash, + prev_hash: flat_head.prev_block_hash, + height: flat_head.height, + }, + }); + store_update.set_flat_storage_status(shard_uid, flat_storage_status.clone()); store_update.commit()?; + metrics.set_status(&flat_storage_status); info!(target: "resharding", ?shard_uid, %deltas_gc_count, "garbage collected flat storage deltas"); // Create the flat storage entry for this shard in the manager. self.runtime.get_flat_storage_manager().create_flat_storage_for_shard(shard_uid)?; @@ -747,7 +776,7 @@ impl FlatStorageResharder { debug_assert!(!current_event.has_started()); // Clean up the database state. match current_event { - FlatStorageReshardingEventStatus::SplitShard(parent_shard, split_status, _) => { + FlatStorageReshardingEventStatus::SplitShard(parent_shard, split_status, ..) => { let flat_store = self.runtime.store().flat_store(); let mut store_update = flat_store.store_update(); // Parent go back to Ready state. @@ -1038,7 +1067,7 @@ impl FlatStorageReshardingEventStatus { fn resharding_hash(&self) -> CryptoHash { match self { - FlatStorageReshardingEventStatus::SplitShard(_, split_status, _) => { + FlatStorageReshardingEventStatus::SplitShard(_, split_status, ..) => { split_status.resharding_hash } } @@ -1400,7 +1429,13 @@ mod tests { // Immediately cancel the resharding and call the resharding task. controller.handle.stop(); - resharder.split_shard_task_impl(); + let (parent_shard, split_params) = resharder.get_parent_shard_and_split_params().unwrap(); + let metrics = FlatStorageReshardingShardSplitMetrics::new( + parent_shard, + split_params.left_child_shard, + split_params.right_child_shard, + ); + resharder.split_shard_task_impl(parent_shard, &split_params, &metrics); assert!(resharder.resharding_event().is_some()); assert!(resharder.start_resharding(resharding_event_type, &new_shard_layout).is_err()); diff --git a/chain/chain/src/runtime/mod.rs b/chain/chain/src/runtime/mod.rs index 83b121cd2c0..671acbdb3fa 100644 --- a/chain/chain/src/runtime/mod.rs +++ b/chain/chain/src/runtime/mod.rs @@ -1199,14 +1199,11 @@ impl RuntimeAdapter for NightshadeRuntime { genesis_config.protocol_upgrade_stake_threshold = epoch_config.protocol_upgrade_stake_threshold; genesis_config.shard_layout = epoch_config.shard_layout; - genesis_config.num_chunk_only_producer_seats = - epoch_config.validator_selection_config.num_chunk_only_producer_seats; - genesis_config.minimum_validators_per_shard = - epoch_config.validator_selection_config.minimum_validators_per_shard; - genesis_config.minimum_stake_ratio = - epoch_config.validator_selection_config.minimum_stake_ratio; + genesis_config.num_chunk_only_producer_seats = epoch_config.num_chunk_only_producer_seats; + genesis_config.minimum_validators_per_shard = epoch_config.minimum_validators_per_shard; + genesis_config.minimum_stake_ratio = epoch_config.minimum_stake_ratio; genesis_config.shuffle_shard_assignment_for_chunk_producers = - epoch_config.validator_selection_config.shuffle_shard_assignment_for_chunk_producers; + epoch_config.shuffle_shard_assignment_for_chunk_producers; let runtime_config = self.runtime_config_store.get_config(protocol_version).as_ref().clone(); diff --git a/chain/chain/src/test_utils/kv_runtime.rs b/chain/chain/src/test_utils/kv_runtime.rs index ac9d22db4a0..0563979619f 100644 --- a/chain/chain/src/test_utils/kv_runtime.rs +++ b/chain/chain/src/test_utils/kv_runtime.rs @@ -23,7 +23,6 @@ use near_primitives::epoch_block_info::BlockInfo; use near_primitives::epoch_info::EpochInfo; use near_primitives::epoch_manager::EpochConfig; use near_primitives::epoch_manager::ShardConfig; -use near_primitives::epoch_manager::ValidatorSelectionConfig; use near_primitives::errors::{EpochError, InvalidTxError}; use near_primitives::hash::{hash, CryptoHash}; use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum, ReceiptV0}; @@ -55,7 +54,6 @@ use near_store::{ TrieChanges, WrappedTrieChanges, }; use near_vm_runner::{ContractCode, ContractRuntimeCache, NoContractRuntimeCache}; -use num_rational::Ratio; use std::cmp::Ordering; use std::collections::{BTreeMap, HashMap, HashSet}; use std::sync::{Arc, RwLock}; @@ -496,24 +494,7 @@ impl EpochManagerAdapter for MockEpochManager { } fn get_epoch_config(&self, epoch_id: &EpochId) -> Result { - Ok(EpochConfig { - epoch_length: self.epoch_length, - num_block_producer_seats: 2, - num_block_producer_seats_per_shard: vec![1, 1], - avg_hidden_validator_seats_per_shard: vec![1, 1], - block_producer_kickout_threshold: 0, - chunk_producer_kickout_threshold: 0, - chunk_validator_only_kickout_threshold: 0, - target_validator_mandates_per_shard: 1, - validator_max_kickout_stake_perc: 0, - online_min_threshold: Ratio::new(1i32, 4i32), - online_max_threshold: Ratio::new(3i32, 4i32), - fishermen_threshold: 1, - minimum_stake_divisor: 1, - protocol_upgrade_stake_threshold: Ratio::new(3i32, 4i32), - shard_layout: self.get_shard_layout(epoch_id).unwrap(), - validator_selection_config: ValidatorSelectionConfig::default(), - }) + Ok(EpochConfig::mock(self.epoch_length, self.get_shard_layout(epoch_id).unwrap())) } /// Return the epoch info containing the mocked data. diff --git a/chain/chain/src/tests/simple_chain.rs b/chain/chain/src/tests/simple_chain.rs index 19a0fd3f7e1..cf07fa5e0df 100644 --- a/chain/chain/src/tests/simple_chain.rs +++ b/chain/chain/src/tests/simple_chain.rs @@ -33,7 +33,7 @@ fn build_chain() { // cargo insta test --accept -p near-chain --features nightly -- tests::simple_chain::build_chain let hash = chain.head().unwrap().last_block_hash; if cfg!(feature = "nightly") { - insta::assert_snapshot!(hash, @"Ch6xEoeJ1MNWLR2em48f6mKFko7niVoMyUazeUzFY8b4"); + insta::assert_snapshot!(hash, @"GARF4HBtQJ41quFA9fvjHpbVYT4o15syhL3FkH1o7poT"); } else { insta::assert_snapshot!(hash, @"5LkmueLrB2cc3vURr6VKvT9acRTuNzWGTvzLGFJkRD9c"); } @@ -51,7 +51,7 @@ fn build_chain() { let hash = chain.head().unwrap().last_block_hash; if cfg!(feature = "nightly") { - insta::assert_snapshot!(hash, @"A1uyFaVoJ2spKmGfjDRgnFXRx4aP21yQxArA7muN23bb"); + insta::assert_snapshot!(hash, @"HiXuBfW5Xd6e8ZTbMhwtPEXeZxe7macc8DvaWryNdvcf"); } else { insta::assert_snapshot!(hash, @"5txsrLCmQp9kn3jYRp1VHrCDt7oBTnhyi71rEPZmm8Ce"); } diff --git a/chain/epoch-manager/src/shard_tracker.rs b/chain/epoch-manager/src/shard_tracker.rs index 0a03cb63a77..55c9331f5dd 100644 --- a/chain/epoch-manager/src/shard_tracker.rs +++ b/chain/epoch-manager/src/shard_tracker.rs @@ -252,7 +252,13 @@ mod tests { minimum_stake_divisor: 1, protocol_upgrade_stake_threshold: Ratio::new(80, 100), shard_layout: ShardLayout::multi_shard(num_shards, 0), - validator_selection_config: Default::default(), + num_chunk_producer_seats: 100, + num_chunk_validator_seats: 300, + num_chunk_only_producer_seats: 300, + minimum_validators_per_shard: 1, + minimum_stake_ratio: Ratio::new(160i32, 1_000_000i32), + chunk_producer_assignment_changes_limit: 5, + shuffle_shard_assignment_for_chunk_producers: false, validator_max_kickout_stake_perc: 100, }; let reward_calculator = RewardCalculator { diff --git a/chain/epoch-manager/src/test_utils.rs b/chain/epoch-manager/src/test_utils.rs index 286253ec8d7..002a666beb6 100644 --- a/chain/epoch-manager/src/test_utils.rs +++ b/chain/epoch-manager/src/test_utils.rs @@ -13,7 +13,7 @@ use near_crypto::{KeyType, SecretKey}; use near_primitives::challenge::SlashedValidator; use near_primitives::epoch_block_info::BlockInfoV2; use near_primitives::epoch_info::EpochInfo; -use near_primitives::epoch_manager::{AllEpochConfig, EpochConfig, ValidatorSelectionConfig}; +use near_primitives::epoch_manager::{AllEpochConfig, EpochConfig}; use near_primitives::hash::{hash, CryptoHash}; use near_primitives::types::validator_stake::ValidatorStake; use near_primitives::types::{ @@ -151,10 +151,13 @@ pub fn epoch_config_with_production_config( online_max_threshold: Ratio::new(99, 100), protocol_upgrade_stake_threshold: Ratio::new(80, 100), minimum_stake_divisor: 1, - validator_selection_config: ValidatorSelectionConfig { - num_chunk_producer_seats, - ..Default::default() - }, + num_chunk_producer_seats, + num_chunk_validator_seats: 300, + num_chunk_only_producer_seats: 300, + minimum_validators_per_shard: 1, + minimum_stake_ratio: Ratio::new(160i32, 1_000_000i32), + chunk_producer_assignment_changes_limit: 5, + shuffle_shard_assignment_for_chunk_producers: false, shard_layout: ShardLayout::multi_shard(num_shards, 0), validator_max_kickout_stake_perc: 100, }; diff --git a/chain/epoch-manager/src/tests/mod.rs b/chain/epoch-manager/src/tests/mod.rs index e7187dbf2d2..6c11fe7e0ea 100644 --- a/chain/epoch-manager/src/tests/mod.rs +++ b/chain/epoch-manager/src/tests/mod.rs @@ -2336,7 +2336,13 @@ fn test_protocol_version_switch_with_many_seats() { protocol_upgrade_stake_threshold: Ratio::new(80, 100), minimum_stake_divisor: 1, shard_layout: ShardLayout::single_shard(), - validator_selection_config: Default::default(), + num_chunk_producer_seats: 100, + num_chunk_validator_seats: 300, + num_chunk_only_producer_seats: 300, + minimum_validators_per_shard: 1, + minimum_stake_ratio: Ratio::new(160i32, 1_000_000i32), + chunk_producer_assignment_changes_limit: 5, + shuffle_shard_assignment_for_chunk_producers: false, validator_max_kickout_stake_perc: 100, }; let config = AllEpochConfig::new(false, PROTOCOL_VERSION, epoch_config, "test-chain"); diff --git a/chain/epoch-manager/src/validator_selection.rs b/chain/epoch-manager/src/validator_selection.rs index 10c727a98c3..2b7bcc4afb1 100644 --- a/chain/epoch-manager/src/validator_selection.rs +++ b/chain/epoch-manager/src/validator_selection.rs @@ -50,14 +50,14 @@ fn select_validators_from_proposals( ) -> ValidatorRoles { let shard_ids: Vec<_> = epoch_config.shard_layout.shard_ids().collect(); let min_stake_ratio = { - let rational = epoch_config.validator_selection_config.minimum_stake_ratio; + let rational = epoch_config.minimum_stake_ratio; Ratio::new(*rational.numer() as u128, *rational.denom() as u128) }; let chunk_producer_proposals = order_proposals(proposals.values().cloned()); let (chunk_producers, _, cp_stake_threshold) = select_chunk_producers( chunk_producer_proposals, - epoch_config.validator_selection_config.num_chunk_producer_seats as usize, + epoch_config.num_chunk_producer_seats as usize, min_stake_ratio, shard_ids.len() as NumShards, protocol_version, @@ -74,7 +74,7 @@ fn select_validators_from_proposals( let chunk_validator_proposals = order_proposals(proposals.values().cloned()); let (chunk_validators, _, cv_stake_threshold) = select_validators( chunk_validator_proposals, - epoch_config.validator_selection_config.num_chunk_validator_seats as usize, + epoch_config.num_chunk_validator_seats as usize, min_stake_ratio, protocol_version, ); @@ -135,8 +135,7 @@ fn get_chunk_producers_assignment( // Assign chunk producers to shards. let num_chunk_producers = chunk_producers.len(); - let minimum_validators_per_shard = - epoch_config.validator_selection_config.minimum_validators_per_shard as usize; + let minimum_validators_per_shard = epoch_config.minimum_validators_per_shard as usize; let prev_chunk_producers_assignment = if use_stable_shard_assignment { let mut assignment = vec![]; for validator_ids in prev_epoch_info.chunk_producers_settlement().iter() { @@ -156,7 +155,7 @@ fn get_chunk_producers_assignment( chunk_producers.clone(), shard_ids.len() as NumShards, minimum_validators_per_shard, - epoch_config.validator_selection_config.chunk_producer_assignment_changes_limit as usize, + epoch_config.chunk_producer_assignment_changes_limit as usize, rng_seed, prev_chunk_producers_assignment, ) @@ -262,7 +261,7 @@ pub fn proposals_to_epoch_info( )? }; - if epoch_config.validator_selection_config.shuffle_shard_assignment_for_chunk_producers { + if epoch_config.shuffle_shard_assignment_for_chunk_producers { chunk_producers_settlement.shuffle(&mut EpochInfo::shard_assignment_rng(&rng_seed)); } @@ -462,7 +461,7 @@ mod old_validator_selection { ) -> ValidatorRoles { let max_bp_selected = epoch_config.num_block_producer_seats as usize; let min_stake_ratio = { - let rational = epoch_config.validator_selection_config.minimum_stake_ratio; + let rational = epoch_config.minimum_stake_ratio; Ratio::new(*rational.numer() as u128, *rational.denom() as u128) }; @@ -476,9 +475,8 @@ mod old_validator_selection { let (chunk_producer_proposals, chunk_producers, cp_stake_threshold) = if checked_feature!("stable", ChunkOnlyProducers, protocol_version) { let chunk_producer_proposals = order_proposals(proposals.into_values()); - let max_cp_selected = max_bp_selected - + (epoch_config.validator_selection_config.num_chunk_only_producer_seats - as usize); + let max_cp_selected = + max_bp_selected + (epoch_config.num_chunk_only_producer_seats as usize); let num_shards = epoch_config.shard_layout.shard_ids().count() as NumShards; let (chunk_producers, not_chunk_producers, cp_stake_threshold) = select_chunk_producers( @@ -523,8 +521,7 @@ mod old_validator_selection { } let shard_ids: Vec<_> = epoch_config.shard_layout.shard_ids().collect(); - let minimum_validators_per_shard = - epoch_config.validator_selection_config.minimum_validators_per_shard as usize; + let minimum_validators_per_shard = epoch_config.minimum_validators_per_shard as usize; let shard_assignment = shard_assignment::old_validator_selection::assign_shards( chunk_producers, shard_ids.len() as NumShards, @@ -627,9 +624,9 @@ mod tests { use near_crypto::{KeyType, PublicKey}; use near_primitives::account::id::AccountIdRef; use near_primitives::epoch_info::{EpochInfo, EpochInfoV3}; - use near_primitives::epoch_manager::ValidatorSelectionConfig; use near_primitives::shard_layout::ShardLayout; use near_primitives::types::validator_stake::ValidatorStake; + use near_primitives::types::NumSeats; use near_primitives::version::PROTOCOL_VERSION; use num_rational::Ratio; @@ -638,7 +635,7 @@ mod tests { // A simple sanity test. Given fewer proposals than the number of seats, // none of which has too little stake, they all get assigned as block and // chunk producers. - let epoch_config = create_epoch_config(2, 100, Default::default()); + let epoch_config = create_epoch_config(2, 100, None, None, None); let prev_epoch_height = 7; let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &["test1", "test2"], &[]); let proposals = create_proposals(&[("test1", 1000), ("test2", 2000), ("test3", 300)]); @@ -687,12 +684,9 @@ mod tests { let epoch_config = create_epoch_config( 2, num_bp_seats, - ValidatorSelectionConfig { - num_chunk_producer_seats: num_bp_seats + num_cp_seats, - num_chunk_validator_seats: num_bp_seats + num_cp_seats, - num_chunk_only_producer_seats: num_cp_seats, - ..Default::default() - }, + Some(num_bp_seats + num_cp_seats), + Some(num_bp_seats + num_cp_seats), + None, ); let prev_epoch_height = 3; let test1_stake = 1000; @@ -783,12 +777,9 @@ mod tests { let mut epoch_config = create_epoch_config( 6, num_bp_seats, - ValidatorSelectionConfig { - num_chunk_producer_seats: num_bp_seats + num_cp_seats, - num_chunk_validator_seats: num_bp_seats + num_cp_seats, - num_chunk_only_producer_seats: num_cp_seats, - ..Default::default() - }, + Some(num_bp_seats + num_cp_seats), + Some(num_bp_seats + num_cp_seats), + None, ); let prev_epoch_height = 3; let prev_epoch_info = create_prev_epoch_info::<&str>(prev_epoch_height, &[], &[]); @@ -828,7 +819,7 @@ mod tests { ) .unwrap(); - epoch_config.validator_selection_config.shuffle_shard_assignment_for_chunk_producers = true; + epoch_config.shuffle_shard_assignment_for_chunk_producers = true; let epoch_info_with_shuffling = proposals_to_epoch_info( &epoch_config, [1; 32], @@ -888,16 +879,7 @@ mod tests { #[test] fn test_block_producer_sampling() { let num_shards = 4; - let epoch_config = create_epoch_config( - num_shards, - 2, - ValidatorSelectionConfig { - num_chunk_producer_seats: 2, - num_chunk_validator_seats: 2, - num_chunk_only_producer_seats: 0, - ..Default::default() - }, - ); + let epoch_config = create_epoch_config(num_shards, 2, Some(2), Some(2), None); let prev_epoch_height = 7; let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &["test1", "test2"], &[]); let proposals = create_proposals(&[("test1", 1000), ("test2", 2000)]); @@ -932,12 +914,9 @@ mod tests { let epoch_config = create_epoch_config( num_shards, 2 * num_shards, - ValidatorSelectionConfig { - num_chunk_producer_seats: 2 * num_shards, - num_chunk_validator_seats: 2 * num_shards, - num_chunk_only_producer_seats: 0, - ..Default::default() - }, + Some(2 * num_shards), + Some(2 * num_shards), + None, ); let prev_epoch_height = 7; let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &["test1", "test2"], &[]); @@ -1007,14 +986,9 @@ mod tests { let epoch_config = create_epoch_config( num_shards, 2 * num_shards, - ValidatorSelectionConfig { - num_chunk_producer_seats: 2 * num_shards, - num_chunk_validator_seats: 2 * num_shards, - num_chunk_only_producer_seats: 0, - // for example purposes, we choose a higher ratio than in production - minimum_stake_ratio: Ratio::new(1, 10), - ..Default::default() - }, + Some(2 * num_shards), + Some(2 * num_shards), + Some(Ratio::new(1, 10)), ); let prev_epoch_height = 7; let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &["test1", "test2"], &[]); @@ -1091,18 +1065,8 @@ mod tests { // (the reason we can't choose them is because the probability of them actually // being selected to make a block would be too low since it is done in // proportion to stake). - let epoch_config = create_epoch_config( - 1, - 100, - ValidatorSelectionConfig { - num_chunk_producer_seats: 300, - num_chunk_validator_seats: 300, - num_chunk_only_producer_seats: 300, - // for example purposes, we choose a higher ratio than in production - minimum_stake_ratio: Ratio::new(1, 10), - ..Default::default() - }, - ); + let epoch_config = + create_epoch_config(1, 100, Some(300), Some(300), Some(Ratio::new(1i32, 10i32))); let prev_epoch_height = 7; // test5 and test6 are going to get kicked out for not enough stake. let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &["test5", "test6"], &[]); @@ -1196,7 +1160,7 @@ mod tests { #[test] fn test_validator_assignment_with_kickout() { // kicked out validators are not selected - let epoch_config = create_epoch_config(1, 100, Default::default()); + let epoch_config = create_epoch_config(1, 100, None, None, None); let prev_epoch_height = 7; let prev_epoch_info = create_prev_epoch_info( prev_epoch_height, @@ -1228,7 +1192,7 @@ mod tests { // validator balances are updated based on their rewards let validators = [("test1", 3000), ("test2", 2000), ("test3", 1000)]; let rewards: [u128; 3] = [7, 8, 9]; - let epoch_config = create_epoch_config(1, 100, Default::default()); + let epoch_config = create_epoch_config(1, 100, None, None, None); let prev_epoch_height = 7; let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &validators, &[]); let rewards_map = validators @@ -1260,26 +1224,31 @@ mod tests { fn create_epoch_config( num_shards: u64, num_block_producer_seats: u64, - validator_selection_config: ValidatorSelectionConfig, + num_chunk_producer_seats: Option, + num_chunk_validator_seats: Option, + minimum_stake_ratio: Option>, ) -> EpochConfig { - EpochConfig { - epoch_length: 10, - num_block_producer_seats, - num_block_producer_seats_per_shard: vec![num_block_producer_seats; num_shards as usize], - avg_hidden_validator_seats_per_shard: vec![0; num_shards as usize], - block_producer_kickout_threshold: 0, - chunk_producer_kickout_threshold: 0, - chunk_validator_only_kickout_threshold: 0, - target_validator_mandates_per_shard: 68, - validator_max_kickout_stake_perc: 100, - online_min_threshold: 0.into(), - online_max_threshold: 0.into(), - fishermen_threshold: 0, - minimum_stake_divisor: 0, - protocol_upgrade_stake_threshold: 0.into(), - shard_layout: ShardLayout::multi_shard(num_shards, 0), - validator_selection_config, + let mut epoch_config = EpochConfig::minimal(); + epoch_config.epoch_length = 10; + epoch_config.num_block_producer_seats = num_block_producer_seats; + epoch_config.num_block_producer_seats_per_shard = + vec![num_block_producer_seats; num_shards as usize]; + epoch_config.avg_hidden_validator_seats_per_shard = vec![0; num_shards as usize]; + epoch_config.target_validator_mandates_per_shard = 68; + epoch_config.validator_max_kickout_stake_perc = 100; + epoch_config.shard_layout = ShardLayout::multi_shard(num_shards, 0); + + if let Some(num_chunk_producer_seats) = num_chunk_producer_seats { + epoch_config.num_chunk_producer_seats = num_chunk_producer_seats; + } + if let Some(num_chunk_validator_seats) = num_chunk_validator_seats { + epoch_config.num_chunk_validator_seats = num_chunk_validator_seats; } + if let Some(minimum_stake_ratio) = minimum_stake_ratio { + epoch_config.minimum_stake_ratio = minimum_stake_ratio; + } + + epoch_config } fn create_prev_epoch_info( diff --git a/chain/indexer/src/lib.rs b/chain/indexer/src/lib.rs index 1f1aac4e6f9..72571ef03c8 100644 --- a/chain/indexer/src/lib.rs +++ b/chain/indexer/src/lib.rs @@ -172,6 +172,5 @@ pub fn indexer_init_configs( params.download_config_url.as_deref(), params.boot_nodes.as_deref(), params.max_gas_burnt_view, - None, ) } diff --git a/core/chain-configs/src/genesis_config.rs b/core/chain-configs/src/genesis_config.rs index 973346bd069..6d435dc58ee 100644 --- a/core/chain-configs/src/genesis_config.rs +++ b/core/chain-configs/src/genesis_config.rs @@ -12,7 +12,7 @@ use anyhow::Context; use chrono::{DateTime, Utc}; use near_config_utils::ValidationError; use near_parameters::{RuntimeConfig, RuntimeConfigView}; -use near_primitives::epoch_manager::{EpochConfig, ValidatorSelectionConfig}; +use near_primitives::epoch_manager::EpochConfig; use near_primitives::shard_layout::ShardLayout; use near_primitives::types::validator_stake::ValidatorStake; use near_primitives::types::StateRoot; @@ -269,17 +269,14 @@ impl From<&GenesisConfig> for EpochConfig { protocol_upgrade_stake_threshold: config.protocol_upgrade_stake_threshold, minimum_stake_divisor: config.minimum_stake_divisor, shard_layout: config.shard_layout.clone(), - validator_selection_config: near_primitives::epoch_manager::ValidatorSelectionConfig { - num_chunk_producer_seats: config.num_chunk_producer_seats, - num_chunk_validator_seats: config.num_chunk_validator_seats, - num_chunk_only_producer_seats: config.num_chunk_only_producer_seats, - minimum_validators_per_shard: config.minimum_validators_per_shard, - minimum_stake_ratio: config.minimum_stake_ratio, - chunk_producer_assignment_changes_limit: config - .chunk_producer_assignment_changes_limit, - shuffle_shard_assignment_for_chunk_producers: config - .shuffle_shard_assignment_for_chunk_producers, - }, + num_chunk_producer_seats: config.num_chunk_producer_seats, + num_chunk_validator_seats: config.num_chunk_validator_seats, + num_chunk_only_producer_seats: config.num_chunk_only_producer_seats, + minimum_validators_per_shard: config.minimum_validators_per_shard, + minimum_stake_ratio: config.minimum_stake_ratio, + chunk_producer_assignment_changes_limit: config.chunk_producer_assignment_changes_limit, + shuffle_shard_assignment_for_chunk_producers: config + .shuffle_shard_assignment_for_chunk_producers, validator_max_kickout_stake_perc: config.max_kickout_stake_perc, } } @@ -756,32 +753,22 @@ impl Genesis { // Create test-only epoch config. // Not depends on genesis! // TODO(#11265): move to crate with `EpochConfig`. + // Cannot move the configs consts to `primitives`. pub fn test_epoch_config( num_block_producer_seats: NumSeats, shard_layout: ShardLayout, epoch_length: BlockHeightDelta, ) -> EpochConfig { - EpochConfig { - epoch_length, + EpochConfig::genesis_test( num_block_producer_seats, - num_block_producer_seats_per_shard: vec![ - num_block_producer_seats; - shard_layout.shard_ids().count() - ], - avg_hidden_validator_seats_per_shard: vec![], - target_validator_mandates_per_shard: 68, - validator_max_kickout_stake_perc: 100, - online_min_threshold: Rational32::new(90, 100), - online_max_threshold: Rational32::new(99, 100), - minimum_stake_divisor: 10, - protocol_upgrade_stake_threshold: PROTOCOL_UPGRADE_STAKE_THRESHOLD, - block_producer_kickout_threshold: BLOCK_PRODUCER_KICKOUT_THRESHOLD, - chunk_producer_kickout_threshold: CHUNK_PRODUCER_KICKOUT_THRESHOLD, - chunk_validator_only_kickout_threshold: CHUNK_VALIDATOR_ONLY_KICKOUT_THRESHOLD, - fishermen_threshold: FISHERMEN_THRESHOLD, shard_layout, - validator_selection_config: ValidatorSelectionConfig::default(), - } + epoch_length, + BLOCK_PRODUCER_KICKOUT_THRESHOLD, + CHUNK_PRODUCER_KICKOUT_THRESHOLD, + CHUNK_VALIDATOR_ONLY_KICKOUT_THRESHOLD, + PROTOCOL_UPGRADE_STAKE_THRESHOLD, + FISHERMEN_THRESHOLD, + ) } } diff --git a/core/chain-configs/src/test_genesis.rs b/core/chain-configs/src/test_genesis.rs index 085c1653e8f..fccec7ffad4 100644 --- a/core/chain-configs/src/test_genesis.rs +++ b/core/chain-configs/src/test_genesis.rs @@ -195,9 +195,8 @@ impl TestEpochConfigBuilder { epoch_config.block_producer_kickout_threshold = 0; epoch_config.chunk_producer_kickout_threshold = 0; epoch_config.chunk_validator_only_kickout_threshold = 0; - epoch_config.validator_selection_config.num_chunk_producer_seats = num_chunk_producer_seats; - epoch_config.validator_selection_config.num_chunk_validator_seats = - num_chunk_validator_seats; + epoch_config.num_chunk_producer_seats = num_chunk_producer_seats; + epoch_config.num_chunk_validator_seats = num_chunk_validator_seats; if let Some(target_validator_mandates_per_shard) = self.target_validator_mandates_per_shard { @@ -236,31 +235,30 @@ impl TestEpochConfigBuilder { ); } if let Some(minimum_stake_ratio) = self.minimum_stake_ratio { - epoch_config.validator_selection_config.minimum_stake_ratio = minimum_stake_ratio; + epoch_config.minimum_stake_ratio = minimum_stake_ratio; } else { tracing::warn!( "Epoch config minimum_stake_ratio not explicitly set, defaulting to {:?}.", - epoch_config.validator_selection_config.minimum_stake_ratio + epoch_config.minimum_stake_ratio ); } if let Some(minimum_validators_per_shard) = self.minimum_validators_per_shard { - epoch_config.validator_selection_config.minimum_validators_per_shard = - minimum_validators_per_shard; + epoch_config.minimum_validators_per_shard = minimum_validators_per_shard; } else { tracing::warn!( "Epoch config minimum_validators_per_shard not explicitly set, defaulting to {:?}.", - epoch_config.validator_selection_config.minimum_validators_per_shard + epoch_config.minimum_validators_per_shard ); } if let Some(shuffle_shard_assignment_for_chunk_producers) = self.shuffle_shard_assignment_for_chunk_producers { - epoch_config.validator_selection_config.shuffle_shard_assignment_for_chunk_producers = + epoch_config.shuffle_shard_assignment_for_chunk_producers = shuffle_shard_assignment_for_chunk_producers; } else { tracing::warn!( "Epoch config shuffle_shard_assignment_for_chunk_producers not explicitly set, defaulting to {:?}.", - epoch_config.validator_selection_config.shuffle_shard_assignment_for_chunk_producers + epoch_config.shuffle_shard_assignment_for_chunk_producers ); } diff --git a/core/chain-configs/src/test_utils.rs b/core/chain-configs/src/test_utils.rs index a7f39d19872..dd18ef7bb56 100644 --- a/core/chain-configs/src/test_utils.rs +++ b/core/chain-configs/src/test_utils.rs @@ -72,7 +72,7 @@ impl Genesis { } add_protocol_account(&mut records); let epoch_config = - Self::test_epoch_config(num_validator_seats, shard_layout, FAST_EPOCH_LENGTH); + Genesis::test_epoch_config(num_validator_seats, shard_layout, FAST_EPOCH_LENGTH); let config = GenesisConfig { protocol_version: PROTOCOL_VERSION, genesis_time: from_timestamp(clock.now_utc().unix_timestamp_nanos() as u64), @@ -106,24 +106,14 @@ impl Genesis { online_min_threshold: epoch_config.online_min_threshold, online_max_threshold: epoch_config.online_max_threshold, minimum_stake_divisor: epoch_config.minimum_stake_divisor, - num_chunk_producer_seats: epoch_config - .validator_selection_config - .num_chunk_producer_seats, - num_chunk_validator_seats: epoch_config - .validator_selection_config - .num_chunk_validator_seats, - num_chunk_only_producer_seats: epoch_config - .validator_selection_config - .num_chunk_only_producer_seats, - minimum_validators_per_shard: epoch_config - .validator_selection_config - .minimum_validators_per_shard, - minimum_stake_ratio: epoch_config.validator_selection_config.minimum_stake_ratio, + num_chunk_producer_seats: epoch_config.num_chunk_producer_seats, + num_chunk_validator_seats: epoch_config.num_chunk_validator_seats, + num_chunk_only_producer_seats: epoch_config.num_chunk_only_producer_seats, + minimum_validators_per_shard: epoch_config.minimum_validators_per_shard, + minimum_stake_ratio: epoch_config.minimum_stake_ratio, chunk_producer_assignment_changes_limit: epoch_config - .validator_selection_config .chunk_producer_assignment_changes_limit, shuffle_shard_assignment_for_chunk_producers: epoch_config - .validator_selection_config .shuffle_shard_assignment_for_chunk_producers, ..Default::default() diff --git a/core/parameters/src/config_store.rs b/core/parameters/src/config_store.rs index 2d2691459f0..895e3b59e00 100644 --- a/core/parameters/src/config_store.rs +++ b/core/parameters/src/config_store.rs @@ -94,7 +94,7 @@ impl RuntimeConfigStore { } for (protocol_version, diff_bytes) in CONFIG_DIFFS { - let diff :ParameterTableDiff= diff_bytes.parse().unwrap_or_else(|err| panic!("Failed parsing runtime parameters diff for version {protocol_version}. Error: {err}")); + let diff :ParameterTableDiff = diff_bytes.parse().unwrap_or_else(|err| panic!("Failed parsing runtime parameters diff for version {protocol_version}. Error: {err}")); params.apply_diff(diff).unwrap_or_else(|err| panic!("Failed applying diff to `RuntimeConfig` for version {protocol_version}. Error: {err}")); #[cfg(not(feature = "calimero_zero_storage"))] store.insert( diff --git a/core/primitives-core/src/version.rs b/core/primitives-core/src/version.rs index d4cae70983e..cb8e9b4e6fa 100644 --- a/core/primitives-core/src/version.rs +++ b/core/primitives-core/src/version.rs @@ -191,6 +191,8 @@ pub enum ProtocolFeature { /// Exclude existing contract code in deploy-contract and delete-account actions from the chunk state witness. /// Instead of sending code in the witness, the code checks the code-size using the internal trie nodes. ExcludeExistingCodeFromWitnessForCodeLen, + /// Use the block height instead of the block hash to calculate the receipt ID. + BlockHeightForReceiptId, } impl ProtocolFeature { @@ -272,6 +274,7 @@ impl ProtocolFeature { ProtocolFeature::RelaxedChunkValidation => 146, ProtocolFeature::ExcludeExistingCodeFromWitnessForCodeLen => 147, ProtocolFeature::BandwidthScheduler => 148, + ProtocolFeature::BlockHeightForReceiptId => 149, // Place features that are not yet in Nightly below this line. } } @@ -285,7 +288,7 @@ impl ProtocolFeature { const STABLE_PROTOCOL_VERSION: ProtocolVersion = 74; // On nightly, pick big enough version to support all features. -const NIGHTLY_PROTOCOL_VERSION: ProtocolVersion = 148; +const NIGHTLY_PROTOCOL_VERSION: ProtocolVersion = 149; /// Largest protocol version supported by the current binary. pub const PROTOCOL_VERSION: ProtocolVersion = if cfg!(feature = "nightly_protocol") { diff --git a/core/primitives/res/epoch_configs/mainnet/100.json b/core/primitives/res/epoch_configs/mainnet/100.json index 8ed94841f00..f006c15274f 100644 --- a/core/primitives/res/epoch_configs/mainnet/100.json +++ b/core/primitives/res/epoch_configs/mainnet/100.json @@ -81,16 +81,14 @@ "version": 4 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 62500 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 62500 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/mainnet/101.json b/core/primitives/res/epoch_configs/mainnet/101.json index 2b2d278432a..4d750599381 100644 --- a/core/primitives/res/epoch_configs/mainnet/101.json +++ b/core/primitives/res/epoch_configs/mainnet/101.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 62500 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 62500 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/mainnet/143.json b/core/primitives/res/epoch_configs/mainnet/143.json index 52b755b6e8f..092c919aee3 100644 --- a/core/primitives/res/epoch_configs/mainnet/143.json +++ b/core/primitives/res/epoch_configs/mainnet/143.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 62500 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": true - } + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 62500 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": true } \ No newline at end of file diff --git a/core/primitives/res/epoch_configs/mainnet/29.json b/core/primitives/res/epoch_configs/mainnet/29.json index 7c6c855f3ca..27eb0bdd345 100644 --- a/core/primitives/res/epoch_configs/mainnet/29.json +++ b/core/primitives/res/epoch_configs/mainnet/29.json @@ -1,47 +1,45 @@ { - "epoch_length": 43200, - "num_block_producer_seats": 100, - "num_block_producer_seats_per_shard": [ - 100 - ], - "avg_hidden_validator_seats_per_shard": [ - 0 - ], - "block_producer_kickout_threshold": 90, - "chunk_producer_kickout_threshold": 90, - "chunk_validator_only_kickout_threshold": 80, - "target_validator_mandates_per_shard": 68, - "validator_max_kickout_stake_perc": 100, - "online_min_threshold": [ - 90, - 100 - ], - "online_max_threshold": [ - 99, - 100 - ], - "fishermen_threshold": "340282366920938463463374607431768211455", - "minimum_stake_divisor": 10, - "protocol_upgrade_stake_threshold": [ - 4, - 5 - ], - "shard_layout": { - "V0": { - "num_shards": 1, - "version": 0 - } - }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 300, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false + "epoch_length": 43200, + "num_block_producer_seats": 100, + "num_block_producer_seats_per_shard": [ + 100 + ], + "avg_hidden_validator_seats_per_shard": [ + 0 + ], + "block_producer_kickout_threshold": 90, + "chunk_producer_kickout_threshold": 90, + "chunk_validator_only_kickout_threshold": 80, + "target_validator_mandates_per_shard": 68, + "validator_max_kickout_stake_perc": 100, + "online_min_threshold": [ + 90, + 100 + ], + "online_max_threshold": [ + 99, + 100 + ], + "fishermen_threshold": "340282366920938463463374607431768211455", + "minimum_stake_divisor": 10, + "protocol_upgrade_stake_threshold": [ + 4, + 5 + ], + "shard_layout": { + "V0": { + "num_shards": 1, + "version": 0 } - } \ No newline at end of file + }, + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 300, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/mainnet/48.json b/core/primitives/res/epoch_configs/mainnet/48.json index 07a59db30e2..86cd86776d0 100644 --- a/core/primitives/res/epoch_configs/mainnet/48.json +++ b/core/primitives/res/epoch_configs/mainnet/48.json @@ -56,16 +56,14 @@ "version": 1 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 300, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 300, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/mainnet/56.json b/core/primitives/res/epoch_configs/mainnet/56.json index 3658e2a568a..82502387abd 100644 --- a/core/primitives/res/epoch_configs/mainnet/56.json +++ b/core/primitives/res/epoch_configs/mainnet/56.json @@ -56,16 +56,14 @@ "version": 1 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 200, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 200, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/mainnet/64.json b/core/primitives/res/epoch_configs/mainnet/64.json index f60a976a8e4..9fc38ffdc0c 100644 --- a/core/primitives/res/epoch_configs/mainnet/64.json +++ b/core/primitives/res/epoch_configs/mainnet/64.json @@ -67,16 +67,14 @@ "version": 2 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 200, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 200, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/mainnet/65.json b/core/primitives/res/epoch_configs/mainnet/65.json index 31a72ef3c10..bdbef6ac15a 100644 --- a/core/primitives/res/epoch_configs/mainnet/65.json +++ b/core/primitives/res/epoch_configs/mainnet/65.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 200, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 200, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/mainnet/69.json b/core/primitives/res/epoch_configs/mainnet/69.json index 7169e6f91c6..dd0ead91fb0 100644 --- a/core/primitives/res/epoch_configs/mainnet/69.json +++ b/core/primitives/res/epoch_configs/mainnet/69.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/mainnet/70.json b/core/primitives/res/epoch_configs/mainnet/70.json index 5b479207724..dd0ead91fb0 100644 --- a/core/primitives/res/epoch_configs/mainnet/70.json +++ b/core/primitives/res/epoch_configs/mainnet/70.json @@ -1,89 +1,87 @@ { - "epoch_length": 43200, - "num_block_producer_seats": 100, - "num_block_producer_seats_per_shard": [ - 100, - 100, - 100, - 100, - 100, - 100 - ], - "avg_hidden_validator_seats_per_shard": [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - "block_producer_kickout_threshold": 80, - "chunk_producer_kickout_threshold": 80, - "chunk_validator_only_kickout_threshold": 80, - "target_validator_mandates_per_shard": 68, - "validator_max_kickout_stake_perc": 30, - "online_min_threshold": [ - 90, - 100 - ], - "online_max_threshold": [ - 99, - 100 - ], - "fishermen_threshold": "340282366920938463463374607431768211455", - "minimum_stake_divisor": 10, - "protocol_upgrade_stake_threshold": [ - 4, - 5 - ], - "shard_layout": { - "V1": { - "boundary_accounts": [ - "aurora", - "aurora-0", - "game.hot.tg", - "kkuuue2akv_1630967379.near", - "tge-lockup.sweat" + "epoch_length": 43200, + "num_block_producer_seats": 100, + "num_block_producer_seats_per_shard": [ + 100, + 100, + 100, + 100, + 100, + 100 + ], + "avg_hidden_validator_seats_per_shard": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "block_producer_kickout_threshold": 80, + "chunk_producer_kickout_threshold": 80, + "chunk_validator_only_kickout_threshold": 80, + "target_validator_mandates_per_shard": 68, + "validator_max_kickout_stake_perc": 30, + "online_min_threshold": [ + 90, + 100 + ], + "online_max_threshold": [ + 99, + 100 + ], + "fishermen_threshold": "340282366920938463463374607431768211455", + "minimum_stake_divisor": 10, + "protocol_upgrade_stake_threshold": [ + 4, + 5 + ], + "shard_layout": { + "V1": { + "boundary_accounts": [ + "aurora", + "aurora-0", + "game.hot.tg", + "kkuuue2akv_1630967379.near", + "tge-lockup.sweat" + ], + "shards_split_map": [ + [ + 0 ], - "shards_split_map": [ - [ - 0 - ], - [ - 1 - ], - [ - 2, - 3 - ], - [ - 4 - ], - [ - 5 - ] + [ + 1 ], - "to_parent_shard_map": [ - 0, - 1, - 2, + [ 2, - 3, + 3 + ], + [ 4 ], - "version": 3 - } - }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ + [ + 5 + ] + ], + "to_parent_shard_map": [ + 0, 1, - 6250 + 2, + 2, + 3, + 4 ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false + "version": 3 } - } \ No newline at end of file + }, + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/mainnet/71.json b/core/primitives/res/epoch_configs/mainnet/71.json index c34b2a82029..9b337da5ee0 100644 --- a/core/primitives/res/epoch_configs/mainnet/71.json +++ b/core/primitives/res/epoch_configs/mainnet/71.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 62500 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 62500 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/mainnet/72.json b/core/primitives/res/epoch_configs/mainnet/72.json index 2b2d278432a..4d750599381 100644 --- a/core/primitives/res/epoch_configs/mainnet/72.json +++ b/core/primitives/res/epoch_configs/mainnet/72.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 62500 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 62500 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/100.json b/core/primitives/res/epoch_configs/testnet/100.json index 7e026958f82..2b7002fec99 100644 --- a/core/primitives/res/epoch_configs/testnet/100.json +++ b/core/primitives/res/epoch_configs/testnet/100.json @@ -81,16 +81,14 @@ "version": 4 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 20, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 62500 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 20, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 62500 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/101.json b/core/primitives/res/epoch_configs/testnet/101.json index b4c00ef688b..ac3f1d52bb7 100644 --- a/core/primitives/res/epoch_configs/testnet/101.json +++ b/core/primitives/res/epoch_configs/testnet/101.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 20, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 62500 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 20, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 62500 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/143.json b/core/primitives/res/epoch_configs/testnet/143.json index 6664cd659a2..dda14b41663 100644 --- a/core/primitives/res/epoch_configs/testnet/143.json +++ b/core/primitives/res/epoch_configs/testnet/143.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 20, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 62500 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": true - } + "num_chunk_producer_seats": 20, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 62500 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": true } \ No newline at end of file diff --git a/core/primitives/res/epoch_configs/testnet/29.json b/core/primitives/res/epoch_configs/testnet/29.json index 7f6613d94b7..917cdf9d1a9 100644 --- a/core/primitives/res/epoch_configs/testnet/29.json +++ b/core/primitives/res/epoch_configs/testnet/29.json @@ -1,47 +1,45 @@ { - "epoch_length": 43200, - "num_block_producer_seats": 200, - "num_block_producer_seats_per_shard": [ - 200 - ], - "avg_hidden_validator_seats_per_shard": [ - 0 - ], - "block_producer_kickout_threshold": 80, - "chunk_producer_kickout_threshold": 90, - "chunk_validator_only_kickout_threshold": 80, - "target_validator_mandates_per_shard": 68, - "validator_max_kickout_stake_perc": 100, - "online_min_threshold": [ - 90, - 100 - ], - "online_max_threshold": [ - 99, - 100 - ], - "fishermen_threshold": "340282366920938463463374607431768211455", - "minimum_stake_divisor": 10, - "protocol_upgrade_stake_threshold": [ - 4, - 5 - ], - "shard_layout": { - "V0": { - "num_shards": 1, - "version": 0 - } - }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 300, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false + "epoch_length": 43200, + "num_block_producer_seats": 200, + "num_block_producer_seats_per_shard": [ + 200 + ], + "avg_hidden_validator_seats_per_shard": [ + 0 + ], + "block_producer_kickout_threshold": 80, + "chunk_producer_kickout_threshold": 90, + "chunk_validator_only_kickout_threshold": 80, + "target_validator_mandates_per_shard": 68, + "validator_max_kickout_stake_perc": 100, + "online_min_threshold": [ + 90, + 100 + ], + "online_max_threshold": [ + 99, + 100 + ], + "fishermen_threshold": "340282366920938463463374607431768211455", + "minimum_stake_divisor": 10, + "protocol_upgrade_stake_threshold": [ + 4, + 5 + ], + "shard_layout": { + "V0": { + "num_shards": 1, + "version": 0 } - } \ No newline at end of file + }, + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 300, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/48.json b/core/primitives/res/epoch_configs/testnet/48.json index 3089ac8975a..99580b68545 100644 --- a/core/primitives/res/epoch_configs/testnet/48.json +++ b/core/primitives/res/epoch_configs/testnet/48.json @@ -56,16 +56,14 @@ "version": 1 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 300, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 300, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/56.json b/core/primitives/res/epoch_configs/testnet/56.json index 3658e2a568a..82502387abd 100644 --- a/core/primitives/res/epoch_configs/testnet/56.json +++ b/core/primitives/res/epoch_configs/testnet/56.json @@ -56,16 +56,14 @@ "version": 1 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 200, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 200, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/64.json b/core/primitives/res/epoch_configs/testnet/64.json index 661d6396050..a0ae5be58a8 100644 --- a/core/primitives/res/epoch_configs/testnet/64.json +++ b/core/primitives/res/epoch_configs/testnet/64.json @@ -67,16 +67,14 @@ "version": 2 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 100, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 100, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/65.json b/core/primitives/res/epoch_configs/testnet/65.json index 9967bd464b7..5fd7b81df70 100644 --- a/core/primitives/res/epoch_configs/testnet/65.json +++ b/core/primitives/res/epoch_configs/testnet/65.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 100, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 100, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 100, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 100, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/69.json b/core/primitives/res/epoch_configs/testnet/69.json index 14b63c03cd8..6d48229be95 100644 --- a/core/primitives/res/epoch_configs/testnet/69.json +++ b/core/primitives/res/epoch_configs/testnet/69.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 20, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 6250 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 20, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/70.json b/core/primitives/res/epoch_configs/testnet/70.json index 34a868ddb45..6d48229be95 100644 --- a/core/primitives/res/epoch_configs/testnet/70.json +++ b/core/primitives/res/epoch_configs/testnet/70.json @@ -1,89 +1,87 @@ { - "epoch_length": 43200, - "num_block_producer_seats": 20, - "num_block_producer_seats_per_shard": [ - 20, - 20, - 20, - 20, - 20, - 20 - ], - "avg_hidden_validator_seats_per_shard": [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - "block_producer_kickout_threshold": 80, - "chunk_producer_kickout_threshold": 80, - "chunk_validator_only_kickout_threshold": 80, - "target_validator_mandates_per_shard": 68, - "validator_max_kickout_stake_perc": 30, - "online_min_threshold": [ - 90, - 100 - ], - "online_max_threshold": [ - 99, - 100 - ], - "fishermen_threshold": "340282366920938463463374607431768211455", - "minimum_stake_divisor": 10, - "protocol_upgrade_stake_threshold": [ - 4, - 5 - ], - "shard_layout": { - "V1": { - "boundary_accounts": [ - "aurora", - "aurora-0", - "game.hot.tg", - "kkuuue2akv_1630967379.near", - "tge-lockup.sweat" + "epoch_length": 43200, + "num_block_producer_seats": 20, + "num_block_producer_seats_per_shard": [ + 20, + 20, + 20, + 20, + 20, + 20 + ], + "avg_hidden_validator_seats_per_shard": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "block_producer_kickout_threshold": 80, + "chunk_producer_kickout_threshold": 80, + "chunk_validator_only_kickout_threshold": 80, + "target_validator_mandates_per_shard": 68, + "validator_max_kickout_stake_perc": 30, + "online_min_threshold": [ + 90, + 100 + ], + "online_max_threshold": [ + 99, + 100 + ], + "fishermen_threshold": "340282366920938463463374607431768211455", + "minimum_stake_divisor": 10, + "protocol_upgrade_stake_threshold": [ + 4, + 5 + ], + "shard_layout": { + "V1": { + "boundary_accounts": [ + "aurora", + "aurora-0", + "game.hot.tg", + "kkuuue2akv_1630967379.near", + "tge-lockup.sweat" + ], + "shards_split_map": [ + [ + 0 ], - "shards_split_map": [ - [ - 0 - ], - [ - 1 - ], - [ - 2, - 3 - ], - [ - 4 - ], - [ - 5 - ] + [ + 1 ], - "to_parent_shard_map": [ - 0, - 1, - 2, + [ 2, - 3, + 3 + ], + [ 4 ], - "version": 3 - } - }, - "validator_selection_config": { - "num_chunk_producer_seats": 20, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ + [ + 5 + ] + ], + "to_parent_shard_map": [ + 0, 1, - 6250 + 2, + 2, + 3, + 4 ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false + "version": 3 } - } \ No newline at end of file + }, + "num_chunk_producer_seats": 20, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/71.json b/core/primitives/res/epoch_configs/testnet/71.json index 47029a64823..06d21ad1470 100644 --- a/core/primitives/res/epoch_configs/testnet/71.json +++ b/core/primitives/res/epoch_configs/testnet/71.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 20, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 62500 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 20, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 62500 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/res/epoch_configs/testnet/72.json b/core/primitives/res/epoch_configs/testnet/72.json index b4c00ef688b..ac3f1d52bb7 100644 --- a/core/primitives/res/epoch_configs/testnet/72.json +++ b/core/primitives/res/epoch_configs/testnet/72.json @@ -74,16 +74,14 @@ "version": 3 } }, - "validator_selection_config": { - "num_chunk_producer_seats": 20, - "num_chunk_validator_seats": 300, - "num_chunk_only_producer_seats": 0, - "minimum_validators_per_shard": 1, - "minimum_stake_ratio": [ - 1, - 62500 - ], - "chunk_producer_assignment_changes_limit": 5, - "shuffle_shard_assignment_for_chunk_producers": false - } -} \ No newline at end of file + "num_chunk_producer_seats": 20, + "num_chunk_validator_seats": 300, + "num_chunk_only_producer_seats": 0, + "minimum_validators_per_shard": 1, + "minimum_stake_ratio": [ + 1, + 62500 + ], + "chunk_producer_assignment_changes_limit": 5, + "shuffle_shard_assignment_for_chunk_producers": false +} diff --git a/core/primitives/src/epoch_manager.rs b/core/primitives/src/epoch_manager.rs index 45ec908e0b0..3fb693ed1fe 100644 --- a/core/primitives/src/epoch_manager.rs +++ b/core/primitives/src/epoch_manager.rs @@ -11,7 +11,6 @@ use near_primitives_core::hash::CryptoHash; use near_primitives_core::serialize::dec_format; use near_primitives_core::version::{ProtocolFeature, PROTOCOL_VERSION}; use near_schema_checker_lib::ProtocolSchema; -use smart_default::SmartDefault; use std::collections::{BTreeMap, HashMap}; use std::fs; use std::ops::Bound; @@ -55,16 +54,133 @@ pub struct EpochConfig { pub protocol_upgrade_stake_threshold: Rational32, /// Shard layout of this epoch, may change from epoch to epoch pub shard_layout: ShardLayout, - /// Additional config for validator selection algorithm - pub validator_selection_config: ValidatorSelectionConfig, + /// Additional configuration parameters for the new validator selection + /// algorithm. See for details. + // #[default(100)] + pub num_chunk_producer_seats: NumSeats, + // #[default(300)] + pub num_chunk_validator_seats: NumSeats, + // TODO (#11267): deprecate after StatelessValidationV0 is in place. + // Use 300 for older protocol versions. + // #[default(300)] + pub num_chunk_only_producer_seats: NumSeats, + // #[default(1)] + pub minimum_validators_per_shard: NumSeats, + // #[default(Rational32::new(160, 1_000_000))] + pub minimum_stake_ratio: Rational32, + // #[default(5)] + /// Limits the number of shard changes in chunk producer assignments, + /// if algorithm is able to choose assignment with better balance of + /// number of chunk producers for shards. + pub chunk_producer_assignment_changes_limit: NumSeats, + // #[default(false)] + pub shuffle_shard_assignment_for_chunk_producers: bool, } impl EpochConfig { /// Total number of validator seats in the epoch since protocol version 69. pub fn num_validators(&self) -> NumSeats { self.num_block_producer_seats - .max(self.validator_selection_config.num_chunk_producer_seats) - .max(self.validator_selection_config.num_chunk_validator_seats) + .max(self.num_chunk_producer_seats) + .max(self.num_chunk_validator_seats) + } +} + +impl EpochConfig { + // Create test-only epoch config. + // Not depends on genesis! + pub fn genesis_test( + num_block_producer_seats: NumSeats, + shard_layout: ShardLayout, + epoch_length: BlockHeightDelta, + block_producer_kickout_threshold: u8, + chunk_producer_kickout_threshold: u8, + chunk_validator_only_kickout_threshold: u8, + protocol_upgrade_stake_threshold: Rational32, + fishermen_threshold: Balance, + ) -> Self { + Self { + epoch_length, + num_block_producer_seats, + num_block_producer_seats_per_shard: vec![ + num_block_producer_seats; + shard_layout.shard_ids().count() + ], + avg_hidden_validator_seats_per_shard: vec![], + target_validator_mandates_per_shard: 68, + validator_max_kickout_stake_perc: 100, + online_min_threshold: Rational32::new(90, 100), + online_max_threshold: Rational32::new(99, 100), + minimum_stake_divisor: 10, + protocol_upgrade_stake_threshold, + block_producer_kickout_threshold, + chunk_producer_kickout_threshold, + chunk_validator_only_kickout_threshold, + fishermen_threshold, + shard_layout, + num_chunk_producer_seats: 100, + num_chunk_validator_seats: 300, + num_chunk_only_producer_seats: 300, + minimum_validators_per_shard: 1, + minimum_stake_ratio: Rational32::new(160i32, 1_000_000i32), + chunk_producer_assignment_changes_limit: 5, + shuffle_shard_assignment_for_chunk_producers: false, + } + } + + /// Mignimal config for testing. + pub fn minimal() -> Self { + Self { + epoch_length: 0, + num_block_producer_seats: 0, + num_block_producer_seats_per_shard: vec![], + avg_hidden_validator_seats_per_shard: vec![], + block_producer_kickout_threshold: 0, + chunk_producer_kickout_threshold: 0, + chunk_validator_only_kickout_threshold: 0, + target_validator_mandates_per_shard: 0, + validator_max_kickout_stake_perc: 0, + online_min_threshold: 0.into(), + online_max_threshold: 0.into(), + fishermen_threshold: 0, + minimum_stake_divisor: 0, + protocol_upgrade_stake_threshold: 0.into(), + shard_layout: ShardLayout::get_simple_nightshade_layout(), + num_chunk_producer_seats: 100, + num_chunk_validator_seats: 300, + num_chunk_only_producer_seats: 300, + minimum_validators_per_shard: 1, + minimum_stake_ratio: Rational32::new(160i32, 1_000_000i32), + chunk_producer_assignment_changes_limit: 5, + shuffle_shard_assignment_for_chunk_producers: false, + } + } + + pub fn mock(epoch_length: BlockHeightDelta, shard_layout: ShardLayout) -> Self { + Self { + epoch_length, + num_block_producer_seats: 2, + num_block_producer_seats_per_shard: vec![1, 1], + avg_hidden_validator_seats_per_shard: vec![1, 1], + block_producer_kickout_threshold: 0, + chunk_producer_kickout_threshold: 0, + chunk_validator_only_kickout_threshold: 0, + target_validator_mandates_per_shard: 1, + validator_max_kickout_stake_perc: 0, + online_min_threshold: Rational32::new(1i32, 4i32), + online_max_threshold: Rational32::new(3i32, 4i32), + fishermen_threshold: 1, + minimum_stake_divisor: 1, + protocol_upgrade_stake_threshold: Rational32::new(3i32, 4i32), + shard_layout, + num_chunk_producer_seats: 100, + num_chunk_validator_seats: 300, + num_chunk_only_producer_seats: 300, + minimum_validators_per_shard: 1, + minimum_stake_ratio: Rational32::new(160i32, 1_000_000i32), + chunk_producer_assignment_changes_limit: 5, + shuffle_shard_assignment_for_chunk_producers: false, + } } } @@ -249,7 +365,7 @@ impl AllEpochConfig { // the codepaths for state sync more often. // TODO(#11201): When stabilizing "ShuffleShardAssignments" in mainnet, // also remove this temporary code and always rely on ShuffleShardAssignments. - config.validator_selection_config.shuffle_shard_assignment_for_chunk_producers = true; + config.shuffle_shard_assignment_for_chunk_producers = true; } /// Configures validator-selection related features. @@ -257,7 +373,7 @@ impl AllEpochConfig { // Shuffle shard assignments every epoch, to trigger state sync more // frequently to exercise that code path. if checked_feature!("stable", ShuffleShardAssignments, protocol_version) { - config.validator_selection_config.shuffle_shard_assignment_for_chunk_producers = true; + config.shuffle_shard_assignment_for_chunk_producers = true; } } @@ -312,7 +428,7 @@ impl AllEpochConfig { config.shard_layout.shard_ids().map(|_| 100).collect(); config.block_producer_kickout_threshold = 80; config.chunk_producer_kickout_threshold = 80; - config.validator_selection_config.num_chunk_only_producer_seats = 200; + config.num_chunk_only_producer_seats = 200; } // Adjust the number of block and chunk producers for testnet, to make it easier to test the change. @@ -324,18 +440,18 @@ impl AllEpochConfig { config.num_block_producer_seats = 20; // Checking feature NoChunkOnlyProducers in stateless validation if ProtocolFeature::StatelessValidation.enabled(protocol_version) { - config.validator_selection_config.num_chunk_producer_seats = 20; + config.num_chunk_producer_seats = 20; } config.num_block_producer_seats_per_shard = shard_ids.map(|_| config.num_block_producer_seats).collect(); // Decrease the number of chunk producers. - config.validator_selection_config.num_chunk_only_producer_seats = 100; + config.num_chunk_only_producer_seats = 100; } // Checking feature NoChunkOnlyProducers in stateless validation if ProtocolFeature::StatelessValidation.enabled(protocol_version) { // Make sure there is no chunk only producer in stateless validation - config.validator_selection_config.num_chunk_only_producer_seats = 0; + config.num_chunk_only_producer_seats = 0; } } @@ -347,7 +463,7 @@ impl AllEpochConfig { fn config_fix_min_stake_ratio(config: &mut EpochConfig, protocol_version: u32) { if checked_feature!("stable", FixMinStakeRatio, protocol_version) { - config.validator_selection_config.minimum_stake_ratio = Rational32::new(1, 62_500); + config.minimum_stake_ratio = Rational32::new(1, 62_500); } } @@ -375,31 +491,6 @@ impl AllEpochConfig { } } -/// Additional configuration parameters for the new validator selection -/// algorithm. See for details. -#[derive(Debug, Clone, SmartDefault, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct ValidatorSelectionConfig { - #[default(100)] - pub num_chunk_producer_seats: NumSeats, - #[default(300)] - pub num_chunk_validator_seats: NumSeats, - // TODO (#11267): deprecate after StatelessValidationV0 is in place. - // Use 300 for older protocol versions. - #[default(300)] - pub num_chunk_only_producer_seats: NumSeats, - #[default(1)] - pub minimum_validators_per_shard: NumSeats, - #[default(Rational32::new(160, 1_000_000))] - pub minimum_stake_ratio: Rational32, - #[default(5)] - /// Limits the number of shard changes in chunk producer assignments, - /// if algorithm is able to choose assignment with better balance of - /// number of chunk producers for shards. - pub chunk_producer_assignment_changes_limit: NumSeats, - #[default(false)] - pub shuffle_shard_assignment_for_chunk_producers: bool, -} - #[derive(BorshSerialize, BorshDeserialize, ProtocolSchema)] pub struct EpochSummary { pub prev_epoch_last_block_hash: CryptoHash, diff --git a/core/primitives/src/shard_layout.rs b/core/primitives/src/shard_layout.rs index ff91304d68c..3aea7f884b3 100644 --- a/core/primitives/src/shard_layout.rs +++ b/core/primitives/src/shard_layout.rs @@ -1072,7 +1072,7 @@ impl ShardInfo { #[cfg(test)] mod tests { - use crate::epoch_manager::{AllEpochConfig, EpochConfig, ValidatorSelectionConfig}; + use crate::epoch_manager::{AllEpochConfig, EpochConfig}; use crate::shard_layout::{ new_shard_ids_vec, new_shards_split_map, ShardLayout, ShardLayoutV1, ShardUId, }; @@ -1111,24 +1111,7 @@ mod tests { pub fn for_protocol_version(protocol_version: ProtocolVersion) -> Self { // none of the epoch config fields matter, we only need the shard layout // constructed through [`AllEpochConfig::for_protocol_version()`]. - let genesis_epoch_config = EpochConfig { - epoch_length: 0, - num_block_producer_seats: 0, - num_block_producer_seats_per_shard: vec![], - avg_hidden_validator_seats_per_shard: vec![], - block_producer_kickout_threshold: 0, - chunk_producer_kickout_threshold: 0, - chunk_validator_only_kickout_threshold: 0, - target_validator_mandates_per_shard: 0, - validator_max_kickout_stake_perc: 0, - online_min_threshold: 0.into(), - online_max_threshold: 0.into(), - fishermen_threshold: 0, - minimum_stake_divisor: 0, - protocol_upgrade_stake_threshold: 0.into(), - shard_layout: ShardLayout::get_simple_nightshade_layout(), - validator_selection_config: ValidatorSelectionConfig::default(), - }; + let genesis_epoch_config = EpochConfig::minimal(); let genesis_protocol_version = PROTOCOL_VERSION; let all_epoch_config = AllEpochConfig::new( diff --git a/core/primitives/src/utils.rs b/core/primitives/src/utils.rs index ead8ec029e9..36518f8ce09 100644 --- a/core/primitives/src/utils.rs +++ b/core/primitives/src/utils.rs @@ -5,6 +5,8 @@ use std::fmt; use chrono; use chrono::DateTime; +use near_primitives_core::types::BlockHeight; +use near_primitives_core::version::ProtocolFeature; use serde; use crate::hash::{hash, CryptoHash}; @@ -215,30 +217,33 @@ pub fn get_outcome_id_block_hash_rev(key: &[u8]) -> std::io::Result<(CryptoHash, Ok((outcome_id, block_hash)) } -/// Creates a new Receipt ID from a given signed transaction and a block hash. +/// Creates a new Receipt ID from a given signed transaction and a block height or hash. /// This method is backward compatible, so it takes the current protocol version. pub fn create_receipt_id_from_transaction( protocol_version: ProtocolVersion, signed_transaction: &SignedTransaction, prev_block_hash: &CryptoHash, block_hash: &CryptoHash, + block_height: BlockHeight, ) -> CryptoHash { create_hash_upgradable( protocol_version, &signed_transaction.get_hash(), prev_block_hash, block_hash, + block_height, 0, ) } -/// Creates a new Receipt ID from a given receipt id, a block hash and a new receipt index. +/// Creates a new Receipt ID from a given receipt id, a block height or hash and a new receipt index. /// This method is backward compatible, so it takes the current protocol version. pub fn create_receipt_id_from_receipt_id( protocol_version: ProtocolVersion, receipt_id: &CryptoHash, prev_block_hash: &CryptoHash, block_hash: &CryptoHash, + block_height: BlockHeight, receipt_index: usize, ) -> CryptoHash { create_hash_upgradable( @@ -246,32 +251,42 @@ pub fn create_receipt_id_from_receipt_id( receipt_id, prev_block_hash, block_hash, + block_height, receipt_index as u64, ) } -/// Creates a new action_hash from a given receipt, a block hash and an action index. +/// Creates a new action_hash from a given receipt, a block height or hash and an action index. /// This method is backward compatible, so it takes the current protocol version. pub fn create_action_hash_from_receipt_id( protocol_version: ProtocolVersion, receipt_id: &CryptoHash, prev_block_hash: &CryptoHash, block_hash: &CryptoHash, + block_height: BlockHeight, action_index: usize, ) -> CryptoHash { // Action hash uses the same input as a new receipt ID, so to avoid hash conflicts we use the // salt starting from the `u64` going backward. let salt = u64::MAX.wrapping_sub(action_index as u64); - create_hash_upgradable(protocol_version, receipt_id, prev_block_hash, block_hash, salt) + create_hash_upgradable( + protocol_version, + receipt_id, + prev_block_hash, + block_hash, + block_height, + salt, + ) } -/// Creates a new Receipt ID from a given action hash, a block hash and a new receipt index. +/// Creates a new Receipt ID from a given action hash, a block height or hash and a new receipt index. /// This method is backward compatible, so it takes the current protocol version. pub fn create_receipt_id_from_action_hash( protocol_version: ProtocolVersion, action_hash: &CryptoHash, prev_block_hash: &CryptoHash, block_hash: &CryptoHash, + block_height: BlockHeight, receipt_index: usize, ) -> CryptoHash { create_hash_upgradable( @@ -279,6 +294,7 @@ pub fn create_receipt_id_from_action_hash( action_hash, prev_block_hash, block_hash, + block_height, receipt_index as u64, ) } @@ -310,14 +326,17 @@ pub fn create_random_seed( /// Creates a new CryptoHash ID based on the protocol version. /// Before `CREATE_HASH_PROTOCOL_VERSION` it uses `create_nonce_with_nonce` with -/// just `base` and `salt`. But after `CREATE_HASH_PROTOCOL_VERSION` it uses -/// `extra_hash` in addition to the `base` and `salt`. +/// just `base` and `salt`. +/// After `CREATE_HASH_PROTOCOL_VERSION` it uses `extra_hash` in addition to the `base` and `salt`. /// E.g. this `extra_hash` can be a block hash to distinguish receipts between forks. +/// After ProtocolFeature::BlockHeightForReceiptId, the code uses `block_height` instead of `extra_hash`. +/// This enables applying chunks using only the optimistic block, which does not yet have a block hash. fn create_hash_upgradable( protocol_version: ProtocolVersion, base: &CryptoHash, extra_hash_old: &CryptoHash, extra_hash: &CryptoHash, + block_height: BlockHeight, salt: u64, ) -> CryptoHash { if protocol_version < CREATE_HASH_PROTOCOL_VERSION { @@ -327,13 +346,13 @@ fn create_hash_upgradable( size_of::() + size_of::() + size_of::(); let mut bytes: Vec = Vec::with_capacity(BYTES_LEN); bytes.extend_from_slice(base.as_ref()); - let extra_hash_used = - if protocol_version < CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION { - extra_hash_old - } else { - extra_hash - }; - bytes.extend_from_slice(extra_hash_used.as_ref()); + if ProtocolFeature::BlockHeightForReceiptId.enabled(protocol_version) { + bytes.extend_from_slice(block_height.to_le_bytes().as_ref()) + } else if protocol_version >= CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION { + bytes.extend_from_slice(extra_hash.as_ref()) + } else { + bytes.extend_from_slice(extra_hash_old.as_ref()) + }; bytes.extend(index_to_bytes(salt)); hash(&bytes) } @@ -496,6 +515,7 @@ pub fn derive_eth_implicit_account_id(public_key: &Secp256K1PublicKey) -> Accoun mod tests { use super::*; use near_crypto::{KeyType, PublicKey}; + use near_primitives_core::version::ProtocolFeature; #[test] fn test_derive_near_implicit_account_id() { @@ -529,6 +549,8 @@ mod tests { let base = hash(b"atata"); let extra_base = hash(b"hohoho"); let other_extra_base = hash(b"banana"); + let block_height: BlockHeight = 123_456_789; + let other_block_height: BlockHeight = 123_123_123; let salt = 3; assert_eq!( create_nonce_with_nonce(&base, salt), @@ -537,6 +559,7 @@ mod tests { &base, &extra_base, &extra_base, + block_height, salt, ) ); @@ -547,6 +570,7 @@ mod tests { &base, &extra_base, &extra_base, + block_height, salt, ) ); @@ -556,6 +580,7 @@ mod tests { &base, &extra_base, &extra_base, + block_height, salt, ), create_hash_upgradable( @@ -563,6 +588,7 @@ mod tests { &base, &other_extra_base, &other_extra_base, + block_height, salt, ) ); @@ -572,6 +598,7 @@ mod tests { &base, &extra_base, &other_extra_base, + block_height, salt, ), create_hash_upgradable( @@ -579,6 +606,7 @@ mod tests { &base, &extra_base, &other_extra_base, + block_height, salt, ) ); @@ -588,6 +616,7 @@ mod tests { &base, &extra_base, &other_extra_base, + block_height, salt, ), create_hash_upgradable( @@ -595,8 +624,66 @@ mod tests { &base, &other_extra_base, &other_extra_base, + block_height, salt ) ); + // Check that for protocol versions post BlockHeightForReceiptId, the hash does not depend on block hash. + assert_eq!( + create_hash_upgradable( + ProtocolFeature::BlockHeightForReceiptId.protocol_version(), + &base, + &extra_base, + &extra_base, + block_height, + salt, + ), + create_hash_upgradable( + ProtocolFeature::BlockHeightForReceiptId.protocol_version(), + &base, + &other_extra_base, + &other_extra_base, + block_height, + salt + ) + ); + // Check that for protocol versions pre BlockHeightForReceiptId, the hash does not depend on block height. + assert_eq!( + create_hash_upgradable( + ProtocolFeature::BlockHeightForReceiptId.protocol_version() - 1, + &base, + &extra_base, + &other_extra_base, + block_height, + salt, + ), + create_hash_upgradable( + ProtocolFeature::BlockHeightForReceiptId.protocol_version() - 1, + &base, + &extra_base, + &other_extra_base, + other_block_height, + salt, + ) + ); + // Check that for protocol versions post BlockHeightForReceiptId, the hash changes if block height changes. + assert_ne!( + create_hash_upgradable( + ProtocolFeature::BlockHeightForReceiptId.protocol_version(), + &base, + &extra_base, + &other_extra_base, + block_height, + salt, + ), + create_hash_upgradable( + ProtocolFeature::BlockHeightForReceiptId.protocol_version(), + &base, + &extra_base, + &other_extra_base, + other_block_height, + salt, + ) + ); } } diff --git a/core/store/src/flat/metrics.rs b/core/store/src/flat/metrics.rs index 19c601ff2ff..7c6033a07ac 100644 --- a/core/store/src/flat/metrics.rs +++ b/core/store/src/flat/metrics.rs @@ -1,3 +1,4 @@ +use super::{FlatStorageManager, FlatStorageStatus}; use crate::metrics::flat_state_metrics; use near_o11y::metrics::IntGauge; use near_primitives::{shard_layout::ShardUId, types::BlockHeight}; @@ -50,3 +51,105 @@ impl FlatStorageMetrics { self.cached_changes_size.set(cached_changes_size as i64); } } + +/// Metrics for flat storage resharding. +/// +/// This struct is a collection of metrics to monitor the operation of splitting a shard. +pub struct FlatStorageReshardingShardSplitMetrics { + parent_shard: ShardUId, + left_child_shard: ShardUId, + right_child_shard: ShardUId, + parent_status: IntGauge, + left_child_status: IntGauge, + right_child_status: IntGauge, + split_shard_processed_batches: IntGauge, + split_shard_batch_size: IntGauge, + split_shard_processed_bytes: IntGauge, +} + +impl FlatStorageReshardingShardSplitMetrics { + pub fn new( + parent_shard: ShardUId, + left_child_shard: ShardUId, + right_child_shard: ShardUId, + ) -> Self { + use flat_state_metrics::*; + let parent_shard_label = parent_shard.to_string(); + let left_child_shard_label = left_child_shard.to_string(); + let right_child_shard_label = right_child_shard.to_string(); + Self { + parent_shard, + left_child_shard, + right_child_shard, + parent_status: resharding::STATUS.with_label_values(&[&parent_shard_label]), + left_child_status: resharding::STATUS.with_label_values(&[&left_child_shard_label]), + right_child_status: resharding::STATUS.with_label_values(&[&right_child_shard_label]), + split_shard_processed_batches: resharding::SPLIT_SHARD_PROCESSED_BATCHES + .with_label_values(&[&parent_shard_label]), + split_shard_batch_size: resharding::SPLIT_SHARD_BATCH_SIZE.clone(), + split_shard_processed_bytes: resharding::SPLIT_SHARD_PROCESSED_BYTES + .with_label_values(&[&parent_shard_label]), + } + } + + pub fn set_parent_status(&self, status: &FlatStorageStatus) { + self.parent_status.set(status.into()); + } + + pub fn set_left_child_status(&self, status: &FlatStorageStatus) { + self.left_child_status.set(status.into()); + } + + pub fn set_right_child_status(&self, status: &FlatStorageStatus) { + self.right_child_status.set(status.into()); + } + + pub fn set_split_shard_processed_batches(&self, num_batches: usize) { + self.split_shard_processed_batches.set(num_batches as i64); + } + + pub fn update_shards_status(&self, manager: &FlatStorageManager) { + self.set_parent_status(&manager.get_flat_storage_status(self.parent_shard)); + self.set_left_child_status(&manager.get_flat_storage_status(self.left_child_shard)); + self.set_right_child_status(&manager.get_flat_storage_status(self.right_child_shard)); + } + + pub fn set_split_shard_batch_size(&self, batch_size: usize) { + self.split_shard_batch_size.set(batch_size as i64); + } + + pub fn set_split_shard_processed_bytes(&self, bytes: usize) { + self.split_shard_processed_bytes.set(bytes as i64); + } + + pub fn inc_split_shard_processed_bytes_by(&self, processed_bytes: usize) { + self.split_shard_processed_bytes.add(processed_bytes as i64); + } +} + +/// Metrics for flat storage resharding. +/// +/// This struct is a collection of metrics to monitor the catch up phase of a new shard. +pub struct FlatStorageReshardingShardCatchUpMetrics { + status: IntGauge, + head_height: IntGauge, +} + +impl FlatStorageReshardingShardCatchUpMetrics { + pub fn new(shard_uid: &ShardUId) -> Self { + use flat_state_metrics::*; + let shard_label = shard_uid.to_string(); + Self { + status: resharding::STATUS.with_label_values(&[&shard_label]), + head_height: FLAT_STORAGE_HEAD_HEIGHT.with_label_values(&[&shard_label]), + } + } + + pub fn set_status(&self, status: &FlatStorageStatus) { + self.status.set(status.into()); + } + + pub fn set_head_height(&self, height: u64) { + self.head_height.set(height as i64); + } +} diff --git a/core/store/src/flat/mod.rs b/core/store/src/flat/mod.rs index 1724344fe8b..b0478456de0 100644 --- a/core/store/src/flat/mod.rs +++ b/core/store/src/flat/mod.rs @@ -37,6 +37,9 @@ mod types; pub use chunk_view::FlatStorageChunkView; pub use delta::{FlatStateChanges, FlatStateDelta, FlatStateDeltaMetadata}; pub use manager::FlatStorageManager; +pub use metrics::{ + FlatStorageReshardingShardCatchUpMetrics, FlatStorageReshardingShardSplitMetrics, +}; pub use storage::FlatStorage; pub use types::{ BlockInfo, FetchingStateStatus, FlatStateIterator, FlatStorageCreationStatus, FlatStorageError, diff --git a/core/store/src/flat/types.rs b/core/store/src/flat/types.rs index e8f87826dde..87b718eb75b 100644 --- a/core/store/src/flat/types.rs +++ b/core/store/src/flat/types.rs @@ -48,7 +48,7 @@ impl From for StorageError { pub type FlatStorageResult = Result; #[derive( - BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, serde::Serialize, ProtocolSchema, + BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, serde::Serialize, ProtocolSchema, Clone, )] pub enum FlatStorageStatus { /// Flat Storage is not supported. @@ -89,7 +89,7 @@ impl Into for &FlatStorageStatus { } #[derive( - BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, serde::Serialize, ProtocolSchema, + BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, serde::Serialize, ProtocolSchema, Clone, )] pub struct FlatStorageReadyStatus { pub flat_head: BlockInfo, diff --git a/core/store/src/metrics.rs b/core/store/src/metrics.rs index e25b259c99d..d966bad9a5e 100644 --- a/core/store/src/metrics.rs +++ b/core/store/src/metrics.rs @@ -488,6 +488,45 @@ pub mod flat_state_metrics { .unwrap() }); } + + pub mod resharding { + use near_o11y::metrics::{ + try_create_int_gauge, try_create_int_gauge_vec, IntGauge, IntGaugeVec, + }; + use std::sync::LazyLock; + + pub static STATUS: LazyLock = LazyLock::new(|| { + try_create_int_gauge_vec( + "near_flat_storage_resharding_status", + "Integer representing status of flat storage resharding", + &["shard_uid"], + ) + .unwrap() + }); + pub static SPLIT_SHARD_PROCESSED_BATCHES: LazyLock = LazyLock::new(|| { + try_create_int_gauge_vec( + "near_flat_storage_resharding_split_shard_processed_batches", + "Number of processed batches inside the split shard task", + &["shard_uid"], + ) + .unwrap() + }); + pub static SPLIT_SHARD_BATCH_SIZE: LazyLock = LazyLock::new(|| { + try_create_int_gauge( + "near_flat_storage_resharding_split_shard_batch_size", + "Size in bytes of every batch inside the split shard task", + ) + .unwrap() + }); + pub static SPLIT_SHARD_PROCESSED_BYTES: LazyLock = LazyLock::new(|| { + try_create_int_gauge_vec( + "near_flat_storage_resharding_split_shard_processed_bytes", + "Total bytes of Flat State that have been split inside the split shard task", + &["shard_uid"], + ) + .unwrap() + }); + } } pub static COLD_STORE_MIGRATION_BATCH_WRITE_COUNT: LazyLock = LazyLock::new(|| { diff --git a/integration-tests/src/test_loop/builder.rs b/integration-tests/src/test_loop/builder.rs index b868d5edd10..43c15faccc9 100644 --- a/integration-tests/src/test_loop/builder.rs +++ b/integration-tests/src/test_loop/builder.rs @@ -166,14 +166,14 @@ fn should_drop_chunk_for_protocol_upgrade( let epoch_protocol_version = epoch_manager_adapter.get_epoch_protocol_version(&epoch_id).unwrap(); // Drop condition for the first epoch with new protocol version. - if epoch_protocol_version == version_of_protocol_upgrade { + if epoch_protocol_version >= version_of_protocol_upgrade { let prev_epoch_id = epoch_manager_adapter.get_prev_epoch_id_from_prev_block(prev_block_hash).unwrap(); let prev_epoch_protocol_version = epoch_manager_adapter.get_epoch_protocol_version(&prev_epoch_id).unwrap(); // If this is not the first epoch with new protocol version, // all chunks go through. - if prev_epoch_protocol_version == version_of_protocol_upgrade { + if prev_epoch_protocol_version >= version_of_protocol_upgrade { return false; } @@ -189,7 +189,7 @@ fn should_drop_chunk_for_protocol_upgrade( let epoch_start_height = epoch_manager_adapter.get_epoch_start_height(&prev_block_hash).unwrap(); range.contains(&(height_created as i64 - epoch_start_height as i64)) - } else if epoch_protocol_version + 1 == version_of_protocol_upgrade { + } else if epoch_protocol_version < version_of_protocol_upgrade { // Drop condition for the last epoch with old protocol version. let maybe_upgrade_height = epoch_manager_adapter .get_estimated_protocol_upgrade_block_height(*prev_block_hash) diff --git a/integration-tests/src/test_loop/tests/fix_min_stake_ratio.rs b/integration-tests/src/test_loop/tests/fix_min_stake_ratio.rs index 4bc77b452f2..f7bd732e387 100644 --- a/integration-tests/src/test_loop/tests/fix_min_stake_ratio.rs +++ b/integration-tests/src/test_loop/tests/fix_min_stake_ratio.rs @@ -140,10 +140,7 @@ fn slow_test_fix_min_stake_ratio() { return if validators.len() == 3 { assert!(validators.contains(&small_validator.to_string())); let epoch_config = client.epoch_manager.get_epoch_config(&tip.epoch_id).unwrap(); - assert_eq!( - epoch_config.validator_selection_config.minimum_stake_ratio, - Rational32::new(1, 62_500) - ); + assert_eq!(epoch_config.minimum_stake_ratio, Rational32::new(1, 62_500)); true } else { false diff --git a/integration-tests/src/test_loop/tests/protocol_upgrade.rs b/integration-tests/src/test_loop/tests/protocol_upgrade.rs index c5cb7ee550f..db320ab3606 100644 --- a/integration-tests/src/test_loop/tests/protocol_upgrade.rs +++ b/integration-tests/src/test_loop/tests/protocol_upgrade.rs @@ -89,10 +89,8 @@ pub(crate) fn test_protocol_upgrade( config.epoch_length = epoch_length; config.shard_layout = shard_layout.clone(); config.num_block_producer_seats = genesis_epoch_info.num_block_producer_seats; - config.validator_selection_config.num_chunk_producer_seats = - genesis_epoch_info.validator_selection_config.num_chunk_producer_seats; - config.validator_selection_config.num_chunk_validator_seats = - genesis_epoch_info.validator_selection_config.num_chunk_validator_seats; + config.num_chunk_producer_seats = genesis_epoch_info.num_chunk_producer_seats; + config.num_chunk_validator_seats = genesis_epoch_info.num_chunk_validator_seats; if !missing_chunk_ranges.is_empty() { config.block_producer_kickout_threshold = 0; diff --git a/integration-tests/src/test_loop/tests/resharding_v3.rs b/integration-tests/src/test_loop/tests/resharding_v3.rs index e0d87a6c243..b787c501725 100644 --- a/integration-tests/src/test_loop/tests/resharding_v3.rs +++ b/integration-tests/src/test_loop/tests/resharding_v3.rs @@ -30,13 +30,16 @@ use assert_matches::assert_matches; use near_client::client_actor::ClientActorInner; use near_crypto::Signer; use near_epoch_manager::EpochManagerAdapter; -use near_parameters::{RuntimeConfig, RuntimeConfigStore}; -use near_primitives::receipt::{BufferedReceiptIndices, DelayedReceiptIndices}; +use near_parameters::{vm, RuntimeConfig, RuntimeConfigStore}; +use near_primitives::receipt::{ + BufferedReceiptIndices, DelayedReceiptIndices, PromiseYieldIndices, +}; use near_primitives::state::FlatStateValue; use near_primitives::test_utils::create_user_test_signer; use near_primitives::transaction::SignedTransaction; use near_primitives::trie_key::TrieKey; use near_primitives::views::FinalExecutionStatus; +use near_store::flat::FlatStorageStatus; use std::cell::Cell; use std::u64; @@ -150,6 +153,8 @@ struct TestReshardingParameters { /// If non zero, split parent shard for flat state resharding will be delayed by an additional /// `BlockHeightDelta` number of blocks. Useful to simulate slower task completion. delay_flat_state_resharding: BlockHeightDelta, + /// Make promise yield timeout much shorter than normal. + short_yield_timeout: bool, } impl TestReshardingParameters { @@ -275,6 +280,11 @@ impl TestReshardingParameters { self.delay_flat_state_resharding = num_blocks; self } + + fn short_yield_timeout(mut self) -> Self { + self.short_yield_timeout = true; + self + } } // Returns a callable function that, when invoked inside a test loop iteration, can force the creation of a chain fork. @@ -320,12 +330,13 @@ fn fork_before_resharding_block(double_signing: bool) -> LoopActionFn { enum ReceiptKind { Delayed, Buffered, + PromiseYield, } -/// Checks that the shard containing `account` has a non empty set of receipts +/// Checks that the shards containing `accounts` have a non empty set of receipts /// of type `kind` at the resharding block. fn check_receipts_presence_at_resharding_block( - account: AccountId, + accounts: Vec, kind: ReceiptKind, ) -> LoopActionFn { Box::new( @@ -339,15 +350,17 @@ fn check_receipts_presence_at_resharding_block( return; } - check_receipts_at_block(client_actor, &account, &kind, tip); + accounts.iter().for_each(|account| { + check_receipts_at_block(client_actor, &account, &kind, tip.clone()) + }); }, ) } -/// Checks that the shard containing `account` has a non empty set of receipts +/// Checks that the shards containing `accounts` have a non empty set of receipts /// of type `kind` at the block after the resharding block. fn check_receipts_presence_after_resharding_block( - account: AccountId, + accounts: Vec, kind: ReceiptKind, ) -> LoopActionFn { Box::new( @@ -361,7 +374,9 @@ fn check_receipts_presence_after_resharding_block( return; } - check_receipts_at_block(client_actor, &account, &kind, tip); + accounts.iter().for_each(|account| { + check_receipts_at_block(client_actor, &account, &kind, tip.clone()) + }); }, ) } @@ -407,6 +422,11 @@ fn check_receipts_at_block( &tip.prev_block_hash, ); } + ReceiptKind::PromiseYield => check_promise_yield_receipts_exist_in_memtrie( + &client_actor.client, + &shard_uid, + &tip.prev_block_hash, + ), } } @@ -435,6 +455,18 @@ fn check_buffered_receipts_exist_in_memtrie( assert_ne!(indices.shard_buffers.values().fold(0, |acc, buffer| acc + buffer.len()), 0); } +/// Asserts that a non zero amount of promise yield receipts exist in MemTrie for the given shard. +fn check_promise_yield_receipts_exist_in_memtrie( + client: &Client, + shard_uid: &ShardUId, + prev_block_hash: &CryptoHash, +) { + let memtrie = get_memtrie_for_shard(client, shard_uid, prev_block_hash); + let indices: PromiseYieldIndices = + get(&memtrie, &TrieKey::PromiseYieldIndices).unwrap().unwrap(); + assert_ne!(indices.len(), 0); +} + /// Returns a loop action that invokes a costly method from a contract /// `CALLS_PER_BLOCK_HEIGHT` times per block height. /// @@ -513,17 +545,167 @@ fn call_burn_gas_contract( gas_burnt_per_call + 10 * TGAS, tip.last_block_hash, ); - let mut txs_vec = txs.take(); - tracing::debug!(target: "test", height=tip.height, tx_hash=?tx.get_hash(), ?signer_id, ?receiver_id, "submitting transaction"); - txs_vec.push((tx.get_hash(), tip.height)); - txs.set(txs_vec); - submit_tx(&node_datas, &rpc_id, tx); + store_and_submit_tx( + &node_datas, + &rpc_id, + &txs, + &signer_id, + &receiver_id, + tip.height, + tx, + ); } } }, ) } +/// Sends a promise-yield transaction before resharding. Then, if `call_resume` is `true` also sends +/// a yield-resume transaction after resharding, otherwise it lets the promise-yield go into timeout. +/// +/// Each `signer_id` sends transaction to the corresponding `receiver_id`. +/// +/// A few blocks after resharding all transactions outcomes are checked for successful execution. +fn call_promise_yield( + call_resume: bool, + signer_ids: Vec, + receiver_ids: Vec, +) -> LoopActionFn { + let resharding_height: Cell> = Cell::new(None); + let txs = Cell::new(vec![]); + let latest_height = Cell::new(0); + // TODO: to be fixed when all shard tracking gets disabled. + let rpc_id: AccountId = "account0".parse().unwrap(); + let promise_txs_sent = Cell::new(false); + let nonce = Cell::new(102); + let yield_payload = vec![]; + + Box::new( + move |node_datas: &[TestData], + test_loop_data: &mut TestLoopData, + client_handle: TestLoopDataHandle| { + let client_actor = &mut test_loop_data.get_mut(&client_handle); + let tip = client_actor.client.chain.head().unwrap(); + + // Run this action only once at every block height. + if latest_height.get() == tip.height { + return; + } + latest_height.set(tip.height); + + // The operation to be done depends on the current block height in relation to the + // resharding height. + match (resharding_height.get(), latest_height.get()) { + // Resharding happened in the previous block. + // Maybe send the resume transaction. + (Some(resharding), latest) if latest == resharding + 1 && call_resume => { + for (signer_id, receiver_id) in + signer_ids.clone().into_iter().zip(receiver_ids.clone().into_iter()) + { + let signer: Signer = create_user_test_signer(&signer_id).into(); + nonce.set(nonce.get() + 1); + let tx = SignedTransaction::call( + nonce.get(), + signer_id.clone(), + receiver_id.clone(), + &signer, + 0, + "call_yield_resume_read_data_id_from_storage".to_string(), + yield_payload.clone(), + 300 * TGAS, + tip.last_block_hash, + ); + store_and_submit_tx( + &node_datas, + &rpc_id, + &txs, + &signer_id, + &receiver_id, + tip.height, + tx, + ); + } + } + // Resharding happened a few blocks in the past. + // Check transactions' outcomes. + (Some(resharding), latest) if latest == resharding + 4 => { + let txs = txs.take(); + assert_ne!(txs.len(), 0); + for (tx, tx_height) in txs { + let tx_outcome = + client_actor.client.chain.get_partial_transaction_result(&tx); + let status = tx_outcome.as_ref().map(|o| o.status.clone()); + let status = status.unwrap(); + tracing::debug!(target: "test", ?tx_height, ?tx, ?status, "transaction status"); + assert_matches!(status, FinalExecutionStatus::SuccessValue(_)); + } + } + (Some(_resharding), _latest) => {} + // Resharding didn't happen in the past. + (None, _) => { + // Check if resharding will happen in this block. + if next_block_has_new_shard_layout( + client_actor.client.epoch_manager.as_ref(), + &tip, + ) { + tracing::debug!(target: "test", height=tip.height, "resharding height set"); + resharding_height.set(Some(tip.height)); + return; + } + // Before resharding, send a set of promise transactions, just once. + if promise_txs_sent.get() { + return; + } + for (signer_id, receiver_id) in + signer_ids.clone().into_iter().zip(receiver_ids.clone().into_iter()) + { + let signer: Signer = create_user_test_signer(&signer_id).into(); + nonce.set(nonce.get() + 1); + let tx = SignedTransaction::call( + nonce.get(), + signer_id.clone(), + receiver_id.clone(), + &signer, + 0, + "call_yield_create_return_promise".to_string(), + yield_payload.clone(), + 300 * TGAS, + tip.last_block_hash, + ); + store_and_submit_tx( + &node_datas, + &rpc_id, + &txs, + &signer_id, + &receiver_id, + tip.height, + tx, + ); + } + promise_txs_sent.set(true); + } + } + }, + ) +} + +/// Stores a transaction hash into `txs` and submits the transaction. +fn store_and_submit_tx( + node_datas: &[TestData], + rpc_id: &AccountId, + txs: &Cell>, + signer_id: &AccountId, + receiver_id: &AccountId, + height: u64, + tx: SignedTransaction, +) { + let mut txs_vec = txs.take(); + tracing::debug!(target: "test", height, tx_hash=?tx.get_hash(), ?signer_id, ?receiver_id, "submitting transaction"); + txs_vec.push((tx.get_hash(), height)); + txs.set(txs_vec); + submit_tx(&node_datas, &rpc_id, tx); +} + // We want to understand if the most recent block is a resharding block. To do // this check if the latest block is an epoch start and compare the two epochs' // shard layouts. @@ -661,11 +843,25 @@ fn assert_state_sanity( trie.lock_for_iter().iter().unwrap().collect::, _>>().unwrap(); assert_state_equal(&memtrie_state, &trie_state, shard_uid, "memtrie and trie"); - let Some(flat_store_chunk_view) = client - .chain - .runtime_adapter - .get_flat_storage_manager() - .chunk_view(shard_uid, final_head.last_block_hash) + let flat_storage_manager = client.chain.runtime_adapter.get_flat_storage_manager(); + // FlatStorageChunkView::iter_range() used below to retrieve all key-value pairs in Flat + // Storage only looks at the data committed into the DB. For this reasons comparing Flat + // Storage and Memtries makes sense only if we can retrieve a view at the same height from + // both. + if let FlatStorageStatus::Ready(status) = + flat_storage_manager.get_flat_storage_status(shard_uid) + { + if status.flat_head.hash != final_head.prev_block_hash { + tracing::warn!(target: "test", "skipping flat storage - memtrie state check"); + continue; + } else { + tracing::debug!(target: "test", "checking flat storage - memtrie state"); + } + } else { + continue; + }; + let Some(flat_store_chunk_view) = + flat_storage_manager.chunk_view(shard_uid, final_head.last_block_hash) else { continue; }; @@ -937,7 +1133,7 @@ fn test_resharding_v3_base(params: TestReshardingParameters) { let base_protocol_version = ProtocolFeature::SimpleNightshadeV4.protocol_version() - 1; let mut base_epoch_config = base_epoch_config_store.get_config(base_protocol_version).as_ref().clone(); - base_epoch_config.validator_selection_config.shuffle_shard_assignment_for_chunk_producers = + base_epoch_config.shuffle_shard_assignment_for_chunk_producers = params.shuffle_shard_assignment_for_chunk_producers; if !params.chunk_ranges_to_drop.is_empty() { base_epoch_config.block_producer_kickout_threshold = 0; @@ -981,10 +1177,19 @@ fn test_resharding_v3_base(params: TestReshardingParameters) { builder = builder.track_all_shards(); } - if params.limit_outgoing_gas { + if params.limit_outgoing_gas || params.short_yield_timeout { let mut runtime_config = RuntimeConfig::test(); - runtime_config.congestion_control_config.max_outgoing_gas = 100 * TGAS; - runtime_config.congestion_control_config.min_outgoing_gas = 100 * TGAS; + if params.limit_outgoing_gas { + runtime_config.congestion_control_config.max_outgoing_gas = 100 * TGAS; + runtime_config.congestion_control_config.min_outgoing_gas = 100 * TGAS; + } + if params.short_yield_timeout { + let mut wasm_config = vm::Config::clone(&runtime_config.wasm_config); + // Assuming the promise yield is sent at h=9 and resharding happens at h=13, let's set + // the timeout to trigger at h=14. + wasm_config.limit_config.yield_timeout_length_in_blocks = 5; + runtime_config.wasm_config = Arc::new(wasm_config); + } let runtime_config_store = RuntimeConfigStore::with_one_config(runtime_config); builder = builder.runtime_config_store(runtime_config_store); } @@ -1178,7 +1383,7 @@ fn test_resharding_v3_delayed_receipts_left_child() { 275 * TGAS, )) .add_loop_action(check_receipts_presence_at_resharding_block( - account, + vec![account], ReceiptKind::Delayed, )); test_resharding_v3_base(params); @@ -1197,7 +1402,7 @@ fn test_resharding_v3_delayed_receipts_right_child() { 275 * TGAS, )) .add_loop_action(check_receipts_presence_at_resharding_block( - account, + vec![account], ReceiptKind::Delayed, )); test_resharding_v3_base(params); @@ -1218,11 +1423,11 @@ fn test_resharding_v3_split_parent_buffered_receipts_base(base_shard_layout_vers 10 * TGAS, )) .add_loop_action(check_receipts_presence_at_resharding_block( - account_in_parent, + vec![account_in_parent], ReceiptKind::Buffered, )) .add_loop_action(check_receipts_presence_after_resharding_block( - account_in_left_child, + vec![account_in_left_child], ReceiptKind::Buffered, )); test_resharding_v3_base(params); @@ -1258,11 +1463,11 @@ fn test_resharding_v3_buffered_receipts_towards_splitted_shard_base( 10 * TGAS, )) .add_loop_action(check_receipts_presence_at_resharding_block( - account_in_stable_shard.clone(), + vec![account_in_stable_shard.clone()], ReceiptKind::Buffered, )) .add_loop_action(check_receipts_presence_after_resharding_block( - account_in_stable_shard, + vec![account_in_stable_shard], ReceiptKind::Buffered, )); test_resharding_v3_base(params); @@ -1346,3 +1551,52 @@ fn test_resharding_v3_shard_shuffling_slower_post_processing_tasks() { .delay_flat_state_resharding(2); test_resharding_v3_base(params); } + +#[test] +fn test_resharding_v3_yield_resume() { + let account_in_left_child: AccountId = "account4".parse().unwrap(); + let account_in_right_child: AccountId = "account6".parse().unwrap(); + let params = TestReshardingParameters::new() + .deploy_test_contract(account_in_left_child.clone()) + .deploy_test_contract(account_in_right_child.clone()) + .add_loop_action(call_promise_yield( + true, + vec![account_in_left_child.clone(), account_in_right_child.clone()], + vec![account_in_left_child.clone(), account_in_right_child.clone()], + )) + .add_loop_action(check_receipts_presence_at_resharding_block( + vec![account_in_left_child.clone(), account_in_right_child.clone()], + ReceiptKind::PromiseYield, + )) + .add_loop_action(check_receipts_presence_after_resharding_block( + vec![account_in_left_child, account_in_right_child], + ReceiptKind::PromiseYield, + )); + test_resharding_v3_base(params); +} + +#[test] +// TODO(resharding): fix nearcore and unignore this test. +#[ignore] +fn test_resharding_v3_yield_timeout() { + let account_in_left_child: AccountId = "account4".parse().unwrap(); + let account_in_right_child: AccountId = "account6".parse().unwrap(); + let params = TestReshardingParameters::new() + .deploy_test_contract(account_in_left_child.clone()) + .deploy_test_contract(account_in_right_child.clone()) + .short_yield_timeout() + .add_loop_action(call_promise_yield( + false, + vec![account_in_left_child.clone(), account_in_right_child.clone()], + vec![account_in_left_child.clone(), account_in_right_child.clone()], + )) + .add_loop_action(check_receipts_presence_at_resharding_block( + vec![account_in_left_child.clone(), account_in_right_child.clone()], + ReceiptKind::PromiseYield, + )) + .add_loop_action(check_receipts_presence_after_resharding_block( + vec![account_in_left_child, account_in_right_child], + ReceiptKind::PromiseYield, + )); + test_resharding_v3_base(params); +} diff --git a/nearcore/src/config.rs b/nearcore/src/config.rs index 5efce487b5b..08184e019b3 100644 --- a/nearcore/src/config.rs +++ b/nearcore/src/config.rs @@ -37,8 +37,6 @@ use near_jsonrpc::RpcConfig; use near_network::config::NetworkConfig; use near_network::tcp; use near_o11y::log_config::LogConfig; -use near_primitives::chains::MAINNET; -use near_primitives::epoch_manager::EpochConfigStore; use near_primitives::hash::CryptoHash; use near_primitives::shard_layout::ShardLayout; use near_primitives::test_utils::create_test_signer; @@ -48,7 +46,7 @@ use near_primitives::types::{ }; use near_primitives::utils::{from_timestamp, get_num_seats_per_shard}; use near_primitives::validator_signer::{InMemoryValidatorSigner, ValidatorSigner}; -use near_primitives::version::{ProtocolVersion, PROTOCOL_VERSION}; +use near_primitives::version::PROTOCOL_VERSION; #[cfg(feature = "rosetta_rpc")] use near_rosetta_rpc::RosettaRpcConfig; use near_store::config::{ @@ -792,7 +790,6 @@ pub fn init_configs( download_config_url: Option<&str>, boot_nodes: Option<&str>, max_gas_burnt_view: Option, - dump_epoch_config: Option, ) -> anyhow::Result<()> { fs::create_dir_all(dir).with_context(|| anyhow!("Failed to create directory {:?}", dir))?; @@ -987,20 +984,6 @@ pub fn init_configs( } } - if let Some(first_version) = dump_epoch_config { - let epoch_config_dir = dir.join("epoch_configs"); - fs::create_dir_all(epoch_config_dir.clone()) - .with_context(|| anyhow!("Failed to create directory {:?}", epoch_config_dir))?; - EpochConfigStore::for_chain_id(MAINNET, None) - .expect("Could not load the EpochConfigStore for mainnet.") - .dump_epoch_configs_between( - &first_version, - &PROTOCOL_VERSION, - epoch_config_dir.to_str().unwrap(), - ); - info!(target: "near", "Generated epoch configs files in {}", epoch_config_dir.display()); - } - Ok(()) } @@ -1526,7 +1509,6 @@ mod tests { None, None, None, - None, ) .unwrap(); let genesis = Genesis::from_file( @@ -1584,7 +1566,6 @@ mod tests { None, None, None, - None, ) .unwrap(); @@ -1618,7 +1599,6 @@ mod tests { None, None, None, - None, ) .unwrap(); diff --git a/neard/src/cli.rs b/neard/src/cli.rs index 8fb03570f34..ed84d245dd7 100644 --- a/neard/src/cli.rs +++ b/neard/src/cli.rs @@ -255,6 +255,7 @@ pub(super) enum NeardSubCommand { ReplayArchive(ReplayArchiveCommand), } +#[allow(unused)] #[derive(Debug, Clone)] enum FirstProtocolVersion { Since(ProtocolVersion), @@ -320,10 +321,6 @@ pub(super) struct InitCmd { /// from genesis configuration will be taken. #[clap(long)] max_gas_burnt_view: Option, - /// Dump epoch config from the given protocol version onwards. - /// Can be a number or the word "latest". - #[clap(long)] - dump_epoch_config: Option, } /// Warns if unsupported build of the executable is used on mainnet or testnet. @@ -381,11 +378,6 @@ impl InitCmd { None }; - let dump_epoch_config = self.dump_epoch_config.map(|first| match first { - FirstProtocolVersion::Since(version) => version, - FirstProtocolVersion::Latest => near_primitives::version::PROTOCOL_VERSION, - }); - nearcore::init_configs( home_dir, self.chain_id, @@ -401,7 +393,6 @@ impl InitCmd { self.download_config_url.as_deref(), self.boot_nodes.as_deref(), self.max_gas_burnt_view, - dump_epoch_config, ) .context("Failed to initialize configs") } diff --git a/pytest/tests/mocknet/helpers/neard_runner.py b/pytest/tests/mocknet/helpers/neard_runner.py index 86604f5d4bd..a05e3e2dcfd 100644 --- a/pytest/tests/mocknet/helpers/neard_runner.py +++ b/pytest/tests/mocknet/helpers/neard_runner.py @@ -1080,12 +1080,19 @@ def network_init(self): self.set_state(TestState.AMEND_GENESIS) else: cmd = [ - self.data['binaries'][0]['system_path'], '--home', - self.target_near_home_path(), 'fork-network', 'set-validators', + self.data['binaries'][0]['system_path'], + '--home', + self.target_near_home_path(), + 'fork-network', + 'set-validators', '--validators', - self.home_path('validators.json'), '--epoch-length', - str(n['epoch_length']), '--genesis-time', - str(n['genesis_time']) + self.home_path('validators.json'), + '--epoch-length', + str(n['epoch_length']), + '--genesis-time', + str(n['genesis_time']), + '--num-seats', + str(n['num_seats']), ] if new_chain_id is not None: cmd.append('--chain-id') diff --git a/runtime/runtime-params-estimator/src/main.rs b/runtime/runtime-params-estimator/src/main.rs index d7800ce879a..a229cf01d28 100644 --- a/runtime/runtime-params-estimator/src/main.rs +++ b/runtime/runtime-params-estimator/src/main.rs @@ -181,7 +181,6 @@ fn run_estimation(cli_args: CliArgs) -> anyhow::Result> { None, None, None, - None, ) .expect("failed to init config"); diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index df43f125883..ed832ee9cb6 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -201,6 +201,7 @@ pub(crate) fn action_function_call( apply_state.epoch_id, apply_state.prev_block_hash, apply_state.block_hash, + apply_state.block_height, epoch_info_provider, apply_state.current_protocol_version, ); diff --git a/runtime/runtime/src/ext.rs b/runtime/runtime/src/ext.rs index dfa1836eaf3..c6b7082d571 100644 --- a/runtime/runtime/src/ext.rs +++ b/runtime/runtime/src/ext.rs @@ -6,7 +6,7 @@ use near_primitives::checked_feature; use near_primitives::errors::{EpochError, StorageError}; use near_primitives::hash::CryptoHash; use near_primitives::trie_key::{trie_key_parsers, TrieKey}; -use near_primitives::types::{AccountId, Balance, EpochId, EpochInfoProvider, Gas}; +use near_primitives::types::{AccountId, Balance, BlockHeight, EpochId, EpochInfoProvider, Gas}; use near_primitives::utils::create_receipt_id_from_action_hash; use near_primitives::version::ProtocolVersion; use near_store::contract::ContractStorage; @@ -28,6 +28,7 @@ pub struct RuntimeExt<'a> { epoch_id: EpochId, prev_block_hash: CryptoHash, last_block_hash: CryptoHash, + block_height: BlockHeight, epoch_info_provider: &'a dyn EpochInfoProvider, current_protocol_version: ProtocolVersion, } @@ -70,6 +71,7 @@ impl<'a> RuntimeExt<'a> { epoch_id: EpochId, prev_block_hash: CryptoHash, last_block_hash: CryptoHash, + block_height: BlockHeight, epoch_info_provider: &'a dyn EpochInfoProvider, current_protocol_version: ProtocolVersion, ) -> Self { @@ -83,6 +85,7 @@ impl<'a> RuntimeExt<'a> { epoch_id, prev_block_hash, last_block_hash, + block_height, epoch_info_provider, current_protocol_version, } @@ -188,6 +191,7 @@ impl<'a> External for RuntimeExt<'a> { &self.action_hash, &self.prev_block_hash, &self.last_block_hash, + self.block_height, self.data_count as usize, ); self.data_count += 1; diff --git a/runtime/runtime/src/lib.rs b/runtime/runtime/src/lib.rs index 5b68843d614..8778fc8589f 100644 --- a/runtime/runtime/src/lib.rs +++ b/runtime/runtime/src/lib.rs @@ -336,6 +336,7 @@ impl Runtime { signed_transaction, &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, ); let receipt = Receipt::V0(ReceiptV0 { predecessor_id: transaction.signer_id().clone(), @@ -645,6 +646,7 @@ impl Runtime { receipt.receipt_id(), &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, action_index, ); let mut new_result = self.apply_action( @@ -865,6 +867,7 @@ impl Runtime { receipt.receipt_id(), &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, receipt_index, ); @@ -897,6 +900,7 @@ impl Runtime { receipt.receipt_id(), &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, receipt_index as usize, )) } @@ -2083,18 +2087,27 @@ impl Runtime { let mut own_congestion_info = receipt_sink.own_congestion_info(); if let Some(congestion_info) = &mut own_congestion_info { delayed_receipts.apply_congestion_changes(congestion_info)?; - let shard_layout = epoch_info_provider.shard_layout(&apply_state.epoch_id)?; - let shard_ids = shard_layout.shard_ids().collect_vec(); - let shard_index = shard_layout - .get_shard_index(apply_state.shard_id) - .map_err(Into::::into)? - .try_into() - .expect("Shard Index must fit within u64"); - - let congestion_seed = apply_state.block_height.wrapping_add(shard_index); + let protocol_version = apply_state.current_protocol_version; + + let (all_shards, shard_seed) = + if ProtocolFeature::SimpleNightshadeV4.enabled(protocol_version) { + let shard_layout = epoch_info_provider.shard_layout(&apply_state.epoch_id)?; + let shard_ids = shard_layout.shard_ids().collect_vec(); + let shard_index = shard_layout + .get_shard_index(apply_state.shard_id) + .map_err(Into::::into)? + .try_into() + .expect("Shard Index must fit within u64"); + + (shard_ids, shard_index) + } else { + (apply_state.congestion_info.all_shards(), apply_state.shard_id.into()) + }; + + let congestion_seed = apply_state.block_height.wrapping_add(shard_seed); congestion_info.finalize_allowed_shard( apply_state.shard_id, - shard_ids.as_slice(), + &all_shards, congestion_seed, ); } @@ -2379,6 +2392,7 @@ fn resolve_promise_yield_timeouts( &queue_entry.data_id, &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, new_receipt_index, ); new_receipt_index += 1; diff --git a/runtime/runtime/src/state_viewer/mod.rs b/runtime/runtime/src/state_viewer/mod.rs index 7cc43f5fe93..e83f5027bea 100644 --- a/runtime/runtime/src/state_viewer/mod.rs +++ b/runtime/runtime/src/state_viewer/mod.rs @@ -266,6 +266,7 @@ impl TrieViewer { view_state.epoch_id, view_state.prev_block_hash, view_state.block_hash, + view_state.block_height, epoch_info_provider, view_state.current_protocol_version, ); diff --git a/runtime/runtime/src/tests/apply.rs b/runtime/runtime/src/tests/apply.rs index 50fa7439b1b..f48a8fa7db6 100644 --- a/runtime/runtime/src/tests/apply.rs +++ b/runtime/runtime/src/tests/apply.rs @@ -532,19 +532,22 @@ fn test_apply_delayed_receipts_local_tx() { PROTOCOL_VERSION, &local_transactions[0], &apply_state.prev_block_hash, - &apply_state.block_hash + &apply_state.block_hash, + apply_state.block_height, ), // receipt for tx 0 create_receipt_id_from_transaction( PROTOCOL_VERSION, &local_transactions[1], &apply_state.prev_block_hash, - &apply_state.block_hash + &apply_state.block_hash, + apply_state.block_height, ), // receipt for tx 1 create_receipt_id_from_transaction( PROTOCOL_VERSION, &local_transactions[2], &apply_state.prev_block_hash, - &apply_state.block_hash + &apply_state.block_hash, + apply_state.block_height, ), // receipt for tx 2 ], "STEP #1 failed", @@ -578,12 +581,14 @@ fn test_apply_delayed_receipts_local_tx() { &local_transactions[4], &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, ), // receipt for tx 4 create_receipt_id_from_transaction( PROTOCOL_VERSION, &local_transactions[3], &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, ), // receipt for tx 3 *receipts[0].receipt_id(), // receipt #0 ], @@ -621,18 +626,21 @@ fn test_apply_delayed_receipts_local_tx() { &local_transactions[5], &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, ), // receipt for tx 5 create_receipt_id_from_transaction( PROTOCOL_VERSION, &local_transactions[6], &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, ), // receipt for tx 6 create_receipt_id_from_transaction( PROTOCOL_VERSION, &local_transactions[7], &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, ), // receipt for tx 7 ], "STEP #3 failed", @@ -667,6 +675,7 @@ fn test_apply_delayed_receipts_local_tx() { &local_transactions[8], &apply_state.prev_block_hash, &apply_state.block_hash, + apply_state.block_height, ), // receipt for tx 8 ], "STEP #4 failed", diff --git a/tools/debug-ui/package-lock.json b/tools/debug-ui/package-lock.json index ec8b3a5373f..a161fb8cfc1 100644 --- a/tools/debug-ui/package-lock.json +++ b/tools/debug-ui/package-lock.json @@ -12815,9 +12815,15 @@ } }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -27433,9 +27439,9 @@ } }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==" }, "natural-compare": { "version": "1.4.0", diff --git a/tools/fork-network/src/cli.rs b/tools/fork-network/src/cli.rs index c8715cdf2c3..8c7fad876fc 100644 --- a/tools/fork-network/src/cli.rs +++ b/tools/fork-network/src/cli.rs @@ -14,7 +14,7 @@ use near_parameters::{RuntimeConfig, RuntimeConfigStore}; use near_primitives::account::id::AccountType; use near_primitives::account::{AccessKey, AccessKeyPermission, Account}; use near_primitives::borsh; -use near_primitives::epoch_manager::EpochConfigStore; +use near_primitives::epoch_manager::{EpochConfig, EpochConfigStore}; use near_primitives::hash::CryptoHash; use near_primitives::serialize::dec_format; use near_primitives::shard_layout::ShardUId; @@ -23,7 +23,7 @@ use near_primitives::state_record::StateRecord; use near_primitives::trie_key::col; use near_primitives::trie_key::trie_key_parsers::parse_account_id_from_account_key; use near_primitives::types::{ - AccountId, AccountInfo, Balance, BlockHeight, EpochId, NumBlocks, ShardId, StateRoot, + AccountId, AccountInfo, Balance, BlockHeight, EpochId, NumBlocks, NumSeats, ShardId, StateRoot, }; use near_primitives::version::{ProtocolVersion, PROTOCOL_VERSION}; use near_store::adapter::StoreAdapter; @@ -36,7 +36,7 @@ use near_store::{ use nearcore::{load_config, open_storage, NearConfig, NightshadeRuntime, NightshadeRuntimeExt}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use serde::Deserialize; -use std::collections::HashSet; +use std::collections::{BTreeMap, HashSet}; use std::fs::File; use std::io::BufReader; use std::path::{Path, PathBuf}; @@ -122,6 +122,9 @@ struct SetValidatorsCmd { /// will be used. #[arg(long)] pub protocol_version: Option, + /// Number of validator seats. + #[clap(long)] + pub num_seats: Option, } const FORKED_ROOTS_KEY_PREFIX: &str = "FORK_TOOL_SHARD_ID:"; @@ -211,12 +214,14 @@ impl ForkNetworkCommand { epoch_length, chain_id_suffix, chain_id, + num_seats, }) => { self.set_validators( genesis_time.unwrap_or_else(chrono::Utc::now), *protocol_version, validators, *epoch_length, + num_seats, chain_id_suffix, chain_id, near_config, @@ -395,6 +400,7 @@ impl ForkNetworkCommand { protocol_version: Option, validators: &Path, epoch_length: u64, + num_seats: &Option, chain_id_suffix: &str, chain_id: &Option, near_config: &mut NearConfig, @@ -421,7 +427,7 @@ impl ForkNetworkCommand { let runtime_config = runtime_config_store.get_config(PROTOCOL_VERSION); let storage_mutator = - StorageMutator::new(epoch_manager.clone(), &runtime, epoch_id, prev_state_roots)?; + StorageMutator::new(epoch_manager, &runtime, epoch_id, prev_state_roots)?; let (new_state_roots, new_validator_accounts) = self.add_validator_accounts(validators, runtime_config, home_dir, storage_mutator)?; @@ -431,13 +437,12 @@ impl ForkNetworkCommand { genesis_time, protocol_version, epoch_length, + num_seats, block_height, chain_id_suffix, chain_id, - &epoch_id, new_state_roots.clone(), new_validator_accounts.clone(), - epoch_manager, home_dir, near_config, )?; @@ -527,6 +532,47 @@ impl ForkNetworkCommand { return Ok(()); } + /// Creates epoch config overrides since `first_version` and places them + /// in `home_dir`. + fn override_epoch_configs( + &self, + first_version: ProtocolVersion, + num_seats: &Option, + home_dir: &Path, + ) -> anyhow::Result { + let epoch_config_dir = home_dir.join("epoch_configs"); + if epoch_config_dir.exists() { + std::fs::remove_dir_all(epoch_config_dir.clone())?; + } + std::fs::create_dir_all(epoch_config_dir.clone()).with_context(|| { + anyhow::anyhow!("Failed to create directory {:?}", epoch_config_dir) + })?; + + let base_epoch_config_store = + EpochConfigStore::for_chain_id(near_primitives::chains::MAINNET, None) + .expect("Could not load the EpochConfigStore for mainnet."); + let mut new_epoch_configs = BTreeMap::new(); + for version in first_version..=PROTOCOL_VERSION { + let mut config = base_epoch_config_store.get_config(version).as_ref().clone(); + if let Some(num_seats) = num_seats { + config.num_block_producer_seats = *num_seats; + config.num_chunk_producer_seats = *num_seats; + config.num_chunk_validator_seats = *num_seats; + } + new_epoch_configs.insert(version, Arc::new(config)); + } + let first_config = new_epoch_configs.get(&first_version).unwrap().as_ref().clone(); + let epoch_config_store = EpochConfigStore::test(new_epoch_configs); + + epoch_config_store.dump_epoch_configs_between( + &first_version, + &PROTOCOL_VERSION, + epoch_config_dir.to_str().unwrap(), + ); + tracing::info!(target: "near", "Generated epoch configs files in {}", epoch_config_dir.display()); + Ok(first_config) + } + fn prepare_shard_state( &self, batch_size: u64, @@ -824,13 +870,12 @@ impl ForkNetworkCommand { genesis_time: DateTime, protocol_version: Option, epoch_length: u64, + num_seats: &Option, height: BlockHeight, chain_id_suffix: &str, chain_id: &Option, - epoch_id: &EpochId, new_state_roots: Vec, new_validator_accounts: Vec, - epoch_manager: Arc, home_dir: &Path, near_config: &mut NearConfig, ) -> anyhow::Result<()> { @@ -850,18 +895,9 @@ impl ForkNetworkCommand { // This is based on the assumption that epoch length is part of genesis config and not epoch config. near_config.genesis.config.epoch_length = epoch_length; - let epoch_config_dir = home_dir.join("epoch_configs"); - let epoch_config = if epoch_config_dir.exists() { - tracing::info!(target: "fork-network", "Loading epoch config from {:?}", epoch_config_dir); - EpochConfigStore::for_chain_id(&new_chain_id, Some(epoch_config_dir)) - .unwrap() - .get_config(genesis_protocol_version) - .as_ref() - .clone() - } else { - tracing::info!(target: "fork-network", "Loading epoch config from epoch manager"); - epoch_manager.get_epoch_config(epoch_id)? - }; + let epoch_config = + self.override_epoch_configs(genesis_protocol_version, num_seats, home_dir)?; + let original_config = near_config.genesis.config.clone(); let new_config = GenesisConfig { @@ -883,15 +919,10 @@ impl ForkNetworkCommand { minimum_stake_divisor: epoch_config.minimum_stake_divisor, protocol_upgrade_stake_threshold: epoch_config.protocol_upgrade_stake_threshold, shard_layout: epoch_config.shard_layout.clone(), - num_chunk_only_producer_seats: epoch_config - .validator_selection_config - .num_chunk_only_producer_seats, - minimum_validators_per_shard: epoch_config - .validator_selection_config - .minimum_validators_per_shard, - minimum_stake_ratio: epoch_config.validator_selection_config.minimum_stake_ratio, + num_chunk_only_producer_seats: epoch_config.num_chunk_only_producer_seats, + minimum_validators_per_shard: epoch_config.minimum_validators_per_shard, + minimum_stake_ratio: epoch_config.minimum_stake_ratio, shuffle_shard_assignment_for_chunk_producers: epoch_config - .validator_selection_config .shuffle_shard_assignment_for_chunk_producers, dynamic_resharding: false, protocol_version: genesis_protocol_version, @@ -907,14 +938,9 @@ impl ForkNetworkCommand { total_supply: original_config.total_supply, transaction_validity_period: original_config.transaction_validity_period, use_production_config: original_config.use_production_config, - num_chunk_producer_seats: epoch_config - .validator_selection_config - .num_chunk_producer_seats, - num_chunk_validator_seats: epoch_config - .validator_selection_config - .num_chunk_validator_seats, + num_chunk_producer_seats: epoch_config.num_chunk_producer_seats, + num_chunk_validator_seats: epoch_config.num_chunk_validator_seats, chunk_producer_assignment_changes_limit: epoch_config - .validator_selection_config .chunk_producer_assignment_changes_limit, }; diff --git a/tools/state-viewer/src/commands.rs b/tools/state-viewer/src/commands.rs index f839e9ddfad..f52ab22466f 100644 --- a/tools/state-viewer/src/commands.rs +++ b/tools/state-viewer/src/commands.rs @@ -993,7 +993,7 @@ pub(crate) fn print_epoch_analysis( // chain/epoch-manager/src/validator_selection.rs:227:13. // Probably has something to do with extreme case where all // proposals are selected. - next_next_epoch_config.validator_selection_config.num_chunk_validator_seats = 100; + next_next_epoch_config.num_chunk_validator_seats = 100; } }