From b4d6dc107e9087ffe50deb39a919acad02d0d25e Mon Sep 17 00:00:00 2001 From: Arun Koshy <97870774+arun-koshy@users.noreply.github.com> Date: Wed, 4 Dec 2024 19:22:02 -0800 Subject: [PATCH] [consensus] Get highest accepted rounds from RoundProber and use in ancestor selection (#20336) ## Description For ancestor selection we want to ensure the highest quality ancestors are included in the proposal, therefore if an authority is excluded we should start including them once they have caught up to the network with their accepted blocks not just received as its possible for blocks to be received but suspended on many hosts which can cause performance degradation Additionally switched to using high quorum round of the high quorum round of accepted rounds for inclusion metric. Also bumped protocol version to 70 to include the new feature. ## Test plan How did you test the new or updated feature? Injected Latency Test - https://metrics.sui.io/goto/Jowh4F7Hg?orgId=1 Hoarded Block Test - Pending --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [X] Protocol: v70 Enable probing for accepted rounds in round prober in consensus. - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- consensus/config/src/committee.rs | 5 - consensus/core/src/ancestor.rs | 331 +++++++++++++--- consensus/core/src/authority_service.rs | 27 +- consensus/core/src/broadcaster.rs | 2 +- consensus/core/src/commit_syncer.rs | 2 +- consensus/core/src/core.rs | 30 +- consensus/core/src/core_thread.rs | 61 ++- consensus/core/src/leader_timeout.rs | 3 +- consensus/core/src/metrics.rs | 50 ++- consensus/core/src/network/anemo_network.rs | 15 +- consensus/core/src/network/mod.rs | 11 +- consensus/core/src/network/test_network.rs | 5 +- consensus/core/src/network/tonic_network.rs | 15 +- consensus/core/src/round_prober.rs | 255 +++++++++---- consensus/core/src/subscriber.rs | 2 +- consensus/core/src/synchronizer.rs | 2 +- crates/sui-open-rpc/spec/openrpc.json | 3 +- crates/sui-protocol-config/src/lib.rs | 24 +- ...ocol_config__test__Mainnet_version_70.snap | 340 +++++++++++++++++ ...ocol_config__test__Testnet_version_70.snap | 341 +++++++++++++++++ ...sui_protocol_config__test__version_70.snap | 353 ++++++++++++++++++ ...ests__genesis_config_snapshot_matches.snap | 2 +- ..._populated_genesis_snapshot_matches-2.snap | 30 +- 23 files changed, 1688 insertions(+), 221 deletions(-) create mode 100644 crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_70.snap create mode 100644 crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_70.snap create mode 100644 crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_70.snap diff --git a/consensus/config/src/committee.rs b/consensus/config/src/committee.rs index 2dfb5ff66bb87..0d941a3aa39fd 100644 --- a/consensus/config/src/committee.rs +++ b/consensus/config/src/committee.rs @@ -68,11 +68,6 @@ impl Committee { self.total_stake } - pub fn n_percent_stake_threshold(&self, n: u64) -> Stake { - assert!(n <= 100, "n must be between 0 and 100"); - self.total_stake * n / 100 - } - pub fn quorum_threshold(&self) -> Stake { self.quorum_threshold } diff --git a/consensus/core/src/ancestor.rs b/consensus/core/src/ancestor.rs index 6ad49ac190e39..1b3c8b0b03a1e 100644 --- a/consensus/core/src/ancestor.rs +++ b/consensus/core/src/ancestor.rs @@ -52,7 +52,8 @@ pub(crate) struct AncestorStateManager { state_map: Vec, propagation_score_update_count: u32, quorum_round_update_count: u32, - pub(crate) quorum_round_per_authority: Vec, + pub(crate) received_quorum_round_per_authority: Vec, + pub(crate) accepted_quorum_round_per_authority: Vec, // This is the reputation scores that we use for leader election but we are // using it here as a signal for high quality block propagation as well. pub(crate) propagation_scores: ReputationScores, @@ -78,25 +79,29 @@ impl AncestorStateManager { // Exclusion threshold is based on propagation (reputation) scores const EXCLUSION_THRESHOLD_PERCENTAGE: u64 = 10; - // Inclusion threshold is based on network quorum round - const INCLUSION_THRESHOLD_PERCENTAGE: u64 = 90; - pub(crate) fn new(context: Arc) -> Self { let state_map = vec![AncestorInfo::new(); context.committee.size()]; - let quorum_round_per_authority = vec![(0, 0); context.committee.size()]; + let received_quorum_round_per_authority = vec![(0, 0); context.committee.size()]; + let accepted_quorum_round_per_authority = vec![(0, 0); context.committee.size()]; Self { context, state_map, propagation_score_update_count: 0, quorum_round_update_count: 0, propagation_scores: ReputationScores::default(), - quorum_round_per_authority, + received_quorum_round_per_authority, + accepted_quorum_round_per_authority, } } - pub(crate) fn set_quorum_round_per_authority(&mut self, quorum_rounds: Vec) { - self.quorum_round_per_authority = quorum_rounds; + pub(crate) fn set_quorum_rounds_per_authority( + &mut self, + received_quorum_rounds: Vec, + accepted_quorum_rounds: Vec, + ) { + self.received_quorum_round_per_authority = received_quorum_rounds; + self.accepted_quorum_round_per_authority = accepted_quorum_rounds; self.quorum_round_update_count += 1; } @@ -129,21 +134,29 @@ impl AncestorStateManager { .collect::>(); // If round prober has not run yet and we don't have network quorum round, - // it is okay because network_low_quorum_round will be zero and we will + // it is okay because network_high_quorum_round will be zero and we will // include all ancestors until we get more information. - let network_low_quorum_round = self.calculate_network_low_quorum_round(); + let network_high_quorum_round = self.calculate_network_high_quorum_round(); // If propagation scores are not ready because the first 300 commits have not // happened, this is okay as we will only start excluding ancestors after that // point in time. for (authority_id, score) in propagation_scores_by_authority { - let (authority_low_quorum_round, _high) = self.quorum_round_per_authority[authority_id]; + let (_low, authority_high_quorum_round) = if self + .context + .protocol_config + .consensus_round_prober_probe_accepted_rounds() + { + self.accepted_quorum_round_per_authority[authority_id] + } else { + self.received_quorum_round_per_authority[authority_id] + }; self.update_state( authority_id, score, - authority_low_quorum_round, - network_low_quorum_round, + authority_high_quorum_round, + network_high_quorum_round, ); } } @@ -153,8 +166,8 @@ impl AncestorStateManager { &mut self, authority_id: AuthorityIndex, propagation_score: u64, - authority_low_quorum_round: u32, - network_low_quorum_round: u32, + authority_high_quorum_round: u32, + network_high_quorum_round: u32, ) { let block_hostname = &self.context.committee.authority(authority_id).hostname; let mut ancestor_info = self.state_map[authority_id].clone(); @@ -172,6 +185,7 @@ impl AncestorStateManager { match ancestor_info.state { // Check conditions to switch to EXCLUDE state + // TODO: Consider using received round gaps for exclusion. AncestorState::Include => { if propagation_score <= low_score_threshold { ancestor_info.state = AncestorState::Exclude(propagation_score); @@ -195,14 +209,14 @@ impl AncestorStateManager { // It should not be possible for the scores to get over the threshold // until the node is back in the INCLUDE state, but adding just in case. if propagation_score > low_score_threshold - || authority_low_quorum_round >= network_low_quorum_round + || authority_high_quorum_round >= network_high_quorum_round { ancestor_info.state = AncestorState::Include; ancestor_info.set_lock( self.propagation_score_update_count + Self::STATE_LOCK_SCORE_UPDATES, ); info!( - "Authority {authority_id} moved to INCLUDE state with {propagation_score} > threshold of {low_score_threshold} or {authority_low_quorum_round} >= {network_low_quorum_round} and locked for {:?} score updates.", + "Authority {authority_id} moved to INCLUDE state with {propagation_score} > threshold of {low_score_threshold} or {authority_high_quorum_round} >= {network_high_quorum_round} and locked for {:?} score updates.", Self::STATE_LOCK_SCORE_UPDATES ); self.context @@ -219,39 +233,71 @@ impl AncestorStateManager { self.state_map[authority_id] = ancestor_info; } - /// Calculate the network's quorum round from authorities by inclusion stake - /// threshold, where quorum round is the highest round a block has been seen - /// by a percentage (inclusion threshold) of authorities. - /// TODO: experiment with using high quorum round - fn calculate_network_low_quorum_round(&self) -> u32 { + /// Calculate the network's quorum round based on what information is available + /// via RoundProber. + /// When consensus_round_prober_probe_accepted_rounds is true, uses accepted rounds. + /// Otherwise falls back to received rounds. + fn calculate_network_high_quorum_round(&self) -> u32 { + if self + .context + .protocol_config + .consensus_round_prober_probe_accepted_rounds() + { + self.calculate_network_high_accepted_quorum_round() + } else { + self.calculate_network_high_received_quorum_round() + } + } + + fn calculate_network_high_accepted_quorum_round(&self) -> u32 { + let committee = &self.context.committee; + + let high_quorum_rounds_with_stake = self + .accepted_quorum_round_per_authority + .iter() + .zip(committee.authorities()) + .map(|((_low, high), (_, authority))| (*high, authority.stake)) + .collect::>(); + + self.calculate_network_high_quorum_round_internal(high_quorum_rounds_with_stake) + } + + fn calculate_network_high_received_quorum_round(&self) -> u32 { let committee = &self.context.committee; - let inclusion_stake_threshold = self.get_inclusion_stake_threshold(); - let mut low_quorum_rounds_with_stake = self - .quorum_round_per_authority + + let high_quorum_rounds_with_stake = self + .received_quorum_round_per_authority .iter() .zip(committee.authorities()) - .map(|((low, _high), (_, authority))| (*low, authority.stake)) + .map(|((_low, high), (_, authority))| (*high, authority.stake)) .collect::>(); - low_quorum_rounds_with_stake.sort(); + + self.calculate_network_high_quorum_round_internal(high_quorum_rounds_with_stake) + } + + /// Calculate the network's high quorum round. + /// The authority high quorum round is the lowest round higher or equal to rounds + /// from a quorum of authorities. The network high quorum round is using the high + /// quorum round of each authority as reported by the [`RoundProber`] and then + /// finding the high quroum round of those high quorum rounds. + fn calculate_network_high_quorum_round_internal( + &self, + mut high_quorum_rounds_with_stake: Vec<(u32, u64)>, + ) -> u32 { + high_quorum_rounds_with_stake.sort(); let mut total_stake = 0; - let mut network_low_quorum_round = 0; + let mut network_high_quorum_round = 0; - for (round, stake) in low_quorum_rounds_with_stake.iter().rev() { + for (round, stake) in high_quorum_rounds_with_stake.iter() { total_stake += stake; - if total_stake >= inclusion_stake_threshold { - network_low_quorum_round = *round; + if total_stake >= self.context.committee.quorum_threshold() { + network_high_quorum_round = *round; break; } } - network_low_quorum_round - } - - fn get_inclusion_stake_threshold(&self) -> u64 { - self.context - .committee - .n_percent_stake_threshold(Self::INCLUSION_THRESHOLD_PERCENTAGE) + network_high_quorum_round } } @@ -261,43 +307,206 @@ mod test { use crate::leader_scoring::ReputationScores; #[tokio::test] - async fn test_calculate_network_low_quorum_round() { + async fn test_calculate_network_high_received_quorum_round() { + telemetry_subscribers::init_for_testing(); + + let (mut context, _key_pairs) = Context::new_for_test(4); + context + .protocol_config + .set_consensus_round_prober_probe_accepted_rounds(false); + let context = Arc::new(context); + + let scores = ReputationScores::new((1..=300).into(), vec![1, 2, 4, 3]); + let mut ancestor_state_manager = AncestorStateManager::new(context.clone()); + ancestor_state_manager.set_propagation_scores(scores); + + // Quorum rounds are not set yet, so we should calculate a network + // quorum round of 0 to start. + let network_high_quorum_round = + ancestor_state_manager.calculate_network_high_quorum_round(); + assert_eq!(network_high_quorum_round, 0); + + let received_quorum_rounds = vec![(100, 229), (225, 229), (229, 300), (229, 300)]; + let accepted_quorum_rounds = vec![(50, 229), (175, 229), (179, 229), (179, 300)]; + ancestor_state_manager.set_quorum_rounds_per_authority( + received_quorum_rounds.clone(), + accepted_quorum_rounds.clone(), + ); + + // When probe_accepted_rounds is false, should use received rounds + let network_high_quorum_round = + ancestor_state_manager.calculate_network_high_quorum_round(); + assert_eq!(network_high_quorum_round, 300); + } + + #[tokio::test] + async fn test_calculate_network_high_accepted_quorum_round() { + telemetry_subscribers::init_for_testing(); + + let (mut context, _key_pairs) = Context::new_for_test(4); + context + .protocol_config + .set_consensus_round_prober_probe_accepted_rounds(true); + let context = Arc::new(context); + + let scores = ReputationScores::new((1..=300).into(), vec![1, 2, 4, 3]); + let mut ancestor_state_manager = AncestorStateManager::new(context.clone()); + ancestor_state_manager.set_propagation_scores(scores); + + // Quorum rounds are not set yet, so we should calculate a network + // quorum round of 0 to start. + let network_high_quorum_round = + ancestor_state_manager.calculate_network_high_quorum_round(); + assert_eq!(network_high_quorum_round, 0); + + let received_quorum_rounds = vec![(100, 229), (225, 300), (229, 300), (229, 300)]; + let accepted_quorum_rounds = vec![(50, 229), (175, 229), (179, 229), (179, 300)]; + ancestor_state_manager.set_quorum_rounds_per_authority( + received_quorum_rounds.clone(), + accepted_quorum_rounds.clone(), + ); + + // When probe_accepted_rounds is true, should use accepted rounds + let network_high_quorum_round = + ancestor_state_manager.calculate_network_high_quorum_round(); + assert_eq!(network_high_quorum_round, 229); + } + + // Test all state transitions with probe_accepted_rounds = true + // Default all INCLUDE -> EXCLUDE + // EXCLUDE -> INCLUDE (Blocked due to lock) + // EXCLUDE -> INCLUDE (Pass due to lock expired) + // INCLUDE -> EXCLUDE (Blocked due to lock) + // INCLUDE -> EXCLUDE (Pass due to lock expired) + #[tokio::test] + async fn test_update_all_ancestor_state_using_accepted_rounds() { telemetry_subscribers::init_for_testing(); - let context = Arc::new(Context::new_for_test(4).0); + let (mut context, _key_pairs) = Context::new_for_test(4); + context + .protocol_config + .set_consensus_round_prober_probe_accepted_rounds(true); + let context = Arc::new(context); let scores = ReputationScores::new((1..=300).into(), vec![1, 2, 4, 3]); let mut ancestor_state_manager = AncestorStateManager::new(context); ancestor_state_manager.set_propagation_scores(scores); - // Quorum rounds are not set yet, so we should calculate a network quorum - // round of 0 to start. - let network_low_quorum_round = ancestor_state_manager.calculate_network_low_quorum_round(); - assert_eq!(network_low_quorum_round, 0); + let received_quorum_rounds = vec![(300, 400), (300, 400), (300, 400), (300, 400)]; + let accepted_quorum_rounds = vec![(225, 229), (225, 229), (229, 300), (229, 300)]; + ancestor_state_manager + .set_quorum_rounds_per_authority(received_quorum_rounds, accepted_quorum_rounds); + ancestor_state_manager.update_all_ancestors_state(); + + // Score threshold for exclude is (4 * 10) / 100 = 0 + // No ancestors should be excluded in with this threshold + let state_map = ancestor_state_manager.get_ancestor_states(); + for state in state_map.iter() { + assert_eq!(*state, AncestorState::Include); + } + + let scores = ReputationScores::new((1..=300).into(), vec![10, 10, 100, 100]); + ancestor_state_manager.set_propagation_scores(scores); + ancestor_state_manager.update_all_ancestors_state(); + + // Score threshold for exclude is (100 * 10) / 100 = 10 + // 2 authorities should be excluded in with this threshold + let state_map = ancestor_state_manager.get_ancestor_states(); + for (authority, state) in state_map.iter().enumerate() { + if (0..=1).contains(&authority) { + assert_eq!(*state, AncestorState::Exclude(10)); + } else { + assert_eq!(*state, AncestorState::Include); + } + } + + ancestor_state_manager.update_all_ancestors_state(); + + // 2 authorities should still be excluded with these scores and no new + // quorum round updates have been set to expire the locks. + let state_map = ancestor_state_manager.get_ancestor_states(); + for (authority, state) in state_map.iter().enumerate() { + if (0..=1).contains(&authority) { + assert_eq!(*state, AncestorState::Exclude(10)); + } else { + assert_eq!(*state, AncestorState::Include); + } + } + + // Updating the quorum rounds will expire the lock as we only need 1 + // quorum round update for tests. + let received_quorum_rounds = vec![(400, 500), (400, 500), (400, 500), (400, 500)]; + let accepted_quorum_rounds = vec![(229, 300), (225, 229), (229, 300), (229, 300)]; + ancestor_state_manager + .set_quorum_rounds_per_authority(received_quorum_rounds, accepted_quorum_rounds); + ancestor_state_manager.update_all_ancestors_state(); + + // Authority 0 should now be included again because high quorum round is + // at the network high quorum round of 300. Authority 1's quorum round is + // too low and will remain excluded. + let state_map = ancestor_state_manager.get_ancestor_states(); + for (authority, state) in state_map.iter().enumerate() { + if authority == 1 { + assert_eq!(*state, AncestorState::Exclude(10)); + } else { + assert_eq!(*state, AncestorState::Include); + } + } + + let received_quorum_rounds = vec![(500, 600), (500, 600), (500, 600), (500, 600)]; + let accepted_quorum_rounds = vec![(229, 300), (229, 300), (229, 300), (229, 300)]; + ancestor_state_manager + .set_quorum_rounds_per_authority(received_quorum_rounds, accepted_quorum_rounds); + ancestor_state_manager.update_all_ancestors_state(); + + // Ancestor 1 can transtion to the INCLUDE state. Ancestor 0 is still locked + // in the INCLUDE state until a score update is performed which is why + // even though the scores are still low it has not moved to the EXCLUDE + // state. + let state_map = ancestor_state_manager.get_ancestor_states(); + for state in state_map.iter() { + assert_eq!(*state, AncestorState::Include); + } - let quorum_rounds = vec![(100, 229), (225, 300), (229, 300), (229, 300)]; - ancestor_state_manager.set_quorum_round_per_authority(quorum_rounds); + // Updating the scores will expire the lock as we only need 1 update for tests. + let scores = ReputationScores::new((1..=300).into(), vec![100, 10, 100, 100]); + ancestor_state_manager.set_propagation_scores(scores); + ancestor_state_manager.update_all_ancestors_state(); - let network_low_quorum_round = ancestor_state_manager.calculate_network_low_quorum_round(); - assert_eq!(network_low_quorum_round, 225); + // Ancestor 1 can transition to EXCLUDE state now that the lock expired + // and its scores are below the threshold. + let state_map = ancestor_state_manager.get_ancestor_states(); + for (authority, state) in state_map.iter().enumerate() { + if authority == 1 { + assert_eq!(*state, AncestorState::Exclude(10)); + } else { + assert_eq!(*state, AncestorState::Include); + } + } } - // Test all state transitions + // Test all state transitions with probe_accepted_rounds = false // Default all INCLUDE -> EXCLUDE // EXCLUDE -> INCLUDE (Blocked due to lock) // EXCLUDE -> INCLUDE (Pass due to lock expired) // INCLUDE -> EXCLUDE (Blocked due to lock) // INCLUDE -> EXCLUDE (Pass due to lock expired) #[tokio::test] - async fn test_update_all_ancestor_state() { + async fn test_update_all_ancestor_state_using_received_rounds() { telemetry_subscribers::init_for_testing(); - let context = Arc::new(Context::new_for_test(4).0); + let (mut context, _key_pairs) = Context::new_for_test(4); + context + .protocol_config + .set_consensus_round_prober_probe_accepted_rounds(false); + let context = Arc::new(context); let scores = ReputationScores::new((1..=300).into(), vec![1, 2, 4, 3]); let mut ancestor_state_manager = AncestorStateManager::new(context); ancestor_state_manager.set_propagation_scores(scores); - let quorum_rounds = vec![(225, 229), (225, 300), (229, 300), (229, 300)]; - ancestor_state_manager.set_quorum_round_per_authority(quorum_rounds); + let received_quorum_rounds = vec![(225, 229), (225, 300), (229, 300), (229, 300)]; + let accepted_quorum_rounds = vec![(100, 150), (100, 150), (100, 150), (100, 150)]; + ancestor_state_manager + .set_quorum_rounds_per_authority(received_quorum_rounds, accepted_quorum_rounds); ancestor_state_manager.update_all_ancestors_state(); // Score threshold for exclude is (4 * 10) / 100 = 0 @@ -337,12 +546,14 @@ mod test { // Updating the quorum rounds will expire the lock as we only need 1 // quorum round update for tests. - let quorum_rounds = vec![(229, 300), (225, 300), (229, 300), (229, 300)]; - ancestor_state_manager.set_quorum_round_per_authority(quorum_rounds); + let received_quorum_rounds = vec![(229, 300), (225, 229), (229, 300), (229, 300)]; + let accepted_quorum_rounds = vec![(100, 150), (100, 150), (100, 150), (100, 150)]; + ancestor_state_manager + .set_quorum_rounds_per_authority(received_quorum_rounds, accepted_quorum_rounds); ancestor_state_manager.update_all_ancestors_state(); - // Authority 0 should now be included again because low quorum round is - // at the network low quorum round of 229. Authority 1's quorum round is + // Authority 0 should now be included again because high quorum round is + // at the network high quorum round of 300. Authority 1's quorum round is // too low and will remain excluded. let state_map = ancestor_state_manager.get_ancestor_states(); for (authority, state) in state_map.iter().enumerate() { @@ -353,8 +564,10 @@ mod test { } } - let quorum_rounds = vec![(229, 300), (229, 300), (229, 300), (229, 300)]; - ancestor_state_manager.set_quorum_round_per_authority(quorum_rounds); + let received_quorum_rounds = vec![(229, 300), (229, 300), (229, 300), (229, 300)]; + let accepted_quorum_rounds = vec![(100, 150), (100, 150), (100, 150), (100, 150)]; + ancestor_state_manager + .set_quorum_rounds_per_authority(received_quorum_rounds, accepted_quorum_rounds); ancestor_state_manager.update_all_ancestors_state(); // Ancestor 1 can transtion to the INCLUDE state. Ancestor 0 is still locked diff --git a/consensus/core/src/authority_service.rs b/consensus/core/src/authority_service.rs index 702bf657f7b6d..7ad8d37a04c2d 100644 --- a/consensus/core/src/authority_service.rs +++ b/consensus/core/src/authority_service.rs @@ -409,18 +409,28 @@ impl NetworkService for AuthorityService { Ok(result) } - async fn handle_get_latest_rounds(&self, _peer: AuthorityIndex) -> ConsensusResult> { + async fn handle_get_latest_rounds( + &self, + _peer: AuthorityIndex, + ) -> ConsensusResult<(Vec, Vec)> { fail_point_async!("consensus-rpc-response"); let mut highest_received_rounds = self.core_dispatcher.highest_received_rounds(); - // Own blocks do not go through the core dispatcher, so they need to be set separately. - highest_received_rounds[self.context.own_index] = self + + let blocks = self .dag_state .read() - .get_last_block_for_authority(self.context.own_index) - .round(); + .get_last_cached_block_per_authority(Round::MAX); + let highest_accepted_rounds = blocks + .into_iter() + .map(|block| block.round()) + .collect::>(); + + // Own blocks do not go through the core dispatcher, so they need to be set separately. + highest_received_rounds[self.context.own_index] = + highest_accepted_rounds[self.context.own_index]; - Ok(highest_received_rounds) + Ok((highest_received_rounds, highest_accepted_rounds)) } } @@ -669,7 +679,8 @@ mod tests { fn set_propagation_delay_and_quorum_rounds( &self, _delay: Round, - _quorum_rounds: Vec, + _received_quorum_rounds: Vec, + _accepted_quorum_rounds: Vec, ) -> Result<(), CoreError> { todo!() } @@ -740,7 +751,7 @@ mod tests { &self, _peer: AuthorityIndex, _timeout: Duration, - ) -> ConsensusResult> { + ) -> ConsensusResult<(Vec, Vec)> { unimplemented!("Unimplemented") } } diff --git a/consensus/core/src/broadcaster.rs b/consensus/core/src/broadcaster.rs index d753bf39932c1..ee3b9b0e1d77c 100644 --- a/consensus/core/src/broadcaster.rs +++ b/consensus/core/src/broadcaster.rs @@ -279,7 +279,7 @@ mod test { &self, _peer: AuthorityIndex, _timeout: Duration, - ) -> ConsensusResult> { + ) -> ConsensusResult<(Vec, Vec)> { unimplemented!("Unimplemented") } } diff --git a/consensus/core/src/commit_syncer.rs b/consensus/core/src/commit_syncer.rs index 44a38d595d148..281f1016424cc 100644 --- a/consensus/core/src/commit_syncer.rs +++ b/consensus/core/src/commit_syncer.rs @@ -809,7 +809,7 @@ mod tests { &self, _peer: AuthorityIndex, _timeout: Duration, - ) -> ConsensusResult> { + ) -> ConsensusResult<(Vec, Vec)> { unimplemented!("Unimplemented") } } diff --git a/consensus/core/src/core.rs b/consensus/core/src/core.rs index b327a53955862..b798d367339f9 100644 --- a/consensus/core/src/core.rs +++ b/consensus/core/src/core.rs @@ -246,7 +246,9 @@ impl Core { } /// Processes the provided blocks and accepts them if possible when their causal history exists. - /// The method returns the references of parents that are unknown and need to be fetched. + /// The method returns: + /// - The references of accepted blocks + /// - The references of ancestors missing their block pub(crate) fn add_blocks( &mut self, blocks: Vec, @@ -266,7 +268,7 @@ impl Core { .observe(blocks.len() as f64); // Try to accept them via the block manager - let (accepted_blocks, missing_blocks) = self.block_manager.try_accept_blocks(blocks); + let (accepted_blocks, missing_block_refs) = self.block_manager.try_accept_blocks(blocks); if !accepted_blocks.is_empty() { debug!( @@ -284,13 +286,13 @@ impl Core { // Try to propose now since there are new blocks accepted. self.try_propose(false)?; - } + }; - if !missing_blocks.is_empty() { - debug!("Missing blocks: {:?}", missing_blocks); + if !missing_block_refs.is_empty() { + debug!("Missing block refs: {:?}", missing_block_refs); } - Ok(missing_blocks) + Ok(missing_block_refs) } /// Adds/processed all the newly `accepted_blocks`. We basically try to move the threshold clock and add them to the @@ -691,16 +693,18 @@ impl Core { self.subscriber_exists = exists; } - /// Sets the delay by round for propagating blocks to a quorum and the - /// quorum round per authority for ancestor state manager. + /// Sets the delay by round for propagating blocks to a quorum and the received + /// & accepted quorum rounds per authority for ancestor state manager. pub(crate) fn set_propagation_delay_and_quorum_rounds( &mut self, delay: Round, - quorum_rounds: Vec, + received_quorum_rounds: Vec, + accepted_quorum_rounds: Vec, ) { - info!("Quorum round per authority in ancestor state manager set to: {quorum_rounds:?}"); + info!("Received quorum round per authority in ancestor state manager set to: {received_quorum_rounds:?}"); + info!("Accepted quorum round per authority in ancestor state manager set to: {accepted_quorum_rounds:?}"); self.ancestor_state_manager - .set_quorum_round_per_authority(quorum_rounds); + .set_quorum_rounds_per_authority(received_quorum_rounds, accepted_quorum_rounds); info!("Propagation round delay set to: {delay}"); self.propagation_delay = delay; } @@ -2332,7 +2336,7 @@ mod test { ); // Use a large propagation delay to disable proposing. - core.set_propagation_delay_and_quorum_rounds(1000, vec![]); + core.set_propagation_delay_and_quorum_rounds(1000, vec![], vec![]); // Make propagation delay the only reason for not proposing. core.set_subscriber_exists(true); @@ -2341,7 +2345,7 @@ mod test { assert!(core.try_propose(true).unwrap().is_none()); // Let Core know there is no propagation delay. - core.set_propagation_delay_and_quorum_rounds(0, vec![]); + core.set_propagation_delay_and_quorum_rounds(0, vec![], vec![]); // Proposing now would succeed. assert!(core.try_propose(true).unwrap().is_some()); diff --git a/consensus/core/src/core_thread.rs b/consensus/core/src/core_thread.rs index 5b2a83748672e..d1cbc2ba22160 100644 --- a/consensus/core/src/core_thread.rs +++ b/consensus/core/src/core_thread.rs @@ -67,11 +67,13 @@ pub trait CoreThreadDispatcher: Sync + Send + 'static { fn set_subscriber_exists(&self, exists: bool) -> Result<(), CoreError>; /// Sets the estimated delay to propagate a block to a quorum of peers, in - /// number of rounds, and the quorum rounds for all authorities. + /// number of rounds, and the received & accepted quorum rounds for all + /// authorities. fn set_propagation_delay_and_quorum_rounds( &self, delay: Round, - quorum_rounds: Vec, + received_quorum_rounds: Vec, + accepted_quorum_rounds: Vec, ) -> Result<(), CoreError>; fn set_last_known_proposed_round(&self, round: Round) -> Result<(), CoreError>; @@ -97,7 +99,7 @@ struct CoreThread { core: Core, receiver: Receiver, rx_subscriber_exists: watch::Receiver, - rx_propagation_delay_and_quorum_rounds: watch::Receiver<(Round, Vec)>, + rx_propagation_delay_and_quorum_rounds: watch::Receiver, rx_last_known_proposed_round: watch::Receiver, context: Arc, } @@ -116,8 +118,8 @@ impl CoreThread { match command { CoreThreadCommand::AddBlocks(blocks, sender) => { let _scope = monitored_scope("CoreThread::loop::add_blocks"); - let missing_blocks = self.core.add_blocks(blocks)?; - sender.send(missing_blocks).ok(); + let missing_block_refs = self.core.add_blocks(blocks)?; + sender.send(missing_block_refs).ok(); } CoreThreadCommand::NewBlock(round, sender, force) => { let _scope = monitored_scope("CoreThread::loop::new_block"); @@ -150,8 +152,12 @@ impl CoreThread { _ = self.rx_propagation_delay_and_quorum_rounds.changed() => { let _scope = monitored_scope("CoreThread::loop::set_propagation_delay_and_quorum_rounds"); let should_propose_before = self.core.should_propose(); - let (delay, quorum_rounds) = self.rx_propagation_delay_and_quorum_rounds.borrow().clone(); - self.core.set_propagation_delay_and_quorum_rounds(delay, quorum_rounds); + let state = self.rx_propagation_delay_and_quorum_rounds.borrow().clone(); + self.core.set_propagation_delay_and_quorum_rounds( + state.delay, + state.received_quorum_rounds, + state.accepted_quorum_rounds + ); if !should_propose_before && self.core.should_propose() { // If core cannnot propose before but can propose now, try to produce a new block to ensure liveness, // because block proposal could have been skipped. @@ -170,7 +176,7 @@ pub(crate) struct ChannelCoreThreadDispatcher { context: Arc, sender: WeakSender, tx_subscriber_exists: Arc>, - tx_propagation_delay_and_quorum_rounds: Arc)>>, + tx_propagation_delay_and_quorum_rounds: Arc>, tx_last_known_proposed_round: Arc>, highest_received_rounds: Arc>, } @@ -181,23 +187,29 @@ impl ChannelCoreThreadDispatcher { dag_state: &RwLock, core: Core, ) -> (Self, CoreThreadHandle) { - // Initialize highest received rounds to last accepted rounds. + // Initialize highest received rounds. let highest_received_rounds = { let dag_state = dag_state.read(); - context + let highest_received_rounds = context .committee .authorities() .map(|(index, _)| { AtomicU32::new(dag_state.get_last_block_for_authority(index).round()) }) - .collect() + .collect(); + + highest_received_rounds }; let (sender, receiver) = channel("consensus_core_commands", CORE_THREAD_COMMANDS_CHANNEL_SIZE); let (tx_subscriber_exists, mut rx_subscriber_exists) = watch::channel(false); let (tx_propagation_delay_and_quorum_rounds, mut rx_propagation_delay_and_quorum_rounds) = - watch::channel((0, vec![(0, 0); context.committee.size()])); + watch::channel(PropagationDelayAndQuorumRounds { + delay: 0, + received_quorum_rounds: vec![(0, 0); context.committee.size()], + accepted_quorum_rounds: vec![(0, 0); context.committee.size()], + }); let (tx_last_known_proposed_round, mut rx_last_known_proposed_round) = watch::channel(0); rx_subscriber_exists.mark_unchanged(); rx_propagation_delay_and_quorum_rounds.mark_unchanged(); @@ -264,9 +276,11 @@ impl CoreThreadDispatcher for ChannelCoreThreadDispatcher { self.highest_received_rounds[block.author()].fetch_max(block.round(), Ordering::AcqRel); } let (sender, receiver) = oneshot::channel(); - self.send(CoreThreadCommand::AddBlocks(blocks, sender)) + self.send(CoreThreadCommand::AddBlocks(blocks.clone(), sender)) .await; - receiver.await.map_err(|e| Shutdown(e.to_string())) + let missing_block_refs = receiver.await.map_err(|e| Shutdown(e.to_string()))?; + + Ok(missing_block_refs) } async fn new_block(&self, round: Round, force: bool) -> Result<(), CoreError> { @@ -291,10 +305,15 @@ impl CoreThreadDispatcher for ChannelCoreThreadDispatcher { fn set_propagation_delay_and_quorum_rounds( &self, delay: Round, - quorum_rounds: Vec, + received_quorum_rounds: Vec, + accepted_quorum_rounds: Vec, ) -> Result<(), CoreError> { self.tx_propagation_delay_and_quorum_rounds - .send((delay, quorum_rounds)) + .send(PropagationDelayAndQuorumRounds { + delay, + received_quorum_rounds, + accepted_quorum_rounds, + }) .map_err(|e| Shutdown(e.to_string())) } @@ -312,6 +331,13 @@ impl CoreThreadDispatcher for ChannelCoreThreadDispatcher { } } +#[derive(Clone)] +struct PropagationDelayAndQuorumRounds { + delay: Round, + received_quorum_rounds: Vec, + accepted_quorum_rounds: Vec, +} + // TODO: complete the Mock for thread dispatcher to be used from several tests #[cfg(test)] #[derive(Default)] @@ -372,7 +398,8 @@ impl CoreThreadDispatcher for MockCoreThreadDispatcher { fn set_propagation_delay_and_quorum_rounds( &self, _delay: Round, - _quorum_rounds: Vec, + _received_quorum_rounds: Vec, + _accepted_quorum_rounds: Vec, ) -> Result<(), CoreError> { todo!() } diff --git a/consensus/core/src/leader_timeout.rs b/consensus/core/src/leader_timeout.rs index 1e28852edc3e3..a8b55a0c648d9 100644 --- a/consensus/core/src/leader_timeout.rs +++ b/consensus/core/src/leader_timeout.rs @@ -175,7 +175,8 @@ mod tests { fn set_propagation_delay_and_quorum_rounds( &self, _delay: Round, - _quorum_rounds: Vec, + _received_quorum_rounds: Vec, + _accepted_quorum_rounds: Vec, ) -> Result<(), CoreError> { todo!() } diff --git a/consensus/core/src/metrics.rs b/consensus/core/src/metrics.rs index da29cfae33201..27920071e1763 100644 --- a/consensus/core/src/metrics.rs +++ b/consensus/core/src/metrics.rs @@ -179,12 +179,15 @@ pub(crate) struct NodeMetrics { pub(crate) commit_sync_fetch_loop_latency: Histogram, pub(crate) commit_sync_fetch_once_latency: Histogram, pub(crate) commit_sync_fetch_once_errors: IntCounterVec, - pub(crate) round_prober_quorum_round_gaps: IntGaugeVec, - pub(crate) round_prober_low_quorum_round: IntGaugeVec, - pub(crate) round_prober_current_round_gaps: IntGaugeVec, + pub(crate) round_prober_received_quorum_round_gaps: IntGaugeVec, + pub(crate) round_prober_accepted_quorum_round_gaps: IntGaugeVec, + pub(crate) round_prober_low_received_quorum_round: IntGaugeVec, + pub(crate) round_prober_low_accepted_quorum_round: IntGaugeVec, + pub(crate) round_prober_current_received_round_gaps: IntGaugeVec, + pub(crate) round_prober_current_accepted_round_gaps: IntGaugeVec, pub(crate) round_prober_propagation_delays: Histogram, pub(crate) round_prober_last_propagation_delay: IntGauge, - pub(crate) round_prober_request_errors: IntCounter, + pub(crate) round_prober_request_errors: IntCounterVec, pub(crate) uptime: Histogram, } @@ -654,21 +657,39 @@ impl NodeMetrics { &["authority", "error"], registry ).unwrap(), - round_prober_quorum_round_gaps: register_int_gauge_vec_with_registry!( - "round_prober_quorum_round_gaps", - "Round gaps among peers for blocks proposed from each authority", + round_prober_received_quorum_round_gaps: register_int_gauge_vec_with_registry!( + "round_prober_received_quorum_round_gaps", + "Received round gaps among peers for blocks proposed from each authority", &["authority"], registry ).unwrap(), - round_prober_low_quorum_round: register_int_gauge_vec_with_registry!( - "round_prober_low_quorum_round", + round_prober_accepted_quorum_round_gaps: register_int_gauge_vec_with_registry!( + "round_prober_accepted_quorum_round_gaps", + "Accepted round gaps among peers for blocks proposed & accepted from each authority", + &["authority"], + registry + ).unwrap(), + round_prober_low_received_quorum_round: register_int_gauge_vec_with_registry!( + "round_prober_low_received_quorum_round", "Low quorum round among peers for blocks proposed from each authority", &["authority"], registry ).unwrap(), - round_prober_current_round_gaps: register_int_gauge_vec_with_registry!( - "round_prober_current_round_gaps", - "Round gaps from local last proposed round to the low quorum round of each peer. Can be negative.", + round_prober_low_accepted_quorum_round: register_int_gauge_vec_with_registry!( + "round_prober_low_accepted_quorum_round", + "Low quorum round among peers for blocks proposed & accepted from each authority", + &["authority"], + registry + ).unwrap(), + round_prober_current_received_round_gaps: register_int_gauge_vec_with_registry!( + "round_prober_current_received_round_gaps", + "Received round gaps from local last proposed round to the low received quorum round of each peer. Can be negative.", + &["authority"], + registry + ).unwrap(), + round_prober_current_accepted_round_gaps: register_int_gauge_vec_with_registry!( + "round_prober_current_accepted_round_gaps", + "Accepted round gaps from local last proposed & accepted round to the low accepted quorum round of each peer. Can be negative.", &["authority"], registry ).unwrap(), @@ -683,9 +704,10 @@ impl NodeMetrics { "Most recent propagation delay observed by RoundProber", registry ).unwrap(), - round_prober_request_errors: register_int_counter_with_registry!( + round_prober_request_errors: register_int_counter_vec_with_registry!( "round_prober_request_errors", - "Number of timeouts when probing against peers", + "Number of errors when probing against peers per error type", + &["error_type"], registry ).unwrap(), uptime: register_histogram_with_registry!( diff --git a/consensus/core/src/network/anemo_network.rs b/consensus/core/src/network/anemo_network.rs index 9ffbad76857d4..c872d41cd00f9 100644 --- a/consensus/core/src/network/anemo_network.rs +++ b/consensus/core/src/network/anemo_network.rs @@ -238,7 +238,7 @@ impl NetworkClient for AnemoClient { &self, peer: AuthorityIndex, timeout: Duration, - ) -> ConsensusResult> { + ) -> ConsensusResult<(Vec, Vec)> { let mut client = self.get_client(peer, timeout).await?; let request = GetLatestRoundsRequest {}; let response = client @@ -254,7 +254,7 @@ impl NetworkClient for AnemoClient { } })?; let body = response.into_body(); - Ok(body.highest_received) + Ok((body.highest_received, body.highest_accepted)) } } @@ -440,7 +440,7 @@ impl ConsensusRpc for AnemoServiceProxy { "peer not found", ) })?; - let highest_received = self + let (highest_received, highest_accepted) = self .service .handle_get_latest_rounds(index) .await @@ -450,7 +450,10 @@ impl ConsensusRpc for AnemoServiceProxy { format!("{e}"), ) })?; - Ok(Response::new(GetLatestRoundsResponse { highest_received })) + Ok(Response::new(GetLatestRoundsResponse { + highest_received, + highest_accepted, + })) } } @@ -784,5 +787,7 @@ pub(crate) struct GetLatestRoundsRequest {} #[derive(Clone, Serialize, Deserialize)] pub(crate) struct GetLatestRoundsResponse { // Highest received round per authority. - highest_received: Vec, + highest_received: Vec, + // Highest accepted round per authority. + highest_accepted: Vec, } diff --git a/consensus/core/src/network/mod.rs b/consensus/core/src/network/mod.rs index 86477d972c22c..97c2d7e4f794e 100644 --- a/consensus/core/src/network/mod.rs +++ b/consensus/core/src/network/mod.rs @@ -116,12 +116,12 @@ pub(crate) trait NetworkClient: Send + Sync + Sized + 'static { timeout: Duration, ) -> ConsensusResult>; - /// Gets the latest received rounds of all authorities from the peer. + /// Gets the latest received & accepted rounds of all authorities from the peer. async fn get_latest_rounds( &self, peer: AuthorityIndex, timeout: Duration, - ) -> ConsensusResult>; + ) -> ConsensusResult<(Vec, Vec)>; } /// Network service for handling requests from peers. @@ -166,8 +166,11 @@ pub(crate) trait NetworkService: Send + Sync + 'static { authorities: Vec, ) -> ConsensusResult>; - /// Handles the request to get the latest received rounds of all authorities. - async fn handle_get_latest_rounds(&self, peer: AuthorityIndex) -> ConsensusResult>; + /// Handles the request to get the latest received & accepted rounds of all authorities. + async fn handle_get_latest_rounds( + &self, + peer: AuthorityIndex, + ) -> ConsensusResult<(Vec, Vec)>; } /// An `AuthorityNode` holds a `NetworkManager` until shutdown. diff --git a/consensus/core/src/network/test_network.rs b/consensus/core/src/network/test_network.rs index f736b01ac27cd..0290a599ab4e9 100644 --- a/consensus/core/src/network/test_network.rs +++ b/consensus/core/src/network/test_network.rs @@ -92,7 +92,10 @@ impl NetworkService for Mutex { unimplemented!("Unimplemented") } - async fn handle_get_latest_rounds(&self, _peer: AuthorityIndex) -> ConsensusResult> { + async fn handle_get_latest_rounds( + &self, + _peer: AuthorityIndex, + ) -> ConsensusResult<(Vec, Vec)> { unimplemented!("Unimplemented") } } diff --git a/consensus/core/src/network/tonic_network.rs b/consensus/core/src/network/tonic_network.rs index 59a29a102ff90..43b472fe6aadb 100644 --- a/consensus/core/src/network/tonic_network.rs +++ b/consensus/core/src/network/tonic_network.rs @@ -325,14 +325,15 @@ impl NetworkClient for TonicClient { &self, peer: AuthorityIndex, timeout: Duration, - ) -> ConsensusResult> { + ) -> ConsensusResult<(Vec, Vec)> { let mut client = self.get_client(peer, timeout).await?; let mut request = Request::new(GetLatestRoundsRequest {}); request.set_timeout(timeout); let response = client.get_latest_rounds(request).await.map_err(|e| { ConsensusError::NetworkRequest(format!("get_latest_rounds failed: {e:?}")) })?; - Ok(response.into_inner().highest_received) + let response = response.into_inner(); + Ok((response.highest_received, response.highest_accepted)) } } @@ -644,12 +645,15 @@ impl ConsensusService for TonicServiceProxy { else { return Err(tonic::Status::internal("PeerInfo not found")); }; - let highest_received = self + let (highest_received, highest_accepted) = self .service .handle_get_latest_rounds(peer_index) .await .map_err(|e| tonic::Status::internal(format!("{e:?}")))?; - Ok(Response::new(GetLatestRoundsResponse { highest_received })) + Ok(Response::new(GetLatestRoundsResponse { + highest_received, + highest_accepted, + })) } } @@ -1199,6 +1203,9 @@ pub(crate) struct GetLatestRoundsResponse { // Highest received round per authority. #[prost(uint32, repeated, tag = "1")] highest_received: Vec, + // Highest accepted round per authority. + #[prost(uint32, repeated, tag = "2")] + highest_accepted: Vec, } fn chunk_blocks(blocks: Vec, chunk_limit: usize) -> Vec> { diff --git a/consensus/core/src/round_prober.rs b/consensus/core/src/round_prober.rs index 3ce8b04dc8ede..9fb8022ed9703 100644 --- a/consensus/core/src/round_prober.rs +++ b/consensus/core/src/round_prober.rs @@ -1,18 +1,20 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -//! RoundProber periodically checks each peer for the latest rounds they received from others. -//! This provides insight into how effectively each authority's blocks are propagated across -//! the network. +//! RoundProber periodically checks each peer for the latest rounds they received and accepted +//! from others. This provides insight into how effectively each authority's blocks are propagated +//! and accepted across the network. //! //! Unlike inferring accepted rounds from the DAG of each block, RoundProber has the benefit that //! it remains active even when peers are not proposing. This makes it essential for determining //! when to disable optimizations that improve DAG quality but may compromise liveness. //! -//! RoundProber's data source is the `highest_received_rounds` tracked by the CoreThreadDispatcher. -//! These rounds are updated after blocks are verified but before checking for dependencies. -//! This should make the values more indicative of how well authorities propagate blocks, and less -//! influenced by the quality of ancestors in the proposed blocks. +//! RoundProber's data sources include the `highest_received_rounds` & `highest_accepted_rounds` tracked +//! by the CoreThreadDispatcher and DagState. The received rounds are updated after blocks are verified +//! but before checking for dependencies. This should make the values more indicative of how well authorities +//! propagate blocks, and less influenced by the quality of ancestors in the proposed blocks. The +//! accepted rounds are updated after checking for dependencies which should indicate the quality +//! of the proposed blocks including its ancestors. use std::{sync::Arc, time::Duration}; @@ -29,7 +31,8 @@ use crate::{ }; /// A [`QuorumRound`] is a round range [low, high]. It is computed from -/// highest received rounds of an authority reported by all authorities. +/// highest received or accepted rounds of an authority reported by all +/// authorities. /// The bounds represent: /// - the highest round lower or equal to rounds from a quorum (low) /// - the lowest round higher or equal to rounds from a quorum (high) @@ -117,20 +120,15 @@ impl RoundProber { // Probes each peer for the latest rounds they received from others. // Returns the quorum round for each authority, and the propagation delay // of own blocks. - pub(crate) async fn probe(&self) -> (Vec, Round) { + pub(crate) async fn probe(&self) -> (Vec, Vec, Round) { let _scope = monitored_scope("RoundProber"); let node_metrics = &self.context.metrics.node_metrics; let request_timeout = Duration::from_millis(self.context.parameters.round_prober_request_timeout_ms); let own_index = self.context.own_index; - let last_proposed_round = self - .dag_state - .read() - .get_last_block_for_authority(own_index) - .round(); - let mut requests = FuturesUnordered::new(); + for (peer, _) in self.context.committee.authorities() { if peer == own_index { continue; @@ -148,22 +146,52 @@ impl RoundProber { let mut highest_received_rounds = vec![vec![0; self.context.committee.size()]; self.context.committee.size()]; + let mut highest_accepted_rounds = + vec![vec![0; self.context.committee.size()]; self.context.committee.size()]; + + let blocks = self + .dag_state + .read() + .get_last_cached_block_per_authority(Round::MAX); + let local_highest_accepted_rounds = blocks + .into_iter() + .map(|block| block.round()) + .collect::>(); + let last_proposed_round = local_highest_accepted_rounds[own_index]; + + // For our own index, the highest recieved & accepted round is our last + // accepted round or our last proposed round. highest_received_rounds[own_index] = self.core_thread_dispatcher.highest_received_rounds(); + highest_accepted_rounds[own_index] = local_highest_accepted_rounds; highest_received_rounds[own_index][own_index] = last_proposed_round; + highest_accepted_rounds[own_index][own_index] = last_proposed_round; + loop { tokio::select! { result = requests.next() => { - let Some((peer, result)) = result else { - break; - }; + let Some((peer, result)) = result else { break }; match result { - Ok(Ok(rounds)) => { - if rounds.len() == self.context.committee.size() { - highest_received_rounds[peer] = rounds; + Ok(Ok((received, accepted))) => { + if received.len() == self.context.committee.size() + { + highest_received_rounds[peer] = received; } else { - node_metrics.round_prober_request_errors.inc(); - tracing::warn!("Received invalid number of rounds from peer {}", peer); + node_metrics.round_prober_request_errors.with_label_values(&["invalid_received_rounds"]).inc(); + tracing::warn!("Received invalid number of received rounds from peer {}", peer); } + + if self + .context + .protocol_config + .consensus_round_prober_probe_accepted_rounds() { + if accepted.len() == self.context.committee.size() { + highest_accepted_rounds[peer] = accepted; + } else { + node_metrics.round_prober_request_errors.with_label_values(&["invalid_accepted_rounds"]).inc(); + tracing::warn!("Received invalid number of accepted rounds from peer {}", peer); + } + } + }, // When a request fails, the highest received rounds from that authority will be 0 // for the subsequent computations. @@ -176,22 +204,20 @@ impl RoundProber { // (peer A cannot propagate its blocks well). It can be difficult to distinguish between // own probing failures and actual propagation issues. Ok(Err(err)) => { - node_metrics.round_prober_request_errors.inc(); + node_metrics.round_prober_request_errors.with_label_values(&["failed_fetch"]).inc(); tracing::warn!("Failed to get latest rounds from peer {}: {:?}", peer, err); }, Err(_) => { - node_metrics.round_prober_request_errors.inc(); + node_metrics.round_prober_request_errors.with_label_values(&["timeout"]).inc(); tracing::warn!("Timeout while getting latest rounds from peer {}", peer); }, } } - _ = self.shutdown_notify.wait() => { - break; - } + _ = self.shutdown_notify.wait() => break, } } - let quorum_rounds: Vec<_> = self + let received_quorum_rounds: Vec<_> = self .context .committee .authorities() @@ -199,21 +225,48 @@ impl RoundProber { compute_quorum_round(&self.context.committee, peer, &highest_received_rounds) }) .collect(); - for ((low, high), (_, authority)) in quorum_rounds + for ((low, high), (_, authority)) in received_quorum_rounds .iter() .zip(self.context.committee.authorities()) { node_metrics - .round_prober_quorum_round_gaps + .round_prober_received_quorum_round_gaps .with_label_values(&[&authority.hostname]) .set((high - low) as i64); node_metrics - .round_prober_low_quorum_round + .round_prober_low_received_quorum_round .with_label_values(&[&authority.hostname]) .set(*low as i64); // The gap can be negative if this validator is lagging behind the network. node_metrics - .round_prober_current_round_gaps + .round_prober_current_received_round_gaps + .with_label_values(&[&authority.hostname]) + .set(last_proposed_round as i64 - *low as i64); + } + + let accepted_quorum_rounds: Vec<_> = self + .context + .committee + .authorities() + .map(|(peer, _)| { + compute_quorum_round(&self.context.committee, peer, &highest_accepted_rounds) + }) + .collect(); + for ((low, high), (_, authority)) in accepted_quorum_rounds + .iter() + .zip(self.context.committee.authorities()) + { + node_metrics + .round_prober_accepted_quorum_round_gaps + .with_label_values(&[&authority.hostname]) + .set((high - low) as i64); + node_metrics + .round_prober_low_accepted_quorum_round + .with_label_values(&[&authority.hostname]) + .set(*low as i64); + // The gap can be negative if this validator is lagging behind the network. + node_metrics + .round_prober_current_accepted_round_gaps .with_label_values(&[&authority.hostname]) .set(last_proposed_round as i64 - *low as i64); } @@ -226,7 +279,8 @@ impl RoundProber { // that can reduce round rate. // Because of the nature of TCP and block streaming, propagation delay is expected to be // 0 in most cases, even when the actual latency of broadcasting blocks is high. - let propagation_delay = last_proposed_round.saturating_sub(quorum_rounds[own_index].0); + let propagation_delay = + last_proposed_round.saturating_sub(received_quorum_rounds[own_index].0); node_metrics .round_prober_propagation_delays .observe(propagation_delay as f64); @@ -235,15 +289,23 @@ impl RoundProber { .set(propagation_delay as i64); if let Err(e) = self .core_thread_dispatcher - .set_propagation_delay_and_quorum_rounds(propagation_delay, quorum_rounds.clone()) + .set_propagation_delay_and_quorum_rounds( + propagation_delay, + received_quorum_rounds.clone(), + accepted_quorum_rounds.clone(), + ) { tracing::warn!( - "Failed to set propagation delay and quorum rounds {quorum_rounds:?} on Core: {:?}", + "Failed to set propagation delay and quorum rounds {received_quorum_rounds:?} on Core: {:?}", e ); } - (quorum_rounds, propagation_delay) + ( + received_quorum_rounds, + accepted_quorum_rounds, + propagation_delay, + ) } } @@ -266,9 +328,8 @@ fn compute_quorum_round( let mut total_stake = 0; let mut low = 0; for (round, stake) in rounds_with_stake.iter().rev() { - let reached_quorum_before = total_stake >= committee.quorum_threshold(); total_stake += stake; - if !reached_quorum_before && total_stake >= committee.quorum_threshold() { + if total_stake >= committee.quorum_threshold() { low = *round; break; } @@ -277,9 +338,8 @@ fn compute_quorum_round( let mut total_stake = 0; let mut high = 0; for (round, stake) in rounds_with_stake.iter() { - let reached_quorum_before = total_stake >= committee.quorum_threshold(); total_stake += stake; - if !reached_quorum_before && total_stake >= committee.quorum_threshold() { + if total_stake >= committee.quorum_threshold() { high = *round; break; } @@ -314,7 +374,8 @@ mod test { struct FakeThreadDispatcher { highest_received_rounds: Vec, propagation_delay: Mutex, - quorum_rounds: Mutex>, + received_quorum_rounds: Mutex>, + accepted_quorum_rounds: Mutex>, } impl FakeThreadDispatcher { @@ -322,7 +383,8 @@ mod test { Self { highest_received_rounds, propagation_delay: Mutex::new(0), - quorum_rounds: Mutex::new(Vec::new()), + received_quorum_rounds: Mutex::new(Vec::new()), + accepted_quorum_rounds: Mutex::new(Vec::new()), } } @@ -330,8 +392,12 @@ mod test { *self.propagation_delay.lock() } - fn quorum_rounds(&self) -> Vec { - self.quorum_rounds.lock().clone() + fn received_quorum_rounds(&self) -> Vec { + self.received_quorum_rounds.lock().clone() + } + + fn accepted_quorum_rounds(&self) -> Vec { + self.accepted_quorum_rounds.lock().clone() } } @@ -359,10 +425,13 @@ mod test { fn set_propagation_delay_and_quorum_rounds( &self, delay: Round, - quorum_rounds: Vec, + received_quorum_rounds: Vec, + accepted_quorum_rounds: Vec, ) -> Result<(), CoreError> { - let mut quorum_round_per_authority = self.quorum_rounds.lock(); - *quorum_round_per_authority = quorum_rounds; + let mut received_quorum_round_per_authority = self.received_quorum_rounds.lock(); + *received_quorum_round_per_authority = received_quorum_rounds; + let mut accepted_quorum_round_per_authority = self.accepted_quorum_rounds.lock(); + *accepted_quorum_round_per_authority = accepted_quorum_rounds; let mut propagation_delay = self.propagation_delay.lock(); *propagation_delay = delay; Ok(()) @@ -379,12 +448,17 @@ mod test { struct FakeNetworkClient { highest_received_rounds: Vec>, + highest_accepted_rounds: Vec>, } impl FakeNetworkClient { - fn new(highest_received_rounds: Vec>) -> Self { + fn new( + highest_received_rounds: Vec>, + highest_accepted_rounds: Vec>, + ) -> Self { Self { highest_received_rounds, + highest_accepted_rounds, } } } @@ -444,12 +518,13 @@ mod test { &self, peer: AuthorityIndex, _timeout: Duration, - ) -> ConsensusResult> { - let rounds = self.highest_received_rounds[peer].clone(); - if rounds.is_empty() { + ) -> ConsensusResult<(Vec, Vec)> { + let received_rounds = self.highest_received_rounds[peer].clone(); + let accepted_rounds = self.highest_accepted_rounds[peer].clone(); + if received_rounds.is_empty() && accepted_rounds.is_empty() { Err(ConsensusError::NetworkRequestTimeout("test".to_string())) } else { - Ok(rounds) + Ok((received_rounds, accepted_rounds)) } } } @@ -464,15 +539,26 @@ mod test { let store = Arc::new(MemStore::new()); let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store))); // Have some peers return error or incorrect number of rounds. - let network_client = Arc::new(FakeNetworkClient::new(vec![ - vec![], - vec![109, 121, 131, 0, 151, 161, 171], - vec![101, 0, 103, 104, 105, 166, 107], - vec![], - vec![100, 102, 133, 0, 155, 106, 177], - vec![105, 115, 103, 0, 125, 126, 127], - vec![10, 20, 30, 40, 50, 60], - ])); + let network_client = Arc::new(FakeNetworkClient::new( + vec![ + vec![], + vec![109, 121, 131, 0, 151, 161, 171], + vec![101, 0, 103, 104, 105, 166, 107], + vec![], + vec![100, 102, 133, 0, 155, 106, 177], + vec![105, 115, 103, 0, 125, 126, 127], + vec![10, 20, 30, 40, 50, 60], + ], // highest_received_rounds + vec![ + vec![], + vec![0, 121, 131, 0, 151, 161, 171], + vec![1, 0, 103, 104, 105, 166, 107], + vec![], + vec![0, 102, 133, 0, 155, 106, 177], + vec![1, 115, 103, 0, 125, 126, 127], + vec![1, 20, 30, 40, 50, 60], + ], // highest_accepted_rounds + )); let prober = RoundProber::new( context.clone(), core_thread_dispatcher.clone(), @@ -480,9 +566,15 @@ mod test { network_client.clone(), ); - // Fake last proposed round to be 110. - let block = VerifiedBlock::new_for_test(TestBlock::new(110, 0).build()); - dag_state.write().accept_block(block); + // Create test blocks for each authority with incrementing rounds starting at 110 + let blocks = (0..NUM_AUTHORITIES) + .map(|authority| { + let round = 110 + (authority as u32 * 10); + VerifiedBlock::new_for_test(TestBlock::new(round, authority as u32).build()) + }) + .collect::>(); + + dag_state.write().accept_blocks(blocks); // Compute quorum rounds and propagation delay based on last proposed round = 110, // and highest received rounds: @@ -494,10 +586,11 @@ mod test { // 105, 115, 103, 0, 125, 126, 127, // 0, 0, 0, 0, 0, 0, 0, - let (quorum_rounds, propagation_delay) = prober.probe().await; + let (received_quorum_rounds, accepted_quorum_rounds, propagation_delay) = + prober.probe().await; assert_eq!( - quorum_rounds, + received_quorum_rounds, vec![ (100, 105), (0, 115), @@ -510,7 +603,7 @@ mod test { ); assert_eq!( - core_thread_dispatcher.quorum_rounds(), + core_thread_dispatcher.received_quorum_rounds(), vec![ (100, 105), (0, 115), @@ -524,6 +617,32 @@ mod test { // 110 - 100 = 10 assert_eq!(propagation_delay, 10); assert_eq!(core_thread_dispatcher.propagation_delay(), 10); + + assert_eq!( + accepted_quorum_rounds, + vec![ + (0, 1), + (0, 115), + (103, 130), + (0, 0), + (105, 150), + (106, 160), + (107, 170) + ] + ); + + assert_eq!( + core_thread_dispatcher.accepted_quorum_rounds(), + vec![ + (0, 1), + (0, 115), + (103, 130), + (0, 0), + (105, 150), + (106, 160), + (107, 170) + ] + ); } #[tokio::test] diff --git a/consensus/core/src/subscriber.rs b/consensus/core/src/subscriber.rs index 19035ebfc347d..3782ba74cba6d 100644 --- a/consensus/core/src/subscriber.rs +++ b/consensus/core/src/subscriber.rs @@ -311,7 +311,7 @@ mod test { &self, _peer: AuthorityIndex, _timeout: Duration, - ) -> ConsensusResult> { + ) -> ConsensusResult<(Vec, Vec)> { unimplemented!("Unimplemented") } } diff --git a/consensus/core/src/synchronizer.rs b/consensus/core/src/synchronizer.rs index dbcf447be9b4e..92c2895fe580d 100644 --- a/consensus/core/src/synchronizer.rs +++ b/consensus/core/src/synchronizer.rs @@ -1220,7 +1220,7 @@ mod tests { &self, _peer: AuthorityIndex, _timeout: Duration, - ) -> ConsensusResult> { + ) -> ConsensusResult<(Vec, Vec)> { unimplemented!("Unimplemented") } } diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 68e409862dc98..c95b41b3a5366 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -1293,7 +1293,7 @@ "name": "Result", "value": { "minSupportedProtocolVersion": "1", - "maxSupportedProtocolVersion": "69", + "maxSupportedProtocolVersion": "70", "protocolVersion": "6", "featureFlags": { "accept_zklogin_in_multisig": false, @@ -1307,6 +1307,7 @@ "consensus_distributed_vote_scoring_strategy": false, "consensus_order_end_of_epoch_last": true, "consensus_round_prober": false, + "consensus_round_prober_probe_accepted_rounds": false, "consensus_smart_ancestor_selection": false, "disable_invariant_violation_check_in_swap_loc": false, "disallow_adding_abilities_on_upgrade": false, diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index 34f286e7b521d..25756ae72867d 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -18,7 +18,7 @@ use tracing::{info, warn}; /// The minimum and maximum protocol versions supported by this build. const MIN_PROTOCOL_VERSION: u64 = 1; -const MAX_PROTOCOL_VERSION: u64 = 69; +const MAX_PROTOCOL_VERSION: u64 = 70; // Record history of protocol version allocations here: // @@ -198,6 +198,7 @@ const MAX_PROTOCOL_VERSION: u64 = 69; // Version 69: Sets number of rounds allowed for fastpath voting in consensus. // Enable smart ancestor selection in devnet. // Enable G1Uncompressed group in testnet. +// Version 70: Enable probing for accepted rounds in round prober. #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -571,6 +572,10 @@ struct FeatureFlags { // Use smart ancestor selection in consensus. #[serde(skip_serializing_if = "is_false")] consensus_smart_ancestor_selection: bool, + + // Probe accepted rounds in round prober. + #[serde(skip_serializing_if = "is_false")] + consensus_round_prober_probe_accepted_rounds: bool, } fn is_false(b: &bool) -> bool { @@ -1691,6 +1696,11 @@ impl ProtocolConfig { pub fn consensus_smart_ancestor_selection(&self) -> bool { self.feature_flags.consensus_smart_ancestor_selection } + + pub fn consensus_round_prober_probe_accepted_rounds(&self) -> bool { + self.feature_flags + .consensus_round_prober_probe_accepted_rounds + } } #[cfg(not(msim))] @@ -2970,6 +2980,13 @@ impl ProtocolConfig { cfg.feature_flags.uncompressed_g1_group_elements = true; } } + 70 => { + if chain != Chain::Mainnet && chain != Chain::Testnet { + // Enable probing for accepted rounds in round prober. + cfg.feature_flags + .consensus_round_prober_probe_accepted_rounds = true; + } + } // Use this template when making changes: // // // modify an existing constant. @@ -3136,6 +3153,11 @@ impl ProtocolConfig { self.feature_flags .disallow_new_modules_in_deps_only_packages = val; } + + pub fn set_consensus_round_prober_probe_accepted_rounds(&mut self, val: bool) { + self.feature_flags + .consensus_round_prober_probe_accepted_rounds = val; + } } type OverrideFn = dyn Fn(ProtocolVersion, ProtocolConfig) -> ProtocolConfig + Send; diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_70.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_70.snap new file mode 100644 index 0000000000000..1172bd3f1b3d0 --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_70.snap @@ -0,0 +1,340 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 70 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + zklogin_auth: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + upgraded_multisig_supported: true + txn_base_cost_as_multiplier: true + shared_object_deletion: true + narwhal_new_leader_election_schedule: true + loaded_child_object_format: true + enable_jwk_consensus_updates: true + end_of_epoch_transaction_supported: true + simple_conservation_checks: true + loaded_child_object_format_type: true + receive_objects: true + random_beacon: true + bridge: true + enable_effects_v2: true + narwhal_certificate_v2: true + verify_legacy_zklogin_address: true + recompute_has_public_transfer_in_execution: true + accept_zklogin_in_multisig: true + include_consensus_digest_in_prologue: true + hardened_otw_check: true + allow_receiving_object_id: true + enable_coin_deny_list: true + enable_group_ops_native_functions: true + reject_mutable_random_on_entry_functions: true + per_object_congestion_control_mode: TotalGasBudgetWithCap + consensus_choice: Mysticeti + consensus_network: Tonic + zklogin_max_epoch_upper_bound_delta: 30 + mysticeti_leader_scoring_and_schedule: true + reshare_at_same_initial_version: true + resolve_abort_locations_to_package_id: true + mysticeti_use_committed_subdag_digest: true + record_consensus_determined_version_assignments_in_prologue: true + fresh_vm_on_framework_upgrade: true + prepend_prologue_tx_in_consensus_commit_in_checkpoints: true + mysticeti_num_leaders_per_round: 1 + soft_bundle: true + enable_coin_deny_list_v2: true + rethrow_serialization_type_layout_errors: true + consensus_distributed_vote_scoring_strategy: true + consensus_round_prober: true + validate_identifier_inputs: true + relocate_event_module: true + disallow_new_modules_in_deps_only_packages: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 +gas_model_version: 8 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +group_ops_bls12381_g1_to_uncompressed_g1_cost: 26 +group_ops_bls12381_uncompressed_g1_to_g1_cost: 52 +group_ops_bls12381_uncompressed_g1_sum_base_cost: 26 +group_ops_bls12381_uncompressed_g1_sum_cost_per_term: 13 +group_ops_bls12381_uncompressed_g1_sum_max_terms: 2000 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 3 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 500 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +consensus_voting_rounds: 40 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 40 +max_deferral_rounds_for_congestion_control: 10 +max_txn_cost_overage_per_object_in_commit: 18446744073709551615 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 18500000 +max_accumulated_randomness_txn_cost_per_object_in_mysticeti_commit: 3700000 +gas_budget_based_txn_cost_cap_factor: 400000 +gas_budget_based_txn_cost_absolute_cap_commit_count: 50 + diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_70.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_70.snap new file mode 100644 index 0000000000000..710a4001f03e6 --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_70.snap @@ -0,0 +1,341 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 70 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + zklogin_auth: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + upgraded_multisig_supported: true + txn_base_cost_as_multiplier: true + shared_object_deletion: true + narwhal_new_leader_election_schedule: true + loaded_child_object_format: true + enable_jwk_consensus_updates: true + end_of_epoch_transaction_supported: true + simple_conservation_checks: true + loaded_child_object_format_type: true + receive_objects: true + random_beacon: true + bridge: true + enable_effects_v2: true + narwhal_certificate_v2: true + verify_legacy_zklogin_address: true + recompute_has_public_transfer_in_execution: true + accept_zklogin_in_multisig: true + include_consensus_digest_in_prologue: true + hardened_otw_check: true + allow_receiving_object_id: true + enable_coin_deny_list: true + enable_group_ops_native_functions: true + reject_mutable_random_on_entry_functions: true + per_object_congestion_control_mode: TotalGasBudgetWithCap + consensus_choice: Mysticeti + consensus_network: Tonic + zklogin_max_epoch_upper_bound_delta: 30 + mysticeti_leader_scoring_and_schedule: true + reshare_at_same_initial_version: true + resolve_abort_locations_to_package_id: true + mysticeti_use_committed_subdag_digest: true + record_consensus_determined_version_assignments_in_prologue: true + fresh_vm_on_framework_upgrade: true + prepend_prologue_tx_in_consensus_commit_in_checkpoints: true + mysticeti_num_leaders_per_round: 1 + soft_bundle: true + enable_coin_deny_list_v2: true + rethrow_serialization_type_layout_errors: true + consensus_distributed_vote_scoring_strategy: true + consensus_round_prober: true + validate_identifier_inputs: true + relocate_event_module: true + uncompressed_g1_group_elements: true + disallow_new_modules_in_deps_only_packages: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 +gas_model_version: 8 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +group_ops_bls12381_g1_to_uncompressed_g1_cost: 26 +group_ops_bls12381_uncompressed_g1_to_g1_cost: 52 +group_ops_bls12381_uncompressed_g1_sum_base_cost: 26 +group_ops_bls12381_uncompressed_g1_sum_cost_per_term: 13 +group_ops_bls12381_uncompressed_g1_sum_max_terms: 2000 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 3 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 500 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +consensus_voting_rounds: 40 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 40 +max_deferral_rounds_for_congestion_control: 10 +max_txn_cost_overage_per_object_in_commit: 18446744073709551615 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 18500000 +max_accumulated_randomness_txn_cost_per_object_in_mysticeti_commit: 3700000 +gas_budget_based_txn_cost_cap_factor: 400000 +gas_budget_based_txn_cost_absolute_cap_commit_count: 50 + diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_70.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_70.snap new file mode 100644 index 0000000000000..977a9d7425c52 --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_70.snap @@ -0,0 +1,353 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 70 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + zklogin_auth: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + upgraded_multisig_supported: true + txn_base_cost_as_multiplier: true + shared_object_deletion: true + narwhal_new_leader_election_schedule: true + loaded_child_object_format: true + enable_jwk_consensus_updates: true + end_of_epoch_transaction_supported: true + simple_conservation_checks: true + loaded_child_object_format_type: true + receive_objects: true + random_beacon: true + bridge: true + enable_effects_v2: true + narwhal_certificate_v2: true + verify_legacy_zklogin_address: true + recompute_has_public_transfer_in_execution: true + accept_zklogin_in_multisig: true + include_consensus_digest_in_prologue: true + hardened_otw_check: true + allow_receiving_object_id: true + enable_poseidon: true + enable_coin_deny_list: true + enable_group_ops_native_functions: true + enable_group_ops_native_function_msm: true + reject_mutable_random_on_entry_functions: true + per_object_congestion_control_mode: TotalGasBudgetWithCap + consensus_choice: Mysticeti + consensus_network: Tonic + zklogin_max_epoch_upper_bound_delta: 30 + mysticeti_leader_scoring_and_schedule: true + reshare_at_same_initial_version: true + resolve_abort_locations_to_package_id: true + mysticeti_use_committed_subdag_digest: true + enable_vdf: true + record_consensus_determined_version_assignments_in_prologue: true + fresh_vm_on_framework_upgrade: true + prepend_prologue_tx_in_consensus_commit_in_checkpoints: true + mysticeti_num_leaders_per_round: 1 + soft_bundle: true + enable_coin_deny_list_v2: true + passkey_auth: true + authority_capabilities_v2: true + rethrow_serialization_type_layout_errors: true + consensus_distributed_vote_scoring_strategy: true + consensus_round_prober: true + validate_identifier_inputs: true + mysticeti_fastpath: true + relocate_event_module: true + uncompressed_g1_group_elements: true + disallow_new_modules_in_deps_only_packages: true + consensus_smart_ancestor_selection: true + consensus_round_prober_probe_accepted_rounds: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 +gas_model_version: 8 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +poseidon_bn254_cost_base: 260 +poseidon_bn254_cost_per_block: 10 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +group_ops_bls12381_g1_to_uncompressed_g1_cost: 26 +group_ops_bls12381_uncompressed_g1_to_g1_cost: 52 +group_ops_bls12381_uncompressed_g1_sum_base_cost: 26 +group_ops_bls12381_uncompressed_g1_sum_cost_per_term: 13 +group_ops_bls12381_uncompressed_g1_sum_max_terms: 2000 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +vdf_verify_vdf_cost: 1500 +vdf_hash_to_input_cost: 100 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 3 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 500 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +consensus_voting_rounds: 40 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 40 +max_deferral_rounds_for_congestion_control: 10 +max_txn_cost_overage_per_object_in_commit: 18446744073709551615 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 18500000 +max_accumulated_randomness_txn_cost_per_object_in_mysticeti_commit: 3700000 +gas_budget_based_txn_cost_cap_factor: 400000 +gas_budget_based_txn_cost_absolute_cap_commit_count: 50 + diff --git a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap index 7a81890461ed6..5ef4629fd2567 100644 --- a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap +++ b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap @@ -6,7 +6,7 @@ ssfn_config_info: ~ validator_config_info: ~ parameters: chain_start_timestamp_ms: 0 - protocol_version: 69 + protocol_version: 70 allow_insertion_of_extra_objects: true epoch_duration_ms: 86400000 stake_subsidy_start_epoch: 0 diff --git a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap index ce61493c555c6..1730c9984aa4f 100644 --- a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap +++ b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap @@ -3,7 +3,7 @@ source: crates/sui-swarm-config/tests/snapshot_tests.rs expression: genesis.sui_system_object().into_genesis_version_for_tooling() --- epoch: 0 -protocol_version: 69 +protocol_version: 70 system_state_version: 1 validators: total_stake: 20000000000000000 @@ -240,13 +240,13 @@ validators: next_epoch_worker_address: ~ extra_fields: id: - id: "0x0ab85bf1aef18aad179ca34f4d95e34dd60dd1b2bda18d558e7b0bfe14ba8b77" + id: "0xd95aeec428abb407baf6185f83090de23f0fbe3ab5ec5ac455dfb09b01a86823" size: 0 voting_power: 10000 - operation_cap_id: "0x2fd0e88948443e54727efb6d1756d8c39735a421dac7bb92be690abe205b5bfa" + operation_cap_id: "0x3a9f6bbd0b886a6e35dd50bdb54bb9a1dca35b24b6ae826add15a5e9ea45c1fb" gas_price: 1000 staking_pool: - id: "0xdcc39ac807721ee62f3fe0a6c697cbd93389f79b1a6717a95ccd28dca27821c8" + id: "0x5d5d4c125492ab2dcf2068aeb8f3c1361b4d30912023aac14313ddbbb77e7d71" activation_epoch: 0 deactivation_epoch: ~ sui_balance: 20000000000000000 @@ -254,14 +254,14 @@ validators: value: 0 pool_token_balance: 20000000000000000 exchange_rates: - id: "0xaa706539eb8876b6c6a9b335c8d0ab1275d0a08a8ad29a6e6da68891e59298e4" + id: "0x0383c9a5226e6811ada38b22bf838c2617f89770394bd74560a9a0fdfd94c95d" size: 1 pending_stake: 0 pending_total_sui_withdraw: 0 pending_pool_token_withdraw: 0 extra_fields: id: - id: "0x651bc7aee334c01c02043e8caa326beaf7cf3fee5f099dda61cd39a971b1a0e6" + id: "0xc883911bbcf25665e4336f1e024c68535cfb34177b558d84a3e7870741bfb1b2" size: 0 commission_rate: 200 next_epoch_stake: 20000000000000000 @@ -269,27 +269,27 @@ validators: next_epoch_commission_rate: 200 extra_fields: id: - id: "0x3e9b72b2ab6660a2fd11af8d5f44925fccdb0fd793ec425f6438a1b149f8e3ea" + id: "0x24724d158dc6771c67b1dfce287faaac19570abbc4636bd0a9c3ca84cbc386f9" size: 0 pending_active_validators: contents: - id: "0xd93aaac7fc3ff26d6f2336ad8f11c15f2e51a0623715fce08742faa1caabdc65" + id: "0x07d40f289f98cf6f567ff9faaf07a266fba60765fa14389b4b956bba44c1a95a" size: 0 pending_removals: [] staking_pool_mappings: - id: "0xffabe0c370cf34f726bb6132f97dbc7bf7f77c4b5322797f786cbc73b0c0d125" + id: "0xd4fe1d15e9895507c5018cf0319d87dc43802ca4b86c1506fd643b83809cf731" size: 1 inactive_validators: - id: "0x3d3953f69d3dcb19cbaad3c5372084f72a9290916c5faf5f8f241cb5d5e6c94b" + id: "0xeedec924bd8bbb8b71a3533270c6c59d834268f82c55941140fd0b0a5473cbed" size: 0 validator_candidates: - id: "0x66528ea16da7d7286671c3f187a1e68e0acd4fc13ffe45a0e5de12d2e2a825ec" + id: "0x2b0f871fb557f02fa03767da3ed958431a6d368924716f3f40e442a19f50d93a" size: 0 at_risk_validators: contents: [] extra_fields: id: - id: "0x1dff1b20ab0c400d5e48019f10ded46bdb08927f2e22fa4ced77ee6829b3c618" + id: "0x1f240b3a58741356c667ae3f8afc9b555951caff229481a42ae7ba2d83e020c6" size: 0 storage_fund: total_object_storage_rebates: @@ -306,7 +306,7 @@ parameters: validator_low_stake_grace_period: 7 extra_fields: id: - id: "0x3730cc216e9af079f9f8955b07b50366ee726b2d95dd102c4420e0e5102ce558" + id: "0x210b2c39cb45252263779891213f744bc7e9f41bb36f909312981339e7cdc407" size: 0 reference_gas_price: 1000 validator_report_records: @@ -320,7 +320,7 @@ stake_subsidy: stake_subsidy_decrease_rate: 1000 extra_fields: id: - id: "0xdf3d8018bba72f99aee8cfcb7af644e0edbbd86cb02732cf4bd2f3bbc8b45a25" + id: "0x7e868a08d450d1b4efb59ae47d11b7653bfcfb4951aa8df560c2dc91cd96af81" size: 0 safe_mode: false safe_mode_storage_rewards: @@ -332,6 +332,6 @@ safe_mode_non_refundable_storage_fee: 0 epoch_start_timestamp_ms: 10 extra_fields: id: - id: "0xbb67e32883f2542fb09119328da10c216ae19f663b64f024a724ca8c508502fa" + id: "0x255200b4f3d4163d381d794c0915bdd635ff52661dfc9810d07285738fef908d" size: 0