sui_adapter_v1/
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
9    use crate::execution_mode::{self, ExecutionMode};
10    use move_binary_format::CompiledModule;
11    use move_vm_runtime::move_vm::MoveVM;
12    use std::sync::Arc;
13    use sui_types::balance::{
14        BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
15        BALANCE_MODULE_NAME,
16    };
17    use sui_types::execution_params::ExecutionOrEarlyError;
18    use sui_types::gas_coin::GAS;
19    use sui_types::messages_checkpoint::CheckpointTimestamp;
20    use sui_types::metrics::ExecutionMetrics;
21    use sui_types::object::OBJECT_START_VERSION;
22    use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
23    use tracing::{info, instrument, trace, warn};
24
25    use crate::programmable_transactions;
26    use crate::type_layout_resolver::TypeLayoutResolver;
27    use crate::{gas_charger::GasCharger, temporary_store::TemporaryStore};
28    use sui_protocol_config::{check_limit_by_meter, LimitThresholdCrossed, ProtocolConfig};
29    use sui_types::authenticator_state::{
30        AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME, AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME,
31        AUTHENTICATOR_STATE_MODULE_NAME, AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
32    };
33    use sui_types::clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME};
34    use sui_types::committee::EpochId;
35    use sui_types::effects::TransactionEffects;
36    use sui_types::error::{ExecutionError, ExecutionErrorTrait};
37    use sui_types::execution_status::{ExecutionErrorKind, ExecutionStatus};
38    use sui_types::gas::GasCostSummary;
39    use sui_types::gas::SuiGasStatus;
40    use sui_types::inner_temporary_store::InnerTemporaryStore;
41    use sui_types::storage::BackingStore;
42    #[cfg(msim)]
43    use sui_types::sui_system_state::advance_epoch_result_injection::maybe_modify_result_legacy;
44    use sui_types::sui_system_state::{AdvanceEpochParams, ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME};
45    use sui_types::transaction::CheckedInputObjects;
46    use sui_types::transaction::{
47        Argument, AuthenticatorStateExpire, AuthenticatorStateUpdate, CallArg, ChangeEpoch,
48        Command, EndOfEpochTransactionKind, GenesisTransaction, ObjectArg, ProgrammableTransaction,
49        TransactionKind,
50    };
51    use sui_types::{
52        base_types::{ObjectRef, SuiAddress, TransactionDigest, TxContext},
53        object::{Object, ObjectInner},
54        sui_system_state::{ADVANCE_EPOCH_FUNCTION_NAME, SUI_SYSTEM_MODULE_NAME},
55        SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_FRAMEWORK_ADDRESS, SUI_FRAMEWORK_PACKAGE_ID,
56        SUI_SYSTEM_PACKAGE_ID,
57    };
58
59    #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
60    pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
61        store: &dyn BackingStore,
62        input_objects: CheckedInputObjects,
63        gas_coins: Vec<ObjectRef>,
64        gas_status: SuiGasStatus,
65        transaction_kind: TransactionKind,
66        transaction_signer: SuiAddress,
67        transaction_digest: TransactionDigest,
68        move_vm: &Arc<MoveVM>,
69        epoch_id: &EpochId,
70        epoch_timestamp_ms: u64,
71        protocol_config: &ProtocolConfig,
72        metrics: Arc<ExecutionMetrics>,
73        enable_expensive_checks: bool,
74        execution_params: ExecutionOrEarlyError,
75    ) -> (
76        InnerTemporaryStore,
77        SuiGasStatus,
78        TransactionEffects,
79        Result<Mode::ExecutionResults, ExecutionError>,
80    ) {
81        let input_objects = input_objects.into_inner();
82        let shared_object_refs = input_objects.filter_shared_objects();
83        let receiving_objects = transaction_kind.receiving_objects();
84        let mut transaction_dependencies = input_objects.transaction_dependencies();
85        let mut temporary_store = TemporaryStore::new(
86            store,
87            input_objects,
88            receiving_objects,
89            transaction_digest,
90            protocol_config,
91        );
92
93        let mut gas_charger =
94            GasCharger::new(transaction_digest, gas_coins, gas_status, protocol_config);
95
96        let mut tx_ctx = TxContext::new_from_components(
97            &transaction_signer,
98            &transaction_digest,
99            epoch_id,
100            epoch_timestamp_ms,
101            // Those values are unused in execution versions before 3 (or latest)
102            1,
103            1,
104            1_000_000,
105            None,
106            protocol_config,
107        );
108
109        let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
110
111        let (gas_cost_summary, execution_result) = execute_transaction::<Mode>(
112            &mut temporary_store,
113            transaction_kind,
114            &mut gas_charger,
115            &mut tx_ctx,
116            move_vm,
117            protocol_config,
118            metrics,
119            enable_expensive_checks,
120            execution_params,
121        );
122
123        let status = if let Err(error) = &execution_result {
124            ExecutionStatus::new_failure(error.to_execution_failure())
125        } else {
126            ExecutionStatus::Success
127        };
128
129        #[skip_checked_arithmetic]
130        trace!(
131            tx_digest = ?transaction_digest,
132            computation_gas_cost = gas_cost_summary.computation_cost,
133            storage_gas_cost = gas_cost_summary.storage_cost,
134            storage_gas_rebate = gas_cost_summary.storage_rebate,
135            "Finished execution of transaction with status {:?}",
136            status
137        );
138
139        // Remove from dependencies the generic hash
140        transaction_dependencies.remove(&TransactionDigest::genesis_marker());
141
142        if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
143            temporary_store
144                .check_ownership_invariants(&transaction_signer, &mut gas_charger, is_epoch_change)
145                .unwrap()
146        } // else, in dev inspect mode and anything goes--don't check
147
148        let (inner, effects) = temporary_store.into_effects(
149            shared_object_refs,
150            &transaction_digest,
151            transaction_dependencies,
152            gas_cost_summary,
153            status,
154            &mut gas_charger,
155            *epoch_id,
156        );
157        (
158            inner,
159            gas_charger.into_gas_status(),
160            effects,
161            execution_result,
162        )
163    }
164
165    pub fn execute_genesis_state_update(
166        store: &dyn BackingStore,
167        protocol_config: &ProtocolConfig,
168        metrics: Arc<ExecutionMetrics>,
169        move_vm: &Arc<MoveVM>,
170        tx_context: &mut TxContext,
171        input_objects: CheckedInputObjects,
172        pt: ProgrammableTransaction,
173    ) -> Result<InnerTemporaryStore, ExecutionError> {
174        let input_objects = input_objects.into_inner();
175        let mut temporary_store = TemporaryStore::new(
176            store,
177            input_objects,
178            vec![],
179            tx_context.digest(),
180            protocol_config,
181        );
182        let mut gas_charger = GasCharger::new_unmetered(tx_context.digest());
183        programmable_transactions::execution::execute::<execution_mode::Genesis>(
184            protocol_config,
185            metrics,
186            move_vm,
187            &mut temporary_store,
188            tx_context,
189            &mut gas_charger,
190            pt,
191        )?;
192        temporary_store.update_object_version_and_prev_tx();
193        Ok(temporary_store.into_inner())
194    }
195
196    #[instrument(name = "tx_execute", level = "debug", skip_all)]
197    fn execute_transaction<Mode: ExecutionMode>(
198        temporary_store: &mut TemporaryStore<'_>,
199        transaction_kind: TransactionKind,
200        gas_charger: &mut GasCharger,
201        tx_ctx: &mut TxContext,
202        move_vm: &Arc<MoveVM>,
203        protocol_config: &ProtocolConfig,
204        metrics: Arc<ExecutionMetrics>,
205        enable_expensive_checks: bool,
206        execution_params: ExecutionOrEarlyError,
207    ) -> (
208        GasCostSummary,
209        Result<Mode::ExecutionResults, ExecutionError>,
210    ) {
211        gas_charger.smash_gas(temporary_store);
212
213        // At this point no charges have been applied yet
214        debug_assert!(
215            gas_charger.no_charges(),
216            "No gas charges must be applied yet"
217        );
218
219        let is_genesis_tx = matches!(transaction_kind, TransactionKind::Genesis(_));
220        let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();
221
222        // We must charge object read here during transaction execution, because if this fails
223        // we must still ensure an effect is committed and all objects versions incremented
224        let result = gas_charger.charge_input_objects(temporary_store);
225        let mut result = result.and_then(|()| {
226            let mut execution_result = match execution_params {
227                ExecutionOrEarlyError::Err(early_execution_error) => {
228                    Err(ExecutionError::new(early_execution_error, None))
229                }
230                ExecutionOrEarlyError::Ok(()) => execution_loop::<Mode>(
231                    temporary_store,
232                    transaction_kind,
233                    tx_ctx,
234                    move_vm,
235                    gas_charger,
236                    protocol_config,
237                    metrics.clone(),
238                ),
239            };
240
241            let meter_check = check_meter_limit(
242                temporary_store,
243                gas_charger,
244                protocol_config,
245                metrics.clone(),
246            );
247            if let Err(e) = meter_check {
248                execution_result = Err(e);
249            }
250
251            if execution_result.is_ok() {
252                let gas_check = check_written_objects_limit(
253                    temporary_store,
254                    gas_charger,
255                    protocol_config,
256                    metrics,
257                );
258                if let Err(e) = gas_check {
259                    execution_result = Err(e);
260                }
261            }
262
263            execution_result
264        });
265
266        let cost_summary = gas_charger.charge_gas(temporary_store, &mut result);
267        // For advance epoch transaction, we need to provide epoch rewards and rebates as extra
268        // information provided to check_sui_conserved, because we mint rewards, and burn
269        // the rebates. We also need to pass in the unmetered_storage_rebate because storage
270        // rebate is not reflected in the storage_rebate of gas summary. This is a bit confusing.
271        // We could probably clean up the code a bit.
272        // Put all the storage rebate accumulated in the system transaction
273        // to the 0x5 object so that it's not lost.
274        temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
275
276        if let Err(e) = run_conservation_checks::<Mode>(
277            temporary_store,
278            gas_charger,
279            tx_ctx,
280            move_vm,
281            protocol_config.simple_conservation_checks(),
282            enable_expensive_checks,
283            &cost_summary,
284            is_genesis_tx,
285            advance_epoch_gas_summary,
286        ) {
287            // FIXME: we cannot fail the transaction if this is an epoch change transaction.
288            result = Err(e);
289        }
290
291        (cost_summary, result)
292    }
293
294    #[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
295    fn run_conservation_checks<Mode: ExecutionMode>(
296        temporary_store: &mut TemporaryStore<'_>,
297        gas_charger: &mut GasCharger,
298        tx_ctx: &mut TxContext,
299        move_vm: &Arc<MoveVM>,
300        simple_conservation_checks: bool,
301        enable_expensive_checks: bool,
302        cost_summary: &GasCostSummary,
303        is_genesis_tx: bool,
304        advance_epoch_gas_summary: Option<(u64, u64)>,
305    ) -> Result<(), ExecutionError> {
306        let mut result: std::result::Result<(), sui_types::error::ExecutionError> = Ok(());
307        if !is_genesis_tx && !Mode::skip_conservation_checks() {
308            // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
309            let conservation_result = {
310                temporary_store
311                    .check_sui_conserved(simple_conservation_checks, cost_summary)
312                    .and_then(|()| {
313                        if enable_expensive_checks {
314                            // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
315                            let mut layout_resolver =
316                                TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
317                            temporary_store.check_sui_conserved_expensive(
318                                cost_summary,
319                                advance_epoch_gas_summary,
320                                &mut layout_resolver,
321                            )
322                        } else {
323                            Ok(())
324                        }
325                    })
326            };
327            if let Err(conservation_err) = conservation_result {
328                // conservation violated. try to avoid panic by dumping all writes, charging for gas, re-checking
329                // conservation, and surfacing an aborted transaction with an invariant violation if all of that works
330                result = Err(conservation_err);
331                gas_charger.reset(temporary_store);
332                gas_charger.charge_gas(temporary_store, &mut result);
333                // check conservation once more
334                if let Err(recovery_err) = {
335                    temporary_store
336                        .check_sui_conserved(simple_conservation_checks, cost_summary)
337                        .and_then(|()| {
338                            if enable_expensive_checks {
339                                // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
340                                let mut layout_resolver =
341                                    TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
342                                temporary_store.check_sui_conserved_expensive(
343                                    cost_summary,
344                                    advance_epoch_gas_summary,
345                                    &mut layout_resolver,
346                                )
347                            } else {
348                                Ok(())
349                            }
350                        })
351                } {
352                    // if we still fail, it's a problem with gas
353                    // charging that happens even in the "aborted" case--no other option but panic.
354                    // we will create or destroy SUI otherwise
355                    panic!(
356                        "SUI conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
357                        tx_ctx.digest(),
358                        recovery_err,
359                        gas_charger.summary()
360                    )
361                }
362            }
363        } // else, we're in the genesis transaction which mints the SUI supply, and hence does not satisfy SUI conservation, or
364          // we're in the non-production dev inspect mode which allows us to violate conservation
365        result
366    }
367
368    #[instrument(name = "check_meter_limit", level = "debug", skip_all)]
369    fn check_meter_limit(
370        temporary_store: &mut TemporaryStore<'_>,
371        gas_charger: &mut GasCharger,
372        protocol_config: &ProtocolConfig,
373        metrics: Arc<ExecutionMetrics>,
374    ) -> Result<(), ExecutionError> {
375        let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
376
377        // Check if a limit threshold was crossed.
378        // For metered transactions, there is not soft limit.
379        // For system transactions, we allow a soft limit with alerting, and a hard limit where we terminate
380        match check_limit_by_meter!(
381            !gas_charger.is_unmetered(),
382            effects_estimated_size,
383            protocol_config.max_serialized_tx_effects_size_bytes(),
384            protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
385            metrics.limits_metrics.excessive_estimated_effects_size
386        ) {
387            LimitThresholdCrossed::None => Ok(()),
388            LimitThresholdCrossed::Soft(_, limit) => {
389                warn!(
390                    effects_estimated_size = effects_estimated_size,
391                    soft_limit = limit,
392                    "Estimated transaction effects size crossed soft limit",
393                );
394                Ok(())
395            }
396            LimitThresholdCrossed::Hard(_, lim) => Err(ExecutionError::new_with_source(
397                ExecutionErrorKind::EffectsTooLarge {
398                    current_size: effects_estimated_size as u64,
399                    max_size: lim as u64,
400                },
401                "Transaction effects are too large",
402            )),
403        }
404    }
405
406    #[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
407    fn check_written_objects_limit(
408        temporary_store: &mut TemporaryStore<'_>,
409        gas_charger: &mut GasCharger,
410        protocol_config: &ProtocolConfig,
411        metrics: Arc<ExecutionMetrics>,
412    ) -> Result<(), ExecutionError> {
413        if let (Some(normal_lim), Some(system_lim)) = (
414            protocol_config.max_size_written_objects_as_option(),
415            protocol_config.max_size_written_objects_system_tx_as_option(),
416        ) {
417            let written_objects_size = temporary_store.written_objects_size();
418
419            match check_limit_by_meter!(
420                !gas_charger.is_unmetered(),
421                written_objects_size,
422                normal_lim,
423                system_lim,
424                metrics.limits_metrics.excessive_written_objects_size
425            ) {
426                LimitThresholdCrossed::None => (),
427                LimitThresholdCrossed::Soft(_, limit) => {
428                    warn!(
429                        written_objects_size = written_objects_size,
430                        soft_limit = limit,
431                        "Written objects size crossed soft limit",
432                    )
433                }
434                LimitThresholdCrossed::Hard(_, lim) => {
435                    return Err(ExecutionError::new_with_source(
436                        ExecutionErrorKind::WrittenObjectsTooLarge {
437                            current_size: written_objects_size as u64,
438                            max_size: lim as u64,
439                        },
440                        "Written objects size crossed hard limit",
441                    ));
442                }
443            };
444        }
445
446        Ok(())
447    }
448
449    #[instrument(level = "debug", skip_all)]
450    fn execution_loop<Mode: ExecutionMode>(
451        temporary_store: &mut TemporaryStore<'_>,
452        transaction_kind: TransactionKind,
453        tx_ctx: &mut TxContext,
454        move_vm: &Arc<MoveVM>,
455        gas_charger: &mut GasCharger,
456        protocol_config: &ProtocolConfig,
457        metrics: Arc<ExecutionMetrics>,
458    ) -> Result<Mode::ExecutionResults, ExecutionError> {
459        let result = match transaction_kind {
460            TransactionKind::ChangeEpoch(change_epoch) => {
461                let builder = ProgrammableTransactionBuilder::new();
462                advance_epoch(
463                    builder,
464                    change_epoch,
465                    temporary_store,
466                    tx_ctx,
467                    move_vm,
468                    gas_charger,
469                    protocol_config,
470                    metrics,
471                )?;
472                Ok(Mode::empty_results())
473            }
474            TransactionKind::Genesis(GenesisTransaction { objects }) => {
475                if tx_ctx.epoch() != 0 {
476                    panic!("BUG: Genesis Transactions can only be executed in epoch 0");
477                }
478
479                for genesis_object in objects {
480                    match genesis_object {
481                        sui_types::transaction::GenesisObject::RawObject { data, owner } => {
482                            let object = ObjectInner {
483                                data,
484                                owner,
485                                previous_transaction: tx_ctx.digest(),
486                                storage_rebate: 0,
487                            };
488                            temporary_store.create_object(object.into());
489                        }
490                    }
491                }
492                Ok(Mode::empty_results())
493            }
494            TransactionKind::ConsensusCommitPrologue(prologue) => {
495                setup_consensus_commit(
496                    prologue.commit_timestamp_ms,
497                    temporary_store,
498                    tx_ctx,
499                    move_vm,
500                    gas_charger,
501                    protocol_config,
502                    metrics,
503                )
504                .expect("ConsensusCommitPrologue cannot fail");
505                Ok(Mode::empty_results())
506            }
507            TransactionKind::ConsensusCommitPrologueV2(prologue) => {
508                setup_consensus_commit(
509                    prologue.commit_timestamp_ms,
510                    temporary_store,
511                    tx_ctx,
512                    move_vm,
513                    gas_charger,
514                    protocol_config,
515                    metrics,
516                )
517                .expect("ConsensusCommitPrologue cannot fail");
518                Ok(Mode::empty_results())
519            }
520            TransactionKind::ConsensusCommitPrologueV3(prologue) => {
521                setup_consensus_commit(
522                    prologue.commit_timestamp_ms,
523                    temporary_store,
524                    tx_ctx,
525                    move_vm,
526                    gas_charger,
527                    protocol_config,
528                    metrics,
529                )
530                .expect("ConsensusCommitPrologue cannot fail");
531                Ok(Mode::empty_results())
532            }
533            TransactionKind::ConsensusCommitPrologueV4(prologue) => {
534                setup_consensus_commit(
535                    prologue.commit_timestamp_ms,
536                    temporary_store,
537                    tx_ctx,
538                    move_vm,
539                    gas_charger,
540                    protocol_config,
541                    metrics,
542                )
543                .expect("ConsensusCommitPrologue cannot fail");
544                Ok(Mode::empty_results())
545            }
546            TransactionKind::ProgrammableTransaction(pt) => {
547                programmable_transactions::execution::execute::<Mode>(
548                    protocol_config,
549                    metrics,
550                    move_vm,
551                    temporary_store,
552                    tx_ctx,
553                    gas_charger,
554                    pt,
555                )
556            }
557            TransactionKind::EndOfEpochTransaction(txns) => {
558                let mut builder = ProgrammableTransactionBuilder::new();
559                let len = txns.len();
560                for (i, tx) in txns.into_iter().enumerate() {
561                    match tx {
562                        EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
563                            assert_eq!(i, len - 1);
564                            advance_epoch(
565                                builder,
566                                change_epoch,
567                                temporary_store,
568                                tx_ctx,
569                                move_vm,
570                                gas_charger,
571                                protocol_config,
572                                metrics,
573                            )?;
574                            return Ok(Mode::empty_results());
575                        }
576                        EndOfEpochTransactionKind::AuthenticatorStateCreate => {
577                            assert!(protocol_config.enable_jwk_consensus_updates());
578                            builder = setup_authenticator_state_create(builder);
579                        }
580                        EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
581                            assert!(protocol_config.enable_jwk_consensus_updates());
582
583                            // TODO: it would be nice if a failure of this function didn't cause
584                            // safe mode.
585                            builder = setup_authenticator_state_expire(builder, expire);
586                        }
587                        EndOfEpochTransactionKind::RandomnessStateCreate => {
588                            panic!(
589                                "EndOfEpochTransactionKind::RandomnessStateCreate should not exist in v1"
590                            );
591                        }
592                        EndOfEpochTransactionKind::DenyListStateCreate => {
593                            panic!(
594                                "EndOfEpochTransactionKind::CoinDenyListStateCreate should not exist in v1"
595                            );
596                        }
597                        EndOfEpochTransactionKind::BridgeStateCreate(_) => {
598                            panic!(
599                                "EndOfEpochTransactionKind::BridgeStateCreate should not exist in v1"
600                            );
601                        }
602                        EndOfEpochTransactionKind::BridgeCommitteeInit(_) => {
603                            panic!(
604                                "EndOfEpochTransactionKind::BridgeCommitteeInit should not exist in v1"
605                            );
606                        }
607                        EndOfEpochTransactionKind::StoreExecutionTimeObservations(_) => {
608                            panic!(
609                                "EndOfEpochTransactionKind::StoreExecutionTimeEstimates should not exist in v1"
610                            );
611                        }
612                        EndOfEpochTransactionKind::AccumulatorRootCreate => {
613                            panic!(
614                                "EndOfEpochTransactionKind::AccumulatorRootCreate should not exist in v1"
615                            );
616                        }
617                        EndOfEpochTransactionKind::WriteAccumulatorStorageCost(_) => {
618                            panic!(
619                                "EndOfEpochTransactionKind::WriteAccumulatorStorageCost should not exist in v1"
620                            );
621                        }
622                        EndOfEpochTransactionKind::CoinRegistryCreate => {
623                            panic!(
624                                "EndOfEpochTransactionKind::CoinRegistryCreate should not exist in v1"
625                            );
626                        }
627                        EndOfEpochTransactionKind::DisplayRegistryCreate => {
628                            panic!(
629                                "EndOfEpochTransactionKind::DisplayRegistryCreate should not exist in v1"
630                            );
631                        }
632                        EndOfEpochTransactionKind::AddressAliasStateCreate => {
633                            panic!(
634                                "EndOfEpochTransactionKind::AddressAliasStateCreate should not exist in v1"
635                            );
636                        }
637                    }
638                }
639                unreachable!(
640                    "EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list"
641                )
642            }
643            TransactionKind::AuthenticatorStateUpdate(auth_state_update) => {
644                setup_authenticator_state_update(
645                    auth_state_update,
646                    temporary_store,
647                    tx_ctx,
648                    move_vm,
649                    gas_charger,
650                    protocol_config,
651                    metrics,
652                )?;
653                Ok(Mode::empty_results())
654            }
655            TransactionKind::RandomnessStateUpdate(_) => {
656                panic!("RandomnessStateUpdate should not exist in v1");
657            }
658            TransactionKind::ProgrammableSystemTransaction(_) => {
659                panic!("ProgrammableSystemTransaction should not exist in execution layer v1");
660            }
661        }?;
662        temporary_store.check_execution_results_consistency()?;
663        Ok(result)
664    }
665
666    fn mint_epoch_rewards_in_pt(
667        builder: &mut ProgrammableTransactionBuilder,
668        params: &AdvanceEpochParams,
669    ) -> (Argument, Argument) {
670        // Create storage rewards.
671        let storage_charge_arg = builder
672            .input(CallArg::Pure(
673                bcs::to_bytes(&params.storage_charge).unwrap(),
674            ))
675            .unwrap();
676        let storage_rewards = builder.programmable_move_call(
677            SUI_FRAMEWORK_PACKAGE_ID,
678            BALANCE_MODULE_NAME.to_owned(),
679            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
680            vec![GAS::type_tag()],
681            vec![storage_charge_arg],
682        );
683
684        // Create computation rewards.
685        let computation_charge_arg = builder
686            .input(CallArg::Pure(
687                bcs::to_bytes(&params.computation_charge).unwrap(),
688            ))
689            .unwrap();
690        let computation_rewards = builder.programmable_move_call(
691            SUI_FRAMEWORK_PACKAGE_ID,
692            BALANCE_MODULE_NAME.to_owned(),
693            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
694            vec![GAS::type_tag()],
695            vec![computation_charge_arg],
696        );
697        (storage_rewards, computation_rewards)
698    }
699
700    pub fn construct_advance_epoch_pt(
701        mut builder: ProgrammableTransactionBuilder,
702        params: &AdvanceEpochParams,
703    ) -> Result<ProgrammableTransaction, ExecutionError> {
704        // Step 1: Create storage and computation rewards.
705        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
706
707        // Step 2: Advance the epoch.
708        let mut arguments = vec![storage_rewards, computation_rewards];
709        let call_arg_arguments = vec![
710            CallArg::SUI_SYSTEM_MUT,
711            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
712            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
713            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
714            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
715            CallArg::Pure(bcs::to_bytes(&params.storage_fund_reinvest_rate).unwrap()),
716            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()),
717            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()),
718        ]
719        .into_iter()
720        .map(|a| builder.input(a))
721        .collect::<Result<_, _>>();
722
723        assert_invariant!(
724            call_arg_arguments.is_ok(),
725            "Unable to generate args for advance_epoch transaction!"
726        );
727
728        arguments.append(&mut call_arg_arguments.unwrap());
729
730        info!("Call arguments to advance_epoch transaction: {:?}", params);
731
732        let storage_rebates = builder.programmable_move_call(
733            SUI_SYSTEM_PACKAGE_ID,
734            SUI_SYSTEM_MODULE_NAME.to_owned(),
735            ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
736            vec![],
737            arguments,
738        );
739
740        // Step 3: Destroy the storage rebates.
741        builder.programmable_move_call(
742            SUI_FRAMEWORK_PACKAGE_ID,
743            BALANCE_MODULE_NAME.to_owned(),
744            BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
745            vec![GAS::type_tag()],
746            vec![storage_rebates],
747        );
748        Ok(builder.finish())
749    }
750
751    pub fn construct_advance_epoch_safe_mode_pt(
752        params: &AdvanceEpochParams,
753        protocol_config: &ProtocolConfig,
754    ) -> Result<ProgrammableTransaction, ExecutionError> {
755        let mut builder = ProgrammableTransactionBuilder::new();
756        // Step 1: Create storage and computation rewards.
757        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
758
759        // Step 2: Advance the epoch.
760        let mut arguments = vec![storage_rewards, computation_rewards];
761
762        let mut args = vec![
763            CallArg::SUI_SYSTEM_MUT,
764            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
765            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
766            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
767            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
768        ];
769
770        if protocol_config.get_advance_epoch_start_time_in_safe_mode() {
771            args.push(CallArg::Pure(
772                bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap(),
773            ));
774        }
775
776        let call_arg_arguments = args
777            .into_iter()
778            .map(|a| builder.input(a))
779            .collect::<Result<_, _>>();
780
781        assert_invariant!(
782            call_arg_arguments.is_ok(),
783            "Unable to generate args for advance_epoch transaction!"
784        );
785
786        arguments.append(&mut call_arg_arguments.unwrap());
787
788        info!("Call arguments to advance_epoch transaction: {:?}", params);
789
790        builder.programmable_move_call(
791            SUI_SYSTEM_PACKAGE_ID,
792            SUI_SYSTEM_MODULE_NAME.to_owned(),
793            ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME.to_owned(),
794            vec![],
795            arguments,
796        );
797
798        Ok(builder.finish())
799    }
800
801    fn advance_epoch(
802        builder: ProgrammableTransactionBuilder,
803        change_epoch: ChangeEpoch,
804        temporary_store: &mut TemporaryStore<'_>,
805        tx_ctx: &mut TxContext,
806        move_vm: &Arc<MoveVM>,
807        gas_charger: &mut GasCharger,
808        protocol_config: &ProtocolConfig,
809        metrics: Arc<ExecutionMetrics>,
810    ) -> Result<(), ExecutionError> {
811        let params = AdvanceEpochParams {
812            epoch: change_epoch.epoch,
813            next_protocol_version: change_epoch.protocol_version,
814            storage_charge: change_epoch.storage_charge,
815            computation_charge: change_epoch.computation_charge,
816            storage_rebate: change_epoch.storage_rebate,
817            non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
818            storage_fund_reinvest_rate: protocol_config.storage_fund_reinvest_rate(),
819            reward_slashing_rate: protocol_config.reward_slashing_rate(),
820            epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
821        };
822        let advance_epoch_pt = construct_advance_epoch_pt(builder, &params)?;
823        let result = programmable_transactions::execution::execute::<execution_mode::System>(
824            protocol_config,
825            metrics.clone(),
826            move_vm,
827            temporary_store,
828            tx_ctx,
829            gas_charger,
830            advance_epoch_pt,
831        );
832
833        #[cfg(msim)]
834        let result = maybe_modify_result_legacy(result, change_epoch.epoch);
835
836        if result.is_err() {
837            tracing::error!(
838                "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx data: {:?}",
839                result.as_ref().err(),
840                temporary_store.objects(),
841                change_epoch,
842            );
843            temporary_store.drop_writes();
844            // Must reset the storage rebate since we are re-executing.
845            gas_charger.reset_storage_cost_and_rebate();
846
847            if protocol_config.get_advance_epoch_start_time_in_safe_mode() {
848                temporary_store.advance_epoch_safe_mode(&params, protocol_config);
849            } else {
850                let advance_epoch_safe_mode_pt =
851                    construct_advance_epoch_safe_mode_pt(&params, protocol_config)?;
852                programmable_transactions::execution::execute::<execution_mode::System>(
853                    protocol_config,
854                    metrics.clone(),
855                    move_vm,
856                    temporary_store,
857                    tx_ctx,
858                    gas_charger,
859                    advance_epoch_safe_mode_pt,
860                )
861                .expect("Advance epoch with safe mode must succeed");
862            }
863        }
864
865        let binary_config = protocol_config.binary_config(None);
866        for (version, modules, dependencies) in change_epoch.system_packages.into_iter() {
867            let deserialized_modules: Vec<_> = modules
868                .iter()
869                .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
870                .collect();
871
872            if version == OBJECT_START_VERSION {
873                let package_id = deserialized_modules.first().unwrap().address();
874                info!("adding new system package {package_id}");
875
876                let publish_pt = {
877                    let mut b = ProgrammableTransactionBuilder::new();
878                    b.command(Command::Publish(modules, dependencies));
879                    b.finish()
880                };
881
882                programmable_transactions::execution::execute::<execution_mode::System>(
883                    protocol_config,
884                    metrics.clone(),
885                    move_vm,
886                    temporary_store,
887                    tx_ctx,
888                    gas_charger,
889                    publish_pt,
890                )
891                .expect("System Package Publish must succeed");
892            } else {
893                let mut new_package = Object::new_system_package(
894                    &deserialized_modules,
895                    version,
896                    dependencies,
897                    tx_ctx.digest(),
898                );
899
900                info!(
901                    "upgraded system package {:?}",
902                    new_package.compute_object_reference()
903                );
904
905                // Decrement the version before writing the package so that the store can record the
906                // version growing by one in the effects.
907                new_package
908                    .data
909                    .try_as_package_mut()
910                    .unwrap()
911                    .decrement_version();
912
913                // upgrade of a previously existing framework module
914                temporary_store.upgrade_system_package(new_package);
915            }
916        }
917
918        Ok(())
919    }
920
921    /// Perform metadata updates in preparation for the transactions in the upcoming checkpoint:
922    ///
923    /// - Set the timestamp for the `Clock` shared object from the timestamp in the header from
924    ///   consensus.
925    fn setup_consensus_commit(
926        consensus_commit_timestamp_ms: CheckpointTimestamp,
927        temporary_store: &mut TemporaryStore<'_>,
928        tx_ctx: &mut TxContext,
929        move_vm: &Arc<MoveVM>,
930        gas_charger: &mut GasCharger,
931        protocol_config: &ProtocolConfig,
932        metrics: Arc<ExecutionMetrics>,
933    ) -> Result<(), ExecutionError> {
934        let pt = {
935            let mut builder = ProgrammableTransactionBuilder::new();
936            let res = builder.move_call(
937                SUI_FRAMEWORK_ADDRESS.into(),
938                CLOCK_MODULE_NAME.to_owned(),
939                CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
940                vec![],
941                vec![
942                    CallArg::CLOCK_MUT,
943                    CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
944                ],
945            );
946            assert_invariant!(
947                res.is_ok(),
948                "Unable to generate consensus_commit_prologue transaction!"
949            );
950            builder.finish()
951        };
952        programmable_transactions::execution::execute::<execution_mode::System>(
953            protocol_config,
954            metrics,
955            move_vm,
956            temporary_store,
957            tx_ctx,
958            gas_charger,
959            pt,
960        )
961    }
962
963    fn setup_authenticator_state_create(
964        mut builder: ProgrammableTransactionBuilder,
965    ) -> ProgrammableTransactionBuilder {
966        builder
967            .move_call(
968                SUI_FRAMEWORK_ADDRESS.into(),
969                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
970                AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
971                vec![],
972                vec![],
973            )
974            .expect("Unable to generate authenticator_state_create transaction!");
975        builder
976    }
977
978    fn setup_authenticator_state_update(
979        update: AuthenticatorStateUpdate,
980        temporary_store: &mut TemporaryStore<'_>,
981        tx_ctx: &mut TxContext,
982        move_vm: &Arc<MoveVM>,
983        gas_charger: &mut GasCharger,
984        protocol_config: &ProtocolConfig,
985        metrics: Arc<ExecutionMetrics>,
986    ) -> Result<(), ExecutionError> {
987        let pt = {
988            let mut builder = ProgrammableTransactionBuilder::new();
989            let res = builder.move_call(
990                SUI_FRAMEWORK_ADDRESS.into(),
991                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
992                AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
993                vec![],
994                vec![
995                    CallArg::Object(ObjectArg::SharedObject {
996                        id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
997                        initial_shared_version: update.authenticator_obj_initial_shared_version,
998                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
999                    }),
1000                    CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
1001                ],
1002            );
1003            assert_invariant!(
1004                res.is_ok(),
1005                "Unable to generate authenticator_state_update transaction!"
1006            );
1007            builder.finish()
1008        };
1009        programmable_transactions::execution::execute::<execution_mode::System>(
1010            protocol_config,
1011            metrics,
1012            move_vm,
1013            temporary_store,
1014            tx_ctx,
1015            gas_charger,
1016            pt,
1017        )
1018    }
1019
1020    fn setup_authenticator_state_expire(
1021        mut builder: ProgrammableTransactionBuilder,
1022        expire: AuthenticatorStateExpire,
1023    ) -> ProgrammableTransactionBuilder {
1024        builder
1025            .move_call(
1026                SUI_FRAMEWORK_ADDRESS.into(),
1027                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1028                AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
1029                vec![],
1030                vec![
1031                    CallArg::Object(ObjectArg::SharedObject {
1032                        id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1033                        initial_shared_version: expire.authenticator_obj_initial_shared_version,
1034                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1035                    }),
1036                    CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
1037                ],
1038            )
1039            .expect("Unable to generate authenticator_state_expire transaction!");
1040        builder
1041    }
1042}