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