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