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