From af5a3fb64d5c70f1603378184f9fb3609a32912b Mon Sep 17 00:00:00 2001 From: Waclaw Banasik Date: Tue, 10 Dec 2024 18:28:41 +0000 Subject: [PATCH 01/11] fix(congestion_control) - proper protocol upgrade handling for congestion seed (#12594) Fixing unhandled protocol upgrade introduced in #12555. --- runtime/runtime/src/lib.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/runtime/runtime/src/lib.rs b/runtime/runtime/src/lib.rs index 5b68843d614..66c9b95290d 100644 --- a/runtime/runtime/src/lib.rs +++ b/runtime/runtime/src/lib.rs @@ -2083,18 +2083,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, ); } From 0b1bcc9f7b655d440ae52c5346b0faf434b0237e Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 10 Dec 2024 21:14:44 +0200 Subject: [PATCH 02/11] chore: update smallvec, url, idna (#12591) Should fix the audit error. The updates seem like they should be risk free. (Holy hot dog, whole ICU??? For urls??????) Co-authored-by: Andrea --- Cargo.lock | 297 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 270 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e25c43a2d28..bc66e7f561c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2114,6 +2114,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" @@ -2583,9 +2594,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", ] @@ -3081,6 +3092,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" @@ -3095,12 +3224,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]] @@ -3572,6 +3712,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" @@ -6009,9 +6155,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" @@ -7495,9 +7641,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", ] @@ -7764,6 +7910,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" @@ -7984,6 +8141,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" @@ -8387,27 +8554,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" @@ -8440,9 +8592,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", @@ -8455,6 +8607,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" @@ -9377,6 +9541,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" @@ -9422,6 +9598,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" @@ -9442,6 +9642,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" @@ -9475,6 +9696,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" From c4252890060f173267f445c840a897729a2f6859 Mon Sep 17 00:00:00 2001 From: Andrei <122784628+andrei-near@users.noreply.github.com> Date: Tue, 10 Dec 2024 21:03:44 +0100 Subject: [PATCH 03/11] Chore: notify on CI periodic run failures in Zulip (#12593) Screenshot 2024-12-10 at 14 35 09 --- .github/workflows/ci_nightly.yml | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) 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 }} " From 6bc4f567d267869cfc7cd54f5e65baa4aaf1f4ee Mon Sep 17 00:00:00 2001 From: Andrea Date: Tue, 10 Dec 2024 21:23:29 +0100 Subject: [PATCH 04/11] fix: quick workaround for flat storage memtrie comparison (#12592) Quick fix to allow state comparison between flat storage and memtries only when the view over key value pairs is at the same height, for both of them. --- .../src/test_loop/tests/resharding_v3.rs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/integration-tests/src/test_loop/tests/resharding_v3.rs b/integration-tests/src/test_loop/tests/resharding_v3.rs index e0d87a6c243..89ce9df3d1f 100644 --- a/integration-tests/src/test_loop/tests/resharding_v3.rs +++ b/integration-tests/src/test_loop/tests/resharding_v3.rs @@ -37,6 +37,7 @@ 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; @@ -661,11 +662,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; }; From beb00f4aa1b344a196012b9357b02e21cf167aab Mon Sep 17 00:00:00 2001 From: Andrea Date: Tue, 10 Dec 2024 21:23:59 +0100 Subject: [PATCH 05/11] feat(tests): test loop for promise yield receipts in reshardingV3 (#12582) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding two new tests for resharding V3: - `test_resharding_v3_yield_resume`: tests promise-yield receipts created before the resharding block and resumed after. It works out of the box. - `test_resharding_v3_yield_timeout`: tests promise-yield receipts created before the resharding block and timing out after. Doesn't work when the account belongs to the parent shard. Failure is: ``` 10.556s DEBUG clear_data: garbage_collection: GC block_hash gc_mode=GCMode::Canonical block_hash=J5MyhbZDtgNFofZ1DrWYbh4f8MDLaBkAeRSmkerQFuM1 10.556s DEBUG clear_data:ChainStoreUpdate::commit:ChainUpdate::finalize: store: close time.busy=10.5µs time.idle=374ns thread 'test_loop::tests::resharding_v3::test_resharding_v3_yield_timeout' panicked at core/store/src/db/testdb.rs:100:25: Inserting value with non-positive refcount note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 10.557s DEBUG clear_data:ChainStoreUpdate::commit: store: close time.busy=725µs time.idle=417ns 10.557s DEBUG clear_data: garbage_collection: close time.busy=1.72ms time.idle=500ns ``` Haven't looked deeply into the error, might be similar to the delayed receipts issue. Minor refactoring to avoid excessive code duplication. --- .../src/test_loop/tests/resharding_v3.rs | 283 ++++++++++++++++-- 1 file changed, 261 insertions(+), 22 deletions(-) diff --git a/integration-tests/src/test_loop/tests/resharding_v3.rs b/integration-tests/src/test_loop/tests/resharding_v3.rs index 89ce9df3d1f..f0697d22bb5 100644 --- a/integration-tests/src/test_loop/tests/resharding_v3.rs +++ b/integration-tests/src/test_loop/tests/resharding_v3.rs @@ -30,8 +30,10 @@ 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; @@ -151,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 { @@ -276,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. @@ -321,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( @@ -340,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( @@ -362,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()) + }); }, ) } @@ -408,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, + ), } } @@ -436,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. /// @@ -514,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. @@ -996,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); } @@ -1193,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); @@ -1212,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); @@ -1233,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); @@ -1273,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); @@ -1361,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); +} From f4c7f53cdade5ac3e83ca85648bd8d3957ee9a95 Mon Sep 17 00:00:00 2001 From: Aleksandr Logunov Date: Wed, 11 Dec 2024 10:25:06 +0700 Subject: [PATCH 06/11] feat(fork-network): override configs at SetValidatorsCmd (#12575) I've made two observations: * `num_seats` is not propagated to forknet in `set-validators` command, like it was done in `amend-genesis` * the actual node dir preparation happens at `set-validators`, not `init_configs` So I suggest to move epoch config overrides to `set-validators` as well. It looks very natural. I think we can always assume that `genesis_protocol_version` is the first version, and then pass epoch config overrides through `new-test` to `set-validators`. I used this for forknet resharding and it worked well. If epoch config overrides happen at init_configs, then, after init and before new-test, forknet nodes will be in weird state where epoch configs are already overridden but genesis isn't. --- chain/indexer/src/lib.rs | 1 - nearcore/src/config.rs | 22 +----- neard/src/cli.rs | 11 +-- pytest/tests/mocknet/helpers/neard_runner.py | 17 +++-- runtime/runtime-params-estimator/src/main.rs | 1 - tools/fork-network/src/cli.rs | 76 ++++++++++++++------ 6 files changed, 70 insertions(+), 58 deletions(-) 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/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/tools/fork-network/src/cli.rs b/tools/fork-network/src/cli.rs index c8715cdf2c3..483c16578ca 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.validator_selection_config.num_chunk_producer_seats = *num_seats; + config.validator_selection_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 { From e594174603865a759f3854f76110e239ede373e5 Mon Sep 17 00:00:00 2001 From: Razvan Barbascu Date: Wed, 11 Dec 2024 09:02:09 +0000 Subject: [PATCH 07/11] fix(epoch config): Refactor validator assignment config (#12520) This PR does not change the `EpochConfig` functionality, it just flatten the `EpochConfig` struct. Cetralize the epoch config contructors. This PR is a precursor to the config overriedes on `EpochConfigs`. --- chain/chain/src/runtime/mod.rs | 11 +- chain/chain/src/test_utils/kv_runtime.rs | 21 +-- chain/epoch-manager/src/shard_tracker.rs | 8 +- chain/epoch-manager/src/test_utils.rs | 13 +- chain/epoch-manager/src/tests/mod.rs | 8 +- .../epoch-manager/src/validator_selection.rs | 137 ++++++--------- core/chain-configs/src/genesis_config.rs | 49 ++---- core/chain-configs/src/test_genesis.rs | 18 +- core/chain-configs/src/test_utils.rs | 22 +-- core/parameters/src/config_store.rs | 2 +- .../res/epoch_configs/mainnet/100.json | 24 ++- .../res/epoch_configs/mainnet/101.json | 24 ++- .../res/epoch_configs/mainnet/143.json | 22 ++- .../res/epoch_configs/mainnet/29.json | 88 +++++----- .../res/epoch_configs/mainnet/48.json | 24 ++- .../res/epoch_configs/mainnet/56.json | 24 ++- .../res/epoch_configs/mainnet/64.json | 24 ++- .../res/epoch_configs/mainnet/65.json | 24 ++- .../res/epoch_configs/mainnet/69.json | 24 ++- .../res/epoch_configs/mainnet/70.json | 158 +++++++++-------- .../res/epoch_configs/mainnet/71.json | 24 ++- .../res/epoch_configs/mainnet/72.json | 24 ++- .../res/epoch_configs/testnet/100.json | 24 ++- .../res/epoch_configs/testnet/101.json | 24 ++- .../res/epoch_configs/testnet/143.json | 22 ++- .../res/epoch_configs/testnet/29.json | 88 +++++----- .../res/epoch_configs/testnet/48.json | 24 ++- .../res/epoch_configs/testnet/56.json | 24 ++- .../res/epoch_configs/testnet/64.json | 24 ++- .../res/epoch_configs/testnet/65.json | 24 ++- .../res/epoch_configs/testnet/69.json | 24 ++- .../res/epoch_configs/testnet/70.json | 158 +++++++++-------- .../res/epoch_configs/testnet/71.json | 24 ++- .../res/epoch_configs/testnet/72.json | 24 ++- core/primitives/src/epoch_manager.rs | 165 ++++++++++++++---- core/primitives/src/shard_layout.rs | 21 +-- .../test_loop/tests/fix_min_stake_ratio.rs | 5 +- .../src/test_loop/tests/protocol_upgrade.rs | 6 +- .../src/test_loop/tests/resharding_v3.rs | 2 +- tools/fork-network/src/cli.rs | 24 +-- tools/state-viewer/src/commands.rs | 2 +- 41 files changed, 715 insertions(+), 767 deletions(-) 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/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/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/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/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 f0697d22bb5..b787c501725 100644 --- a/integration-tests/src/test_loop/tests/resharding_v3.rs +++ b/integration-tests/src/test_loop/tests/resharding_v3.rs @@ -1133,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; diff --git a/tools/fork-network/src/cli.rs b/tools/fork-network/src/cli.rs index 483c16578ca..8c7fad876fc 100644 --- a/tools/fork-network/src/cli.rs +++ b/tools/fork-network/src/cli.rs @@ -556,8 +556,8 @@ impl ForkNetworkCommand { 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.validator_selection_config.num_chunk_producer_seats = *num_seats; - config.validator_selection_config.num_chunk_validator_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)); } @@ -919,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, @@ -943,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; } } From 3a33b7a41ebac43bc36fd3155cb793c82bdf519a Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 11 Dec 2024 10:26:59 +0100 Subject: [PATCH 08/11] feat(resharding): add metrics for flat storage resharding (#12571) Adding two new metrics to monitor resharding: - `near_flat_storage_resharding_status` - `near_flat_storage_resharding_split_shard_processed_batches` Reusing the existing metric `near_flat_storage_head_height` to monitor shard catchup. Part of #12174 --- chain/chain/src/flat_storage_resharder.rs | 83 ++++++++++++----- core/store/src/flat/metrics.rs | 103 ++++++++++++++++++++++ core/store/src/flat/mod.rs | 3 + core/store/src/flat/types.rs | 4 +- core/store/src/metrics.rs | 39 ++++++++ 5 files changed, 206 insertions(+), 26 deletions(-) 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/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(|| { From ff605afab5e94c8b4ecea5635ba94ed5a32e340d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:56:10 +0100 Subject: [PATCH 09/11] chore(deps): bump nanoid from 3.3.4 to 3.3.8 in /tools/debug-ui (#12595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.4 to 3.3.8.
Changelog

Sourced from nanoid's changelog.

3.3.8

  • Fixed a way to break Nano ID by passing non-integer size (by @​myndzi).

3.3.7

  • Fixed node16 TypeScript support (by Saadi Myftija).

3.3.6

  • Fixed package.

3.3.5

  • Backport funding information.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nanoid&package-manager=npm_and_yarn&previous-version=3.3.4&new-version=3.3.8)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/near/nearcore/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/debug-ui/package-lock.json | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) 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", From bf0db79b3ea157cfe64f6094675100036753c978 Mon Sep 17 00:00:00 2001 From: Aleksandr Logunov Date: Wed, 11 Dec 2024 20:44:31 +0700 Subject: [PATCH 10/11] test(resharding): chunk dropper fix (#12586) Since `SimpleNightshadeV4` stopped being the latest nightly feature, the dropper stopped dropping chunks **after** protocol upgrade, because the protocol version became too high. --- integration-tests/src/test_loop/builder.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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) From c2d7028e739680e7f259a12bcaffa23658f5e9f8 Mon Sep 17 00:00:00 2001 From: Razvan Barbascu Date: Wed, 11 Dec 2024 13:58:50 +0000 Subject: [PATCH 11/11] feat(optimistic block): use block height for receipt id (#12599) To support optimistic blocks, we need to decouple receipt IDs from the block hash, as optimistic blocks will not include a block hash. This PR introduces a new protocol feature that enables the use of block height in computing receipt IDs. --- chain/chain/src/tests/simple_chain.rs | 4 +- core/primitives-core/src/version.rs | 5 +- core/primitives/src/utils.rs | 115 +++++++++++++++++++++--- runtime/runtime/src/actions.rs | 1 + runtime/runtime/src/ext.rs | 6 +- runtime/runtime/src/lib.rs | 5 ++ runtime/runtime/src/state_viewer/mod.rs | 1 + runtime/runtime/src/tests/apply.rs | 15 +++- 8 files changed, 131 insertions(+), 21 deletions(-) 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/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/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/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 66c9b95290d..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, )) } @@ -2388,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",