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::CoinRegistryCreate => {
667                            panic!("EndOfEpochTransactionKind::CoinRegistryCreate should not exist in v2");
668                        }
669                        EndOfEpochTransactionKind::DisplayRegistryCreate => {
670                            panic!("EndOfEpochTransactionKind::DisplayRegistryCreate should not exist in v2");
671                        }
672                        EndOfEpochTransactionKind::AddressAliasStateCreate => {
673                            panic!("EndOfEpochTransactionKind::AddressAliasStateCreate should not exist in v2");
674                        }
675                    }
676                }
677                unreachable!("EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list")
678            }
679            TransactionKind::AuthenticatorStateUpdate(auth_state_update) => {
680                setup_authenticator_state_update(
681                    auth_state_update,
682                    temporary_store,
683                    tx_ctx,
684                    move_vm,
685                    gas_charger,
686                    protocol_config,
687                    metrics,
688                )?;
689                Ok(Mode::empty_results())
690            }
691            TransactionKind::RandomnessStateUpdate(randomness_state_update) => {
692                setup_randomness_state_update(
693                    randomness_state_update,
694                    temporary_store,
695                    tx_ctx,
696                    move_vm,
697                    gas_charger,
698                    protocol_config,
699                    metrics,
700                )?;
701                Ok(Mode::empty_results())
702            }
703            TransactionKind::ProgrammableSystemTransaction(_) => {
704                panic!("ProgrammableSystemTransaction should not exist in execution layer v2");
705            }
706        }?;
707        temporary_store.check_execution_results_consistency()?;
708        Ok(result)
709    }
710
711    fn mint_epoch_rewards_in_pt(
712        builder: &mut ProgrammableTransactionBuilder,
713        params: &AdvanceEpochParams,
714    ) -> (Argument, Argument) {
715        // Create storage rewards.
716        let storage_charge_arg = builder
717            .input(CallArg::Pure(
718                bcs::to_bytes(&params.storage_charge).unwrap(),
719            ))
720            .unwrap();
721        let storage_rewards = builder.programmable_move_call(
722            SUI_FRAMEWORK_PACKAGE_ID,
723            BALANCE_MODULE_NAME.to_owned(),
724            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
725            vec![GAS::type_tag()],
726            vec![storage_charge_arg],
727        );
728
729        // Create computation rewards.
730        let computation_charge_arg = builder
731            .input(CallArg::Pure(
732                bcs::to_bytes(&params.computation_charge).unwrap(),
733            ))
734            .unwrap();
735        let computation_rewards = builder.programmable_move_call(
736            SUI_FRAMEWORK_PACKAGE_ID,
737            BALANCE_MODULE_NAME.to_owned(),
738            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
739            vec![GAS::type_tag()],
740            vec![computation_charge_arg],
741        );
742        (storage_rewards, computation_rewards)
743    }
744
745    pub fn construct_advance_epoch_pt(
746        mut builder: ProgrammableTransactionBuilder,
747        params: &AdvanceEpochParams,
748    ) -> Result<ProgrammableTransaction, ExecutionError> {
749        // Step 1: Create storage and computation rewards.
750        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
751
752        // Step 2: Advance the epoch.
753        let mut arguments = vec![storage_rewards, computation_rewards];
754        let call_arg_arguments = vec![
755            CallArg::SUI_SYSTEM_MUT,
756            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
757            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
758            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
759            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
760            CallArg::Pure(bcs::to_bytes(&params.storage_fund_reinvest_rate).unwrap()),
761            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()),
762            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()),
763        ]
764        .into_iter()
765        .map(|a| builder.input(a))
766        .collect::<Result<_, _>>();
767
768        assert_invariant!(
769            call_arg_arguments.is_ok(),
770            "Unable to generate args for advance_epoch transaction!"
771        );
772
773        arguments.append(&mut call_arg_arguments.unwrap());
774
775        info!("Call arguments to advance_epoch transaction: {:?}", params);
776
777        let storage_rebates = builder.programmable_move_call(
778            SUI_SYSTEM_PACKAGE_ID,
779            SUI_SYSTEM_MODULE_NAME.to_owned(),
780            ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
781            vec![],
782            arguments,
783        );
784
785        // Step 3: Destroy the storage rebates.
786        builder.programmable_move_call(
787            SUI_FRAMEWORK_PACKAGE_ID,
788            BALANCE_MODULE_NAME.to_owned(),
789            BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
790            vec![GAS::type_tag()],
791            vec![storage_rebates],
792        );
793        Ok(builder.finish())
794    }
795
796    pub fn construct_advance_epoch_safe_mode_pt(
797        params: &AdvanceEpochParams,
798        protocol_config: &ProtocolConfig,
799    ) -> Result<ProgrammableTransaction, ExecutionError> {
800        let mut builder = ProgrammableTransactionBuilder::new();
801        // Step 1: Create storage and computation rewards.
802        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
803
804        // Step 2: Advance the epoch.
805        let mut arguments = vec![storage_rewards, computation_rewards];
806
807        let mut args = vec![
808            CallArg::SUI_SYSTEM_MUT,
809            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
810            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
811            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
812            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
813        ];
814
815        if protocol_config.get_advance_epoch_start_time_in_safe_mode() {
816            args.push(CallArg::Pure(
817                bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap(),
818            ));
819        }
820
821        let call_arg_arguments = args
822            .into_iter()
823            .map(|a| builder.input(a))
824            .collect::<Result<_, _>>();
825
826        assert_invariant!(
827            call_arg_arguments.is_ok(),
828            "Unable to generate args for advance_epoch transaction!"
829        );
830
831        arguments.append(&mut call_arg_arguments.unwrap());
832
833        info!("Call arguments to advance_epoch transaction: {:?}", params);
834
835        builder.programmable_move_call(
836            SUI_SYSTEM_PACKAGE_ID,
837            SUI_SYSTEM_MODULE_NAME.to_owned(),
838            ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME.to_owned(),
839            vec![],
840            arguments,
841        );
842
843        Ok(builder.finish())
844    }
845
846    fn advance_epoch(
847        builder: ProgrammableTransactionBuilder,
848        change_epoch: ChangeEpoch,
849        temporary_store: &mut TemporaryStore<'_>,
850        tx_ctx: &mut TxContext,
851        move_vm: &Arc<MoveVM>,
852        gas_charger: &mut GasCharger,
853        protocol_config: &ProtocolConfig,
854        metrics: Arc<LimitsMetrics>,
855    ) -> Result<(), ExecutionError> {
856        let params = AdvanceEpochParams {
857            epoch: change_epoch.epoch,
858            next_protocol_version: change_epoch.protocol_version,
859            storage_charge: change_epoch.storage_charge,
860            computation_charge: change_epoch.computation_charge,
861            storage_rebate: change_epoch.storage_rebate,
862            non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
863            storage_fund_reinvest_rate: protocol_config.storage_fund_reinvest_rate(),
864            reward_slashing_rate: protocol_config.reward_slashing_rate(),
865            epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
866        };
867        let advance_epoch_pt = construct_advance_epoch_pt(builder, &params)?;
868        let result = programmable_transactions::execution::execute::<execution_mode::System>(
869            protocol_config,
870            metrics.clone(),
871            move_vm,
872            temporary_store,
873            tx_ctx,
874            gas_charger,
875            advance_epoch_pt,
876        );
877
878        #[cfg(msim)]
879        let result = maybe_modify_result_legacy(result, change_epoch.epoch);
880
881        if result.is_err() {
882            tracing::error!(
883            "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx data: {:?}",
884            result.as_ref().err(),
885            temporary_store.objects(),
886            change_epoch,
887        );
888            temporary_store.drop_writes();
889            // Must reset the storage rebate since we are re-executing.
890            gas_charger.reset_storage_cost_and_rebate();
891
892            if protocol_config.get_advance_epoch_start_time_in_safe_mode() {
893                temporary_store.advance_epoch_safe_mode(&params, protocol_config);
894            } else {
895                let advance_epoch_safe_mode_pt =
896                    construct_advance_epoch_safe_mode_pt(&params, protocol_config)?;
897                programmable_transactions::execution::execute::<execution_mode::System>(
898                    protocol_config,
899                    metrics.clone(),
900                    move_vm,
901                    temporary_store,
902                    tx_ctx,
903                    gas_charger,
904                    advance_epoch_safe_mode_pt,
905                )
906                .expect("Advance epoch with safe mode must succeed");
907            }
908        }
909
910        let binary_config = protocol_config.binary_config(None);
911        for (version, modules, dependencies) in change_epoch.system_packages.into_iter() {
912            let deserialized_modules: Vec<_> = modules
913                .iter()
914                .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
915                .collect();
916
917            if version == OBJECT_START_VERSION {
918                let package_id = deserialized_modules.first().unwrap().address();
919                info!("adding new system package {package_id}");
920
921                let publish_pt = {
922                    let mut b = ProgrammableTransactionBuilder::new();
923                    b.command(Command::Publish(modules, dependencies));
924                    b.finish()
925                };
926
927                programmable_transactions::execution::execute::<execution_mode::System>(
928                    protocol_config,
929                    metrics.clone(),
930                    move_vm,
931                    temporary_store,
932                    tx_ctx,
933                    gas_charger,
934                    publish_pt,
935                )
936                .expect("System Package Publish must succeed");
937            } else {
938                let mut new_package = Object::new_system_package(
939                    &deserialized_modules,
940                    version,
941                    dependencies,
942                    tx_ctx.digest(),
943                );
944
945                info!(
946                    "upgraded system package {:?}",
947                    new_package.compute_object_reference()
948                );
949
950                // Decrement the version before writing the package so that the store can record the
951                // version growing by one in the effects.
952                new_package
953                    .data
954                    .try_as_package_mut()
955                    .unwrap()
956                    .decrement_version();
957
958                // upgrade of a previously existing framework module
959                temporary_store.upgrade_system_package(new_package);
960            }
961        }
962
963        Ok(())
964    }
965
966    /// Perform metadata updates in preparation for the transactions in the upcoming checkpoint:
967    ///
968    /// - Set the timestamp for the `Clock` shared object from the timestamp in the header from
969    ///   consensus.
970    fn setup_consensus_commit(
971        consensus_commit_timestamp_ms: CheckpointTimestamp,
972        temporary_store: &mut TemporaryStore<'_>,
973        tx_ctx: &mut TxContext,
974        move_vm: &Arc<MoveVM>,
975        gas_charger: &mut GasCharger,
976        protocol_config: &ProtocolConfig,
977        metrics: Arc<LimitsMetrics>,
978    ) -> Result<(), ExecutionError> {
979        let pt = {
980            let mut builder = ProgrammableTransactionBuilder::new();
981            let res = builder.move_call(
982                SUI_FRAMEWORK_ADDRESS.into(),
983                CLOCK_MODULE_NAME.to_owned(),
984                CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
985                vec![],
986                vec![
987                    CallArg::CLOCK_MUT,
988                    CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
989                ],
990            );
991            assert_invariant!(
992                res.is_ok(),
993                "Unable to generate consensus_commit_prologue transaction!"
994            );
995            builder.finish()
996        };
997        programmable_transactions::execution::execute::<execution_mode::System>(
998            protocol_config,
999            metrics,
1000            move_vm,
1001            temporary_store,
1002            tx_ctx,
1003            gas_charger,
1004            pt,
1005        )
1006    }
1007
1008    fn setup_authenticator_state_create(
1009        mut builder: ProgrammableTransactionBuilder,
1010    ) -> ProgrammableTransactionBuilder {
1011        builder
1012            .move_call(
1013                SUI_FRAMEWORK_ADDRESS.into(),
1014                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1015                AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
1016                vec![],
1017                vec![],
1018            )
1019            .expect("Unable to generate authenticator_state_create transaction!");
1020        builder
1021    }
1022
1023    fn setup_randomness_state_create(
1024        mut builder: ProgrammableTransactionBuilder,
1025    ) -> ProgrammableTransactionBuilder {
1026        builder
1027            .move_call(
1028                SUI_FRAMEWORK_ADDRESS.into(),
1029                RANDOMNESS_MODULE_NAME.to_owned(),
1030                RANDOMNESS_STATE_CREATE_FUNCTION_NAME.to_owned(),
1031                vec![],
1032                vec![],
1033            )
1034            .expect("Unable to generate randomness_state_create transaction!");
1035        builder
1036    }
1037
1038    fn setup_authenticator_state_update(
1039        update: AuthenticatorStateUpdate,
1040        temporary_store: &mut TemporaryStore<'_>,
1041        tx_ctx: &mut TxContext,
1042        move_vm: &Arc<MoveVM>,
1043        gas_charger: &mut GasCharger,
1044        protocol_config: &ProtocolConfig,
1045        metrics: Arc<LimitsMetrics>,
1046    ) -> Result<(), ExecutionError> {
1047        let pt = {
1048            let mut builder = ProgrammableTransactionBuilder::new();
1049            let res = builder.move_call(
1050                SUI_FRAMEWORK_ADDRESS.into(),
1051                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1052                AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1053                vec![],
1054                vec![
1055                    CallArg::Object(ObjectArg::SharedObject {
1056                        id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1057                        initial_shared_version: update.authenticator_obj_initial_shared_version,
1058                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1059                    }),
1060                    CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
1061                ],
1062            );
1063            assert_invariant!(
1064                res.is_ok(),
1065                "Unable to generate authenticator_state_update transaction!"
1066            );
1067            builder.finish()
1068        };
1069        programmable_transactions::execution::execute::<execution_mode::System>(
1070            protocol_config,
1071            metrics,
1072            move_vm,
1073            temporary_store,
1074            tx_ctx,
1075            gas_charger,
1076            pt,
1077        )
1078    }
1079
1080    fn setup_authenticator_state_expire(
1081        mut builder: ProgrammableTransactionBuilder,
1082        expire: AuthenticatorStateExpire,
1083    ) -> ProgrammableTransactionBuilder {
1084        builder
1085            .move_call(
1086                SUI_FRAMEWORK_ADDRESS.into(),
1087                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1088                AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
1089                vec![],
1090                vec![
1091                    CallArg::Object(ObjectArg::SharedObject {
1092                        id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1093                        initial_shared_version: expire.authenticator_obj_initial_shared_version,
1094                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1095                    }),
1096                    CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
1097                ],
1098            )
1099            .expect("Unable to generate authenticator_state_expire transaction!");
1100        builder
1101    }
1102
1103    fn setup_randomness_state_update(
1104        update: RandomnessStateUpdate,
1105        temporary_store: &mut TemporaryStore<'_>,
1106        tx_ctx: &mut TxContext,
1107        move_vm: &Arc<MoveVM>,
1108        gas_charger: &mut GasCharger,
1109        protocol_config: &ProtocolConfig,
1110        metrics: Arc<LimitsMetrics>,
1111    ) -> Result<(), ExecutionError> {
1112        let pt = {
1113            let mut builder = ProgrammableTransactionBuilder::new();
1114            let res = builder.move_call(
1115                SUI_FRAMEWORK_ADDRESS.into(),
1116                RANDOMNESS_MODULE_NAME.to_owned(),
1117                RANDOMNESS_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1118                vec![],
1119                vec![
1120                    CallArg::Object(ObjectArg::SharedObject {
1121                        id: SUI_RANDOMNESS_STATE_OBJECT_ID,
1122                        initial_shared_version: update.randomness_obj_initial_shared_version,
1123                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1124                    }),
1125                    CallArg::Pure(bcs::to_bytes(&update.randomness_round).unwrap()),
1126                    CallArg::Pure(bcs::to_bytes(&update.random_bytes).unwrap()),
1127                ],
1128            );
1129            assert_invariant!(
1130                res.is_ok(),
1131                "Unable to generate randomness_state_update transaction!"
1132            );
1133            builder.finish()
1134        };
1135        programmable_transactions::execution::execute::<execution_mode::System>(
1136            protocol_config,
1137            metrics,
1138            move_vm,
1139            temporary_store,
1140            tx_ctx,
1141            gas_charger,
1142            pt,
1143        )
1144    }
1145
1146    fn setup_coin_deny_list_state_create(
1147        mut builder: ProgrammableTransactionBuilder,
1148    ) -> ProgrammableTransactionBuilder {
1149        builder
1150            .move_call(
1151                SUI_FRAMEWORK_ADDRESS.into(),
1152                DENY_LIST_MODULE.to_owned(),
1153                DENY_LIST_CREATE_FUNC.to_owned(),
1154                vec![],
1155                vec![],
1156            )
1157            .expect("Unable to generate coin_deny_list_create transaction!");
1158        builder
1159    }
1160}