sui_adapter_v0/
execution_engine.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7mod checked {
8    use crate::execution_mode::{self, ExecutionMode};
9    use crate::gas_charger::GasCharger;
10    use crate::programmable_transactions;
11    use crate::temporary_store::TemporaryStore;
12    use crate::type_layout_resolver::TypeLayoutResolver;
13    use move_binary_format::CompiledModule;
14    use move_vm_runtime::move_vm::MoveVM;
15    use std::sync::Arc;
16    use sui_protocol_config::{check_limit_by_meter, LimitThresholdCrossed, ProtocolConfig};
17    use sui_types::balance::{
18        BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
19        BALANCE_MODULE_NAME,
20    };
21    use sui_types::clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME};
22    use sui_types::committee::EpochId;
23    use sui_types::effects::TransactionEffects;
24    use sui_types::error::{ExecutionError, ExecutionErrorTrait};
25    use sui_types::execution_params::ExecutionOrEarlyError;
26    use sui_types::execution_status::{ExecutionErrorKind, ExecutionStatus};
27    use sui_types::gas::GasCostSummary;
28    use sui_types::gas::SuiGasStatus;
29    use sui_types::gas_coin::GAS;
30    use sui_types::inner_temporary_store::InnerTemporaryStore;
31    use sui_types::messages_checkpoint::CheckpointTimestamp;
32    use sui_types::metrics::ExecutionMetrics;
33    use sui_types::object::OBJECT_START_VERSION;
34    use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
35    use sui_types::storage::BackingStore;
36    use sui_types::storage::WriteKind;
37    #[cfg(msim)]
38    use sui_types::sui_system_state::advance_epoch_result_injection::maybe_modify_result_legacy;
39    use sui_types::sui_system_state::{AdvanceEpochParams, ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME};
40    use sui_types::transaction::CheckedInputObjects;
41    use sui_types::transaction::{
42        Argument, CallArg, ChangeEpoch, Command, GenesisTransaction, ProgrammableTransaction,
43        TransactionKind,
44    };
45    use sui_types::{
46        base_types::{ObjectRef, SuiAddress, TransactionDigest, TxContext},
47        object::{Object, ObjectInner},
48        sui_system_state::{ADVANCE_EPOCH_FUNCTION_NAME, SUI_SYSTEM_MODULE_NAME},
49        SUI_FRAMEWORK_ADDRESS,
50    };
51    use sui_types::{SUI_FRAMEWORK_PACKAGE_ID, SUI_SYSTEM_PACKAGE_ID};
52    use tracing::{info, instrument, trace, warn};
53
54    #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
55    pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
56        store: &dyn BackingStore,
57        input_objects: CheckedInputObjects,
58        gas_coins: Vec<ObjectRef>,
59        gas_status: SuiGasStatus,
60        transaction_kind: TransactionKind,
61        transaction_signer: SuiAddress,
62        transaction_digest: TransactionDigest,
63        move_vm: &Arc<MoveVM>,
64        epoch_id: &EpochId,
65        epoch_timestamp_ms: u64,
66        protocol_config: &ProtocolConfig,
67        metrics: Arc<ExecutionMetrics>,
68        enable_expensive_checks: bool,
69        execution_params: ExecutionOrEarlyError,
70    ) -> (
71        InnerTemporaryStore,
72        SuiGasStatus,
73        TransactionEffects,
74        Result<Mode::ExecutionResults, ExecutionError>,
75    ) {
76        let input_objects = input_objects.into_inner();
77        let shared_object_refs = input_objects.filter_shared_objects();
78        let mut transaction_dependencies = input_objects.transaction_dependencies();
79        let mut temporary_store =
80            TemporaryStore::new(store, input_objects, transaction_digest, protocol_config);
81        let mut gas_charger =
82            GasCharger::new(transaction_digest, gas_coins, gas_status, protocol_config);
83
84        let mut tx_ctx = TxContext::new_from_components(
85            &transaction_signer,
86            &transaction_digest,
87            epoch_id,
88            epoch_timestamp_ms,
89            // Those values are unused in execution versions before 3 (or latest)
90            1,
91            1,
92            1_000_000,
93            None,
94            protocol_config,
95        );
96
97        let is_epoch_change = matches!(transaction_kind, TransactionKind::ChangeEpoch(_));
98
99        let (gas_cost_summary, execution_result) = execute_transaction::<Mode>(
100            &mut temporary_store,
101            transaction_kind,
102            &mut gas_charger,
103            &mut tx_ctx,
104            move_vm,
105            protocol_config,
106            metrics,
107            enable_expensive_checks,
108            execution_params,
109        );
110
111        let status = if let Err(error) = &execution_result {
112            ExecutionStatus::new_failure(error.to_execution_failure())
113        } else {
114            ExecutionStatus::Success
115        };
116
117        #[skip_checked_arithmetic]
118        trace!(
119            tx_digest = ?transaction_digest,
120            computation_gas_cost = gas_cost_summary.computation_cost,
121            storage_gas_cost = gas_cost_summary.storage_cost,
122            storage_gas_rebate = gas_cost_summary.storage_rebate,
123            "Finished execution of transaction with status {:?}",
124            status
125        );
126
127        // Remove from dependencies the generic hash
128        transaction_dependencies.remove(&TransactionDigest::genesis_marker());
129
130        if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
131            temporary_store
132                .check_ownership_invariants(&transaction_signer, &mut gas_charger, is_epoch_change)
133                .unwrap()
134        } // else, in dev inspect mode and anything goes--don't check
135
136        let (inner, effects) = temporary_store.to_effects(
137            shared_object_refs,
138            &transaction_digest,
139            transaction_dependencies.into_iter().collect(),
140            gas_cost_summary,
141            status,
142            &mut gas_charger,
143            *epoch_id,
144        );
145        (
146            inner,
147            gas_charger.into_gas_status(),
148            effects,
149            execution_result,
150        )
151    }
152
153    pub fn execute_genesis_state_update(
154        store: &dyn BackingStore,
155        protocol_config: &ProtocolConfig,
156        metrics: Arc<ExecutionMetrics>,
157        move_vm: &Arc<MoveVM>,
158        tx_context: &mut TxContext,
159        input_objects: CheckedInputObjects,
160        pt: ProgrammableTransaction,
161    ) -> Result<InnerTemporaryStore, ExecutionError> {
162        let input_objects = input_objects.into_inner();
163        let mut temporary_store =
164            TemporaryStore::new(store, input_objects, tx_context.digest(), protocol_config);
165        let mut gas_charger = GasCharger::new_unmetered(tx_context.digest());
166        programmable_transactions::execution::execute::<execution_mode::Genesis>(
167            protocol_config,
168            metrics,
169            move_vm,
170            &mut temporary_store,
171            tx_context,
172            &mut gas_charger,
173            pt,
174        )?;
175        temporary_store.update_object_version_and_prev_tx();
176        Ok(temporary_store.into_inner())
177    }
178
179    #[instrument(name = "tx_execute", level = "debug", skip_all)]
180    fn execute_transaction<Mode: ExecutionMode>(
181        temporary_store: &mut TemporaryStore<'_>,
182        transaction_kind: TransactionKind,
183        gas_charger: &mut GasCharger,
184        tx_ctx: &mut TxContext,
185        move_vm: &Arc<MoveVM>,
186        protocol_config: &ProtocolConfig,
187        metrics: Arc<ExecutionMetrics>,
188        enable_expensive_checks: bool,
189        execution_params: ExecutionOrEarlyError,
190    ) -> (
191        GasCostSummary,
192        Result<Mode::ExecutionResults, ExecutionError>,
193    ) {
194        gas_charger.smash_gas(temporary_store);
195
196        // At this point no charges have been applied yet
197        debug_assert!(
198            gas_charger.no_charges(),
199            "No gas charges must be applied yet"
200        );
201        let is_genesis_tx = matches!(transaction_kind, TransactionKind::Genesis(_));
202        let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();
203
204        // We must charge object read here during transaction execution, because if this fails
205        // we must still ensure an effect is committed and all objects versions incremented
206        let result = gas_charger.charge_input_objects(temporary_store);
207        let mut result = result.and_then(|()| {
208            let mut execution_result = match execution_params {
209                ExecutionOrEarlyError::Err(early_execution_error) => {
210                    Err(ExecutionError::new(early_execution_error, None))
211                }
212                ExecutionOrEarlyError::Ok(()) => execution_loop::<Mode>(
213                    temporary_store,
214                    transaction_kind,
215                    tx_ctx,
216                    move_vm,
217                    gas_charger,
218                    protocol_config,
219                    metrics.clone(),
220                ),
221            };
222
223            let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
224
225            // Check if a limit threshold was crossed.
226            // For metered transactions, there is not soft limit.
227            // For system transactions, we allow a soft limit with alerting, and a hard limit where we terminate
228            match check_limit_by_meter!(
229                !gas_charger.is_unmetered(),
230                effects_estimated_size,
231                protocol_config.max_serialized_tx_effects_size_bytes(),
232                protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
233                metrics.limits_metrics.excessive_estimated_effects_size
234            ) {
235                LimitThresholdCrossed::None => (),
236                LimitThresholdCrossed::Soft(_, limit) => {
237                    warn!(
238                        effects_estimated_size = effects_estimated_size,
239                        soft_limit = limit,
240                        "Estimated transaction effects size crossed soft limit",
241                    )
242                }
243                LimitThresholdCrossed::Hard(_, lim) => {
244                    execution_result = Err(ExecutionError::new_with_source(
245                        ExecutionErrorKind::EffectsTooLarge {
246                            current_size: effects_estimated_size as u64,
247                            max_size: lim as u64,
248                        },
249                        "Transaction effects are too large",
250                    ))
251                }
252            };
253            if execution_result.is_ok() {
254                // This limit is only present in Version 3 and up, so use this to gate it
255                if let (Some(normal_lim), Some(system_lim)) = (
256                    protocol_config.max_size_written_objects_as_option(),
257                    protocol_config.max_size_written_objects_system_tx_as_option(),
258                ) {
259                    let written_objects_size = temporary_store.written_objects_size();
260
261                    match check_limit_by_meter!(
262                        !gas_charger.is_unmetered(),
263                        written_objects_size,
264                        normal_lim,
265                        system_lim,
266                        metrics.limits_metrics.excessive_written_objects_size
267                    ) {
268                        LimitThresholdCrossed::None => (),
269                        LimitThresholdCrossed::Soft(_, limit) => {
270                            warn!(
271                                written_objects_size = written_objects_size,
272                                soft_limit = limit,
273                                "Written objects size crossed soft limit",
274                            )
275                        }
276                        LimitThresholdCrossed::Hard(_, lim) => {
277                            execution_result = Err(ExecutionError::new_with_source(
278                                ExecutionErrorKind::WrittenObjectsTooLarge {
279                                    current_size: written_objects_size as u64,
280                                    max_size: lim as u64,
281                                },
282                                "Written objects size crossed hard limit",
283                            ))
284                        }
285                    };
286                }
287            }
288
289            execution_result
290        });
291
292        let cost_summary = gas_charger.charge_gas(temporary_store, &mut result);
293        // For advance epoch transaction, we need to provide epoch rewards and rebates as extra
294        // information provided to check_sui_conserved, because we mint rewards, and burn
295        // the rebates. We also need to pass in the unmetered_storage_rebate because storage
296        // rebate is not reflected in the storage_rebate of gas summary. This is a bit confusing.
297        // We could probably clean up the code a bit.
298        // Put all the storage rebate accumulated in the system transaction
299        // to the 0x5 object so that it's not lost.
300        temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
301
302        // === begin SUI conservation checks ===
303        if !is_genesis_tx && !Mode::allow_arbitrary_values() {
304            // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
305            let conservation_result = {
306                let mut layout_resolver =
307                    TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
308                temporary_store.check_sui_conserved(
309                    &cost_summary,
310                    advance_epoch_gas_summary,
311                    &mut layout_resolver,
312                    enable_expensive_checks,
313                )
314            };
315            if let Err(conservation_err) = conservation_result {
316                // conservation violated. try to avoid panic by dumping all writes, charging for gas, re-checking
317                // conservation, and surfacing an aborted transaction with an invariant violation if all of that works
318                result = Err(conservation_err);
319                gas_charger.reset(temporary_store);
320                gas_charger.charge_gas(temporary_store, &mut result);
321                // check conservation once more
322                let mut layout_resolver =
323                    TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
324                if let Err(recovery_err) = temporary_store.check_sui_conserved(
325                    &cost_summary,
326                    advance_epoch_gas_summary,
327                    &mut layout_resolver,
328                    enable_expensive_checks,
329                ) {
330                    // if we still fail, it's a problem with gas
331                    // charging that happens even in the "aborted" case--no other option but panic.
332                    // we will create or destroy SUI otherwise
333                    panic!(
334                        "SUI conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
335                        tx_ctx.digest(),
336                        recovery_err,
337                        gas_charger.summary()
338                    )
339                }
340            }
341        } // else, we're in the genesis transaction which mints the SUI supply, and hence does not satisfy SUI conservation, or
342          // we're in the non-production dev inspect mode which allows us to violate conservation
343          // === end SUI conservation checks ===
344        (cost_summary, result)
345    }
346
347    fn execution_loop<Mode: ExecutionMode>(
348        temporary_store: &mut TemporaryStore<'_>,
349        transaction_kind: TransactionKind,
350        tx_ctx: &mut TxContext,
351        move_vm: &Arc<MoveVM>,
352        gas_charger: &mut GasCharger,
353        protocol_config: &ProtocolConfig,
354        metrics: Arc<ExecutionMetrics>,
355    ) -> Result<Mode::ExecutionResults, ExecutionError> {
356        match transaction_kind {
357            TransactionKind::ChangeEpoch(change_epoch) => {
358                advance_epoch(
359                    change_epoch,
360                    temporary_store,
361                    tx_ctx,
362                    move_vm,
363                    gas_charger,
364                    protocol_config,
365                    metrics,
366                )?;
367                Ok(Mode::empty_results())
368            }
369            TransactionKind::Genesis(GenesisTransaction { objects }) => {
370                if tx_ctx.epoch() != 0 {
371                    panic!("BUG: Genesis Transactions can only be executed in epoch 0");
372                }
373
374                for genesis_object in objects {
375                    match genesis_object {
376                        sui_types::transaction::GenesisObject::RawObject { data, owner } => {
377                            let object = ObjectInner {
378                                data,
379                                owner,
380                                previous_transaction: tx_ctx.digest(),
381                                storage_rebate: 0,
382                            };
383                            temporary_store.write_object(object.into(), WriteKind::Create);
384                        }
385                    }
386                }
387                Ok(Mode::empty_results())
388            }
389            TransactionKind::ConsensusCommitPrologue(prologue) => {
390                setup_consensus_commit(
391                    prologue.commit_timestamp_ms,
392                    temporary_store,
393                    tx_ctx,
394                    move_vm,
395                    gas_charger,
396                    protocol_config,
397                    metrics,
398                )
399                .expect("ConsensusCommitPrologue cannot fail");
400                Ok(Mode::empty_results())
401            }
402            TransactionKind::ConsensusCommitPrologueV2(prologue) => {
403                setup_consensus_commit(
404                    prologue.commit_timestamp_ms,
405                    temporary_store,
406                    tx_ctx,
407                    move_vm,
408                    gas_charger,
409                    protocol_config,
410                    metrics,
411                )
412                .expect("ConsensusCommitPrologue cannot fail");
413                Ok(Mode::empty_results())
414            }
415            TransactionKind::ConsensusCommitPrologueV3(prologue) => {
416                setup_consensus_commit(
417                    prologue.commit_timestamp_ms,
418                    temporary_store,
419                    tx_ctx,
420                    move_vm,
421                    gas_charger,
422                    protocol_config,
423                    metrics,
424                )
425                .expect("ConsensusCommitPrologue cannot fail");
426                Ok(Mode::empty_results())
427            }
428            TransactionKind::ConsensusCommitPrologueV4(prologue) => {
429                setup_consensus_commit(
430                    prologue.commit_timestamp_ms,
431                    temporary_store,
432                    tx_ctx,
433                    move_vm,
434                    gas_charger,
435                    protocol_config,
436                    metrics,
437                )
438                .expect("ConsensusCommitPrologue cannot fail");
439                Ok(Mode::empty_results())
440            }
441            TransactionKind::ProgrammableTransaction(pt) => {
442                programmable_transactions::execution::execute::<Mode>(
443                    protocol_config,
444                    metrics,
445                    move_vm,
446                    temporary_store,
447                    tx_ctx,
448                    gas_charger,
449                    pt,
450                )
451            }
452            TransactionKind::AuthenticatorStateUpdate(_) => {
453                panic!("AuthenticatorStateUpdate should not exist in execution layer v0");
454            }
455            TransactionKind::RandomnessStateUpdate(_) => {
456                panic!("RandomnessStateUpdate should not exist in execution layer v0");
457            }
458            TransactionKind::EndOfEpochTransaction(_) => {
459                panic!("EndOfEpochTransaction should not exist in execution layer v0");
460            }
461            TransactionKind::ProgrammableSystemTransaction(_) => {
462                panic!("ProgrammableSystemTransaction should not exist in execution layer v0");
463            }
464        }
465    }
466
467    fn mint_epoch_rewards_in_pt(
468        builder: &mut ProgrammableTransactionBuilder,
469        params: &AdvanceEpochParams,
470    ) -> (Argument, Argument) {
471        // Create storage rewards.
472        let storage_charge_arg = builder
473            .input(CallArg::Pure(
474                bcs::to_bytes(&params.storage_charge).unwrap(),
475            ))
476            .unwrap();
477        let storage_rewards = builder.programmable_move_call(
478            SUI_FRAMEWORK_PACKAGE_ID,
479            BALANCE_MODULE_NAME.to_owned(),
480            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
481            vec![GAS::type_tag()],
482            vec![storage_charge_arg],
483        );
484
485        // Create computation rewards.
486        let computation_charge_arg = builder
487            .input(CallArg::Pure(
488                bcs::to_bytes(&params.computation_charge).unwrap(),
489            ))
490            .unwrap();
491        let computation_rewards = builder.programmable_move_call(
492            SUI_FRAMEWORK_PACKAGE_ID,
493            BALANCE_MODULE_NAME.to_owned(),
494            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
495            vec![GAS::type_tag()],
496            vec![computation_charge_arg],
497        );
498        (storage_rewards, computation_rewards)
499    }
500
501    pub fn construct_advance_epoch_pt(
502        params: &AdvanceEpochParams,
503    ) -> Result<ProgrammableTransaction, ExecutionError> {
504        let mut builder = ProgrammableTransactionBuilder::new();
505        // Step 1: Create storage and computation rewards.
506        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
507
508        // Step 2: Advance the epoch.
509        let mut arguments = vec![storage_rewards, computation_rewards];
510        let call_arg_arguments = vec![
511            CallArg::SUI_SYSTEM_MUT,
512            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
513            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
514            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
515            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
516            CallArg::Pure(bcs::to_bytes(&params.storage_fund_reinvest_rate).unwrap()),
517            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()),
518            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()),
519        ]
520        .into_iter()
521        .map(|a| builder.input(a))
522        .collect::<Result<_, _>>();
523
524        assert_invariant!(
525            call_arg_arguments.is_ok(),
526            "Unable to generate args for advance_epoch transaction!"
527        );
528
529        arguments.append(&mut call_arg_arguments.unwrap());
530
531        info!("Call arguments to advance_epoch transaction: {:?}", params);
532
533        let storage_rebates = builder.programmable_move_call(
534            SUI_SYSTEM_PACKAGE_ID,
535            SUI_SYSTEM_MODULE_NAME.to_owned(),
536            ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
537            vec![],
538            arguments,
539        );
540
541        // Step 3: Destroy the storage rebates.
542        builder.programmable_move_call(
543            SUI_FRAMEWORK_PACKAGE_ID,
544            BALANCE_MODULE_NAME.to_owned(),
545            BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
546            vec![GAS::type_tag()],
547            vec![storage_rebates],
548        );
549        Ok(builder.finish())
550    }
551
552    pub fn construct_advance_epoch_safe_mode_pt(
553        params: &AdvanceEpochParams,
554        protocol_config: &ProtocolConfig,
555    ) -> Result<ProgrammableTransaction, ExecutionError> {
556        let mut builder = ProgrammableTransactionBuilder::new();
557        // Step 1: Create storage and computation rewards.
558        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
559
560        // Step 2: Advance the epoch.
561        let mut arguments = vec![storage_rewards, computation_rewards];
562
563        let mut args = vec![
564            CallArg::SUI_SYSTEM_MUT,
565            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
566            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
567            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
568            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
569        ];
570
571        if protocol_config.get_advance_epoch_start_time_in_safe_mode() {
572            args.push(CallArg::Pure(
573                bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap(),
574            ));
575        }
576
577        let call_arg_arguments = args
578            .into_iter()
579            .map(|a| builder.input(a))
580            .collect::<Result<_, _>>();
581
582        assert_invariant!(
583            call_arg_arguments.is_ok(),
584            "Unable to generate args for advance_epoch transaction!"
585        );
586
587        arguments.append(&mut call_arg_arguments.unwrap());
588
589        info!("Call arguments to advance_epoch transaction: {:?}", params);
590
591        builder.programmable_move_call(
592            SUI_SYSTEM_PACKAGE_ID,
593            SUI_SYSTEM_MODULE_NAME.to_owned(),
594            ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME.to_owned(),
595            vec![],
596            arguments,
597        );
598
599        Ok(builder.finish())
600    }
601
602    fn advance_epoch(
603        change_epoch: ChangeEpoch,
604        temporary_store: &mut TemporaryStore<'_>,
605        tx_ctx: &mut TxContext,
606        move_vm: &Arc<MoveVM>,
607        gas_charger: &mut GasCharger,
608        protocol_config: &ProtocolConfig,
609        metrics: Arc<ExecutionMetrics>,
610    ) -> Result<(), ExecutionError> {
611        let params = AdvanceEpochParams {
612            epoch: change_epoch.epoch,
613            next_protocol_version: change_epoch.protocol_version,
614            storage_charge: change_epoch.storage_charge,
615            computation_charge: change_epoch.computation_charge,
616            storage_rebate: change_epoch.storage_rebate,
617            non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
618            storage_fund_reinvest_rate: protocol_config.storage_fund_reinvest_rate(),
619            reward_slashing_rate: protocol_config.reward_slashing_rate(),
620            epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
621        };
622        let advance_epoch_pt = construct_advance_epoch_pt(&params)?;
623        let result = programmable_transactions::execution::execute::<execution_mode::System>(
624            protocol_config,
625            metrics.clone(),
626            move_vm,
627            temporary_store,
628            tx_ctx,
629            gas_charger,
630            advance_epoch_pt,
631        );
632
633        #[cfg(msim)]
634        let result = maybe_modify_result_legacy(result, change_epoch.epoch);
635
636        if result.is_err() {
637            tracing::error!(
638            "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx data: {:?}",
639            result.as_ref().err(),
640            temporary_store.objects(),
641            change_epoch,
642        );
643            temporary_store.drop_writes();
644            // Must reset the storage rebate since we are re-executing.
645            gas_charger.reset_storage_cost_and_rebate();
646
647            if protocol_config.get_advance_epoch_start_time_in_safe_mode() {
648                temporary_store.advance_epoch_safe_mode(&params, protocol_config);
649            } else {
650                let advance_epoch_safe_mode_pt =
651                    construct_advance_epoch_safe_mode_pt(&params, protocol_config)?;
652                programmable_transactions::execution::execute::<execution_mode::System>(
653                    protocol_config,
654                    metrics.clone(),
655                    move_vm,
656                    temporary_store,
657                    tx_ctx,
658                    gas_charger,
659                    advance_epoch_safe_mode_pt,
660                )
661                .expect("Advance epoch with safe mode must succeed");
662            }
663        }
664
665        let binary_config = protocol_config.binary_config(None);
666        for (version, modules, dependencies) in change_epoch.system_packages.into_iter() {
667            let deserialized_modules: Vec<_> = modules
668                .iter()
669                .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
670                .collect();
671
672            if version == OBJECT_START_VERSION {
673                let package_id = deserialized_modules.first().unwrap().address();
674                info!("adding new system package {package_id}");
675
676                let publish_pt = {
677                    let mut b = ProgrammableTransactionBuilder::new();
678                    b.command(Command::Publish(modules, dependencies));
679                    b.finish()
680                };
681
682                programmable_transactions::execution::execute::<execution_mode::System>(
683                    protocol_config,
684                    metrics.clone(),
685                    move_vm,
686                    temporary_store,
687                    tx_ctx,
688                    gas_charger,
689                    publish_pt,
690                )
691                .expect("System Package Publish must succeed");
692            } else {
693                let mut new_package = Object::new_system_package(
694                    &deserialized_modules,
695                    version,
696                    dependencies,
697                    tx_ctx.digest(),
698                );
699
700                info!(
701                    "upgraded system package {:?}",
702                    new_package.compute_object_reference()
703                );
704
705                // Decrement the version before writing the package so that the store can record the
706                // version growing by one in the effects.
707                new_package
708                    .data
709                    .try_as_package_mut()
710                    .unwrap()
711                    .decrement_version();
712
713                // upgrade of a previously existing framework module
714                temporary_store.write_object(new_package, WriteKind::Mutate);
715            }
716        }
717
718        Ok(())
719    }
720
721    /// Perform metadata updates in preparation for the transactions in the upcoming checkpoint:
722    ///
723    /// - Set the timestamp for the `Clock` shared object from the timestamp in the header from
724    ///   consensus.
725    fn setup_consensus_commit(
726        consensus_commit_timestamp_ms: CheckpointTimestamp,
727        temporary_store: &mut TemporaryStore<'_>,
728        tx_ctx: &mut TxContext,
729        move_vm: &Arc<MoveVM>,
730        gas_charger: &mut GasCharger,
731        protocol_config: &ProtocolConfig,
732        metrics: Arc<ExecutionMetrics>,
733    ) -> Result<(), ExecutionError> {
734        let pt = {
735            let mut builder = ProgrammableTransactionBuilder::new();
736            let res = builder.move_call(
737                SUI_FRAMEWORK_ADDRESS.into(),
738                CLOCK_MODULE_NAME.to_owned(),
739                CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
740                vec![],
741                vec![
742                    CallArg::CLOCK_MUT,
743                    CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
744                ],
745            );
746            assert_invariant!(
747                res.is_ok(),
748                "Unable to generate consensus_commit_prologue transaction!"
749            );
750            builder.finish()
751        };
752        programmable_transactions::execution::execute::<execution_mode::System>(
753            protocol_config,
754            metrics,
755            move_vm,
756            temporary_store,
757            tx_ctx,
758            gas_charger,
759            pt,
760        )
761    }
762}