diff --git a/execution/executor-types/src/ledger_update_output.rs b/execution/executor-types/src/ledger_update_output.rs index 41baf957ffeaf1..169fbe77c1aaa4 100644 --- a/execution/executor-types/src/ledger_update_output.rs +++ b/execution/executor-types/src/ledger_update_output.rs @@ -14,8 +14,8 @@ use aptos_types::{ proof::accumulator::InMemoryTransactionAccumulator, state_store::{combine_or_add_sharded_state_updates, ShardedStateUpdates}, transaction::{ - block_epilogue::BlockEndInfo, Transaction, TransactionInfo, TransactionStatus, - TransactionToCommit, Version, + block_epilogue::BlockEndInfo, TransactionInfo, TransactionStatus, TransactionToCommit, + Version, }, }; use itertools::zip_eq; @@ -64,10 +64,9 @@ impl LedgerUpdateOutput { /// block. pub fn ensure_ends_with_state_checkpoint(&self) -> Result<()> { ensure!( - self.to_commit.last().map_or(true, |txn| matches!( - txn.transaction(), - Transaction::StateCheckpoint(_) - )), + self.to_commit + .last() + .map_or(true, |txn| txn.transaction().is_non_reconfig_block_ending()), "Block not ending with a state checkpoint.", ); Ok(()) diff --git a/storage/aptosdb/src/db/include/aptosdb_testonly.rs b/storage/aptosdb/src/db/include/aptosdb_testonly.rs index bf9a4e31964102..cc49b179a619a8 100644 --- a/storage/aptosdb/src/db/include/aptosdb_testonly.rs +++ b/storage/aptosdb/src/db/include/aptosdb_testonly.rs @@ -102,7 +102,7 @@ pub fn gather_state_updates_until_last_checkpoint( if latest_checkpoint_version >= first_version { let idx = (latest_checkpoint_version - first_version) as usize; assert!( - txns_to_commit[idx].is_state_checkpoint(), + txns_to_commit[idx].has_state_checkpoint_hash(), "The new latest snapshot version passed in {:?} does not match with the last checkpoint version in txns_to_commit {:?}", latest_checkpoint_version, first_version + idx as u64 diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 0b0d82ac1e1752..e60bea6cce5587 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -362,7 +362,7 @@ impl AptosDB { skip_index_and_usage, txns_to_commit .iter() - .rposition(|txn| txn.is_state_checkpoint()), + .rposition(|txn| txn.has_state_checkpoint_hash()), )?; // Write block index if event index is skipped. diff --git a/storage/aptosdb/src/db/test_helper.rs b/storage/aptosdb/src/db/test_helper.rs index 8bb61fa9c2cc10..89a7ad311c3ff0 100644 --- a/storage/aptosdb/src/db/test_helper.rs +++ b/storage/aptosdb/src/db/test_helper.rs @@ -125,7 +125,7 @@ pub fn update_in_memory_state(state: &mut StateDelta, txns_to_commit: &[Transact .insert(key.clone(), value.clone()); }); next_version += 1; - if txn_to_commit.is_state_checkpoint() { + if txn_to_commit.has_state_checkpoint_hash() { state.current = state .current .batch_update( @@ -200,7 +200,7 @@ prop_compose! { let event_root_hash = InMemoryEventAccumulator::from_leaves(&event_hashes).root_hash(); // calculate state checkpoint hash and this must be the last txn - let state_checkpoint_hash = if txn.is_state_checkpoint() { + let state_checkpoint_hash = if txn.has_state_checkpoint_hash() { Some(state_checkpoint_root_hash) } else { None @@ -321,7 +321,7 @@ fn gen_snapshot_version( let last_checkpoint = txns_to_commit .iter() .enumerate() - .filter(|(_idx, x)| x.is_state_checkpoint()) + .filter(|(_idx, x)| x.has_state_checkpoint_hash()) .last() .map(|(idx, _)| idx); if let Some(idx) = last_checkpoint { @@ -463,7 +463,7 @@ fn verify_snapshots( for snapshot_version in snapshot_versions { let start = (cur_version - start_version) as usize; let end = (snapshot_version - start_version) as usize; - assert!(txns_to_commit[end].is_state_checkpoint()); + assert!(txns_to_commit[end].has_state_checkpoint_hash()); let expected_root_hash = db .ledger_db .transaction_info_db() @@ -826,7 +826,7 @@ pub fn verify_committed_transactions( assert_eq!(state_value_in_db, *state_value); } - if !txn_to_commit.is_state_checkpoint() { + if !txn_to_commit.has_state_checkpoint_hash() { // Fetch and verify transaction itself. let txn = txn_to_commit .transaction() diff --git a/storage/aptosdb/src/state_store/mod.rs b/storage/aptosdb/src/state_store/mod.rs index df8a0c08ee75b3..96de9f08c76b94 100644 --- a/storage/aptosdb/src/state_store/mod.rs +++ b/storage/aptosdb/src/state_store/mod.rs @@ -523,7 +523,7 @@ impl StateStore { .collect::>>()? .into_iter() .enumerate() - .filter(|(_idx, txn_info)| txn_info.is_state_checkpoint()) + .filter(|(_idx, txn_info)| txn_info.has_state_checkpoint_hash()) .last() .map(|(idx, _)| idx); latest_snapshot_state_view.prime_cache_by_write_set(&write_sets)?; diff --git a/testsuite/forge/src/interface/swarm.rs b/testsuite/forge/src/interface/swarm.rs index ce21f44f18f28f..b92928dcb9599b 100644 --- a/testsuite/forge/src/interface/swarm.rs +++ b/testsuite/forge/src/interface/swarm.rs @@ -307,10 +307,23 @@ pub trait SwarmExt: Swarm { } async fn wait_for_all_nodes_to_catchup_to_next(&self, timeout: Duration) -> Result<()> { + self.wait_for_all_nodes_to_catchup_to_future(timeout, 1) + .await + } + + async fn wait_for_all_nodes_to_catchup_to_future( + &self, + timeout: Duration, + versions_to_sync_past: u64, + ) -> Result<()> { let clients = self.get_all_nodes_clients_with_names(); let highest_synced_version = get_highest_synced_version(&clients).await?; - wait_for_all_nodes_to_catchup_to_version(&clients, highest_synced_version + 1, timeout) - .await + wait_for_all_nodes_to_catchup_to_version( + &clients, + highest_synced_version + versions_to_sync_past, + timeout, + ) + .await } fn get_validator_clients_with_names(&self) -> Vec<(String, RestClient)> { diff --git a/testsuite/smoke-test/src/execution.rs b/testsuite/smoke-test/src/execution.rs index c037c597ce29f5..d23da1224280d3 100644 --- a/testsuite/smoke-test/src/execution.rs +++ b/testsuite/smoke-test/src/execution.rs @@ -2,10 +2,21 @@ // Parts of the project are originally copyright © Meta Platforms, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::{smoke_test_environment::SwarmBuilder, utils::get_current_version}; -use aptos_forge::{NodeExt, SwarmExt}; +use crate::{ + aptos_cli::validator::generate_blob, smoke_test_environment::SwarmBuilder, + utils::get_current_version, +}; +use aptos::test::CliTestFramework; +use aptos_forge::{NodeExt, Swarm, SwarmExt}; +use aptos_rest_client::Client; +use aptos_types::on_chain_config::{ + BlockGasLimitType, ExecutionConfigV4, OnChainExecutionConfig, TransactionDeduperType, + TransactionShufflerType, +}; use std::{sync::Arc, time::Duration}; +const MAX_WAIT_SECS: u64 = 30; + #[tokio::test] async fn fallback_test() { let swarm = SwarmBuilder::new_local(1) @@ -42,3 +53,130 @@ async fn fallback_test() { .expect("milestone 1 taking too long"); } } + +async fn update_execution_config( + cli: &CliTestFramework, + root_cli_index: usize, + new_execution_config: OnChainExecutionConfig, +) { + let update_execution_config_script = format!( + r#" + script {{ + use aptos_framework::aptos_governance; + use aptos_framework::execution_config; + fun main(core_resources: &signer) {{ + let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); + let config_bytes = {}; + execution_config::set_for_next_epoch(&framework_signer, config_bytes); + aptos_governance::force_end_epoch(&framework_signer); + }} + }} + "#, + generate_blob(&bcs::to_bytes(&new_execution_config).unwrap()) + ); + cli.run_script(root_cli_index, &update_execution_config_script) + .await + .unwrap(); +} + +async fn get_last_txn_name(rest_client: &Client) -> Option<&'static str> { + let txns = rest_client + .get_transactions_bcs(None, Some(1)) + .await + .unwrap() + .into_inner(); + let txn_names = txns + .into_iter() + .map(|txn| txn.transaction.type_name()) + .collect::>(); + println!("{:?}", txn_names); + txn_names.last().copied() +} + +#[tokio::test] +async fn block_epilogue_upgrade_test() { + let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(2) + .with_aptos() + // Start with V1 + .with_init_genesis_config(Arc::new(|genesis_config| { + genesis_config.execution_config = OnChainExecutionConfig::V4(ExecutionConfigV4 { + transaction_shuffler_type: TransactionShufflerType::NoShuffling, + block_gas_limit_type: BlockGasLimitType::NoLimit, + transaction_deduper_type: TransactionDeduperType::TxnHashAndAuthenticatorV1, + }); + })) + .build_with_cli(0) + .await; + + swarm + .wait_for_all_nodes_to_catchup_to_future(Duration::from_secs(MAX_WAIT_SECS), 8) + .await + .unwrap(); + + let rest_client = swarm.validators().next().unwrap().rest_client(); + + assert_eq!( + get_last_txn_name(&rest_client).await, + Some("state_checkpoint") + ); + + for _ in 0..3 { + let root_cli_index = cli.add_account_with_address_to_cli( + swarm.root_key(), + swarm.chain_info().root_account().address(), + ); + + let current_execution_config = + crate::utils::get_current_execution_config(&rest_client).await; + match current_execution_config { + OnChainExecutionConfig::V4(inner) => { + assert!(!inner.block_gas_limit_type.add_block_limit_outcome_onchain()) + }, + _ => panic!("Unexpected execution config"), + }; + + // Enable BlockEpilogue + let new_execution_config = OnChainExecutionConfig::V4(ExecutionConfigV4 { + transaction_shuffler_type: TransactionShufflerType::NoShuffling, + block_gas_limit_type: BlockGasLimitType::default_for_genesis(), + transaction_deduper_type: TransactionDeduperType::TxnHashAndAuthenticatorV1, + }); + update_execution_config(&cli, root_cli_index, new_execution_config).await; + + swarm + .wait_for_all_nodes_to_catchup_to_future(Duration::from_secs(MAX_WAIT_SECS), 8) + .await + .unwrap(); + assert_eq!( + get_last_txn_name(&rest_client).await, + Some("block_epilogue") + ); + + let current_execution_config = + crate::utils::get_current_execution_config(&rest_client).await; + match current_execution_config { + OnChainExecutionConfig::V4(inner) => { + assert!(inner.block_gas_limit_type.add_block_limit_outcome_onchain()) + }, + _ => panic!("Unexpected execution config"), + }; + + // Disable BlockEpilogue + let new_execution_config = OnChainExecutionConfig::V4(ExecutionConfigV4 { + transaction_shuffler_type: TransactionShufflerType::NoShuffling, + block_gas_limit_type: BlockGasLimitType::NoLimit, + transaction_deduper_type: TransactionDeduperType::TxnHashAndAuthenticatorV1, + }); + update_execution_config(&cli, root_cli_index, new_execution_config).await; + + swarm + .wait_for_all_nodes_to_catchup_to_future(Duration::from_secs(MAX_WAIT_SECS), 8) + .await + .unwrap(); + + assert_eq!( + get_last_txn_name(&rest_client).await, + Some("state_checkpoint") + ); + } +} diff --git a/testsuite/smoke-test/src/utils.rs b/testsuite/smoke-test/src/utils.rs index d3c8de2a3de6a1..0918e657ade9a4 100644 --- a/testsuite/smoke-test/src/utils.rs +++ b/testsuite/smoke-test/src/utils.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use aptos_rest_client::Client; -use aptos_types::on_chain_config::OnChainConsensusConfig; +use aptos_types::on_chain_config::{OnChainConsensusConfig, OnChainExecutionConfig}; use move_core_types::language_storage::CORE_CODE_ADDRESS; pub(crate) async fn get_current_version(rest_client: &Client) -> u64 { @@ -27,3 +27,17 @@ pub(crate) async fn get_current_consensus_config(rest_client: &Client) -> OnChai ) .unwrap() } + +pub(crate) async fn get_current_execution_config(rest_client: &Client) -> OnChainExecutionConfig { + bcs::from_bytes( + &rest_client + .get_account_resource_bcs::>( + CORE_CODE_ADDRESS, + "0x1::execution_config::ExecutionConfig", + ) + .await + .unwrap() + .into_inner(), + ) + .unwrap() +} diff --git a/types/src/on_chain_config/execution_config.rs b/types/src/on_chain_config/execution_config.rs index d1cbc1c63a15e9..69f8ea20cad5f0 100644 --- a/types/src/on_chain_config/execution_config.rs +++ b/types/src/on_chain_config/execution_config.rs @@ -75,17 +75,7 @@ impl OnChainExecutionConfig { module_conflict_window_size: 1, entry_fun_conflict_window_size: 2, }, - block_gas_limit_type: BlockGasLimitType::ComplexLimitV1 { - effective_block_gas_limit: 30000, - execution_gas_effective_multiplier: 1, - io_gas_effective_multiplier: 1, - conflict_penalty_window: 9, - use_granular_resource_group_conflicts: false, - use_module_publishing_block_conflict: true, - block_output_limit: Some(5 * 1024 * 1024), - include_user_txn_size_in_block_output: true, - add_block_limit_outcome_onchain: true, - }, + block_gas_limit_type: BlockGasLimitType::default_for_genesis(), transaction_deduper_type: TransactionDeduperType::TxnHashAndAuthenticatorV1, }) } @@ -97,6 +87,22 @@ impl OnChainExecutionConfig { } } +impl BlockGasLimitType { + pub fn default_for_genesis() -> Self { + BlockGasLimitType::ComplexLimitV1 { + effective_block_gas_limit: 30000, + execution_gas_effective_multiplier: 1, + io_gas_effective_multiplier: 1, + conflict_penalty_window: 9, + use_granular_resource_group_conflicts: false, + use_module_publishing_block_conflict: true, + block_output_limit: Some(5 * 1024 * 1024), + include_user_txn_size_in_block_output: true, + add_block_limit_outcome_onchain: true, + } + } +} + impl OnChainConfig for OnChainExecutionConfig { const MODULE_IDENTIFIER: &'static str = "execution_config"; const TYPE_IDENTIFIER: &'static str = "ExecutionConfig"; diff --git a/types/src/transaction/mod.rs b/types/src/transaction/mod.rs index 23a3563ba488a3..4311e02c9e3774 100644 --- a/types/src/transaction/mod.rs +++ b/types/src/transaction/mod.rs @@ -1492,7 +1492,7 @@ impl TransactionInfoV0 { self.state_change_hash } - pub fn is_state_checkpoint(&self) -> bool { + pub fn has_state_checkpoint_hash(&self) -> bool { self.state_checkpoint_hash().is_some() } @@ -1607,8 +1607,8 @@ impl TransactionToCommit { &self.transaction_info } - pub fn is_state_checkpoint(&self) -> bool { - self.transaction_info().is_state_checkpoint() + pub fn has_state_checkpoint_hash(&self) -> bool { + self.transaction_info().has_state_checkpoint_hash() } #[cfg(any(test, feature = "fuzzing"))]