sui_adapter_latest/
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::adapter::new_move_runtime;
10    use crate::execution_mode::{self, ExecutionMode};
11    use crate::execution_value::SuiResolver;
12    use crate::gas_charger::{PaymentKind, PaymentMethod};
13    use move_binary_format::CompiledModule;
14    use move_trace_format::format::MoveTraceBuilder;
15    use move_vm_runtime::runtime::MoveRuntime;
16    use mysten_common::debug_fatal;
17    use std::collections::BTreeMap;
18    use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc};
19    use sui_types::accumulator_root::{ACCUMULATOR_ROOT_CREATE_FUNC, ACCUMULATOR_ROOT_MODULE};
20    use sui_types::balance::{
21        BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
22        BALANCE_MODULE_NAME,
23    };
24    use sui_types::coin_reservation::ParsedDigest;
25    use sui_types::execution_params::ExecutionOrEarlyError;
26    use sui_types::gas_coin::GAS;
27    use sui_types::messages_checkpoint::CheckpointTimestamp;
28    use sui_types::metrics::ExecutionMetrics;
29    use sui_types::object::OBJECT_START_VERSION;
30    use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
31    use sui_types::randomness_state::{
32        RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_CREATE_FUNCTION_NAME,
33        RANDOMNESS_STATE_UPDATE_FUNCTION_NAME,
34    };
35    use sui_types::{BRIDGE_ADDRESS, SUI_BRIDGE_OBJECT_ID, SUI_RANDOMNESS_STATE_OBJECT_ID};
36    use tracing::{info, instrument, trace, warn};
37
38    use crate::static_programmable_transactions as SPT;
39    use crate::sui_types::gas::SuiGasStatusAPI;
40    use crate::type_layout_resolver::TypeLayoutResolver;
41    use crate::{gas_charger::GasCharger, temporary_store::TemporaryStore};
42    use move_core_types::ident_str;
43    use move_core_types::language_storage::TypeTag;
44    use sui_move_natives::all_natives;
45    use sui_protocol_config::{
46        LimitThresholdCrossed, PerObjectCongestionControlMode, ProtocolConfig, check_limit_by_meter,
47    };
48    use sui_types::authenticator_state::{
49        AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME, AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME,
50        AUTHENTICATOR_STATE_MODULE_NAME, AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
51    };
52    use sui_types::base_types::SequenceNumber;
53    use sui_types::bridge::BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER;
54    use sui_types::bridge::{
55        BRIDGE_CREATE_FUNCTION_NAME, BRIDGE_INIT_COMMITTEE_FUNCTION_NAME, BRIDGE_MODULE_NAME,
56        BridgeChainId,
57    };
58    use sui_types::clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME};
59    use sui_types::committee::EpochId;
60    use sui_types::deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE};
61    use sui_types::digests::{
62        ChainIdentifier, get_mainnet_chain_identifier, get_testnet_chain_identifier,
63    };
64    use sui_types::effects::TransactionEffects;
65    use sui_types::error::{ExecutionError, ExecutionErrorTrait};
66    use sui_types::execution::{ExecutionTiming, ResultWithTimings};
67    use sui_types::execution_status::{ExecutionErrorKind, ExecutionStatus};
68    use sui_types::gas::GasCostSummary;
69    use sui_types::gas::SuiGasStatus;
70    use sui_types::id::UID;
71    use sui_types::inner_temporary_store::InnerTemporaryStore;
72    use sui_types::storage::BackingStore;
73    #[cfg(msim)]
74    use sui_types::sui_system_state::advance_epoch_result_injection::maybe_modify_result_for;
75    use sui_types::sui_system_state::{ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME, AdvanceEpochParams};
76    use sui_types::transaction::{
77        Argument, AuthenticatorStateExpire, AuthenticatorStateUpdate, CallArg, ChangeEpoch,
78        Command, EndOfEpochTransactionKind, GasData, GenesisTransaction, ObjectArg,
79        ProgrammableTransaction, Reservation, StoredExecutionTimeObservations, TransactionKind,
80        WithdrawFrom, WriteAccumulatorStorageCost, is_gasless_transaction,
81    };
82    use sui_types::transaction::{CheckedInputObjects, RandomnessStateUpdate};
83    use sui_types::{
84        SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_FRAMEWORK_ADDRESS, SUI_FRAMEWORK_PACKAGE_ID,
85        SUI_SYSTEM_PACKAGE_ID,
86        base_types::{SuiAddress, TransactionDigest, TxContext},
87        object::{Object, ObjectInner},
88        sui_system_state::{ADVANCE_EPOCH_FUNCTION_NAME, SUI_SYSTEM_MODULE_NAME},
89    };
90
91    fn payment_kind(
92        gas_data: &GasData,
93        transaction_kind: &TransactionKind,
94        protocol_config: &ProtocolConfig,
95    ) -> PaymentKind {
96        if gas_data.is_unmetered() || transaction_kind.is_system_tx() {
97            PaymentKind::unmetered()
98        } else if protocol_config.enable_gasless()
99            && is_gasless_transaction(gas_data, transaction_kind)
100        {
101            PaymentKind::gasless()
102        } else if gas_data.payment.is_empty() {
103            PaymentKind::smash(vec![PaymentMethod::AddressBalance(
104                gas_data.owner,
105                gas_data.budget,
106            )])
107            .expect("unable to create a payment kind with a single address balance")
108        } else {
109            let payment_methods = gas_data
110                .payment
111                .iter()
112                .map(|entry| {
113                    if let Ok(parsed) = ParsedDigest::try_from(entry.2) {
114                        PaymentMethod::AddressBalance(gas_data.owner, parsed.reservation_amount())
115                    } else {
116                        PaymentMethod::Coin(*entry)
117                    }
118                })
119                .collect();
120            PaymentKind::smash(payment_methods).expect(
121                "unable to create a payment kind from payment methods. \
122                 Should not be possible wit ha non-empty vector",
123            )
124        }
125    }
126
127    #[allow(clippy::type_complexity)]
128    #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
129    pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
130        store: &dyn BackingStore,
131        input_objects: CheckedInputObjects,
132        gas_data: GasData,
133        gas_status: SuiGasStatus,
134        transaction_kind: TransactionKind,
135        rewritten_inputs: Option<Vec<bool>>,
136        transaction_signer: SuiAddress,
137        transaction_digest: TransactionDigest,
138        move_vm: &Arc<MoveRuntime>,
139        epoch_id: &EpochId,
140        epoch_timestamp_ms: u64,
141        protocol_config: &ProtocolConfig,
142        metrics: Arc<ExecutionMetrics>,
143        enable_expensive_checks: bool,
144        execution_params: ExecutionOrEarlyError,
145        trace_builder_opt: &mut Option<MoveTraceBuilder>,
146    ) -> (
147        InnerTemporaryStore,
148        SuiGasStatus,
149        TransactionEffects,
150        Vec<ExecutionTiming>,
151        Result<Mode::ExecutionResults, Mode::Error>,
152    ) {
153        let input_objects = input_objects.into_inner();
154        let mutable_inputs = if enable_expensive_checks {
155            input_objects.all_mutable_inputs().keys().copied().collect()
156        } else {
157            HashSet::new()
158        };
159        let shared_object_refs = input_objects.filter_shared_objects();
160        let receiving_objects = transaction_kind.receiving_objects();
161        let mut transaction_dependencies = input_objects.transaction_dependencies();
162
163        let mut temporary_store = TemporaryStore::new(
164            store,
165            input_objects,
166            receiving_objects,
167            transaction_digest,
168            protocol_config,
169            *epoch_id,
170        );
171
172        let sponsor = {
173            let gas_owner = gas_data.owner;
174            if gas_owner == transaction_signer {
175                None
176            } else {
177                Some(gas_owner)
178            }
179        };
180        let gas_price = gas_status.gas_price();
181        let rgp = gas_status.reference_gas_price();
182
183        let mut gas_charger = GasCharger::new(
184            transaction_digest,
185            payment_kind(&gas_data, &transaction_kind, protocol_config),
186            gas_status,
187            &mut temporary_store,
188            protocol_config,
189        );
190
191        let tx_ctx = TxContext::new_from_components(
192            &transaction_signer,
193            &transaction_digest,
194            epoch_id,
195            epoch_timestamp_ms,
196            rgp,
197            gas_price,
198            gas_data.budget,
199            sponsor,
200            protocol_config,
201        );
202        let tx_ctx = Rc::new(RefCell::new(tx_ctx));
203
204        let is_gasless = protocol_config.enable_gasless()
205            && is_gasless_transaction(&gas_data, &transaction_kind);
206        let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
207
208        let (gas_cost_summary, execution_result, timings) = execute_transaction::<Mode>(
209            store,
210            &mut temporary_store,
211            transaction_kind,
212            rewritten_inputs,
213            &mut gas_charger,
214            tx_ctx,
215            move_vm,
216            protocol_config,
217            metrics.clone(),
218            enable_expensive_checks,
219            execution_params,
220            trace_builder_opt,
221            is_gasless,
222        );
223
224        let status = if let Err(error) = &execution_result {
225            // Elaborate errors in logs if they are unexpected or their status is terse.
226            use ExecutionErrorKind as K;
227            match error.kind() {
228                K::InvariantViolation | K::VMInvariantViolation => {
229                    debug_fatal!(
230                        "INVARIANT VIOLATION! Txn Digest: {}, Source: {:?}",
231                        transaction_digest,
232                        error.source_ref(),
233                    );
234                }
235
236                K::SuiMoveVerificationError | K::VMVerificationOrDeserializationError => {
237                    #[skip_checked_arithmetic]
238                    tracing::debug!(
239                        kind = ?error.kind(),
240                        tx_digest = ?transaction_digest,
241                        "Verification Error. Source: {:?}",
242                        error.source_ref(),
243                    );
244                }
245
246                K::PublishUpgradeMissingDependency | K::PublishUpgradeDependencyDowngrade => {
247                    #[skip_checked_arithmetic]
248                    tracing::debug!(
249                        kind = ?error.kind(),
250                        tx_digest = ?transaction_digest,
251                        "Publish/Upgrade Error. Source: {:?}",
252                        error.source_ref(),
253                    )
254                }
255
256                _ => (),
257            };
258
259            ExecutionStatus::new_failure(error.to_execution_failure())
260        } else {
261            ExecutionStatus::Success
262        };
263
264        #[skip_checked_arithmetic]
265        trace!(
266            tx_digest = ?transaction_digest,
267            computation_gas_cost = gas_cost_summary.computation_cost,
268            storage_gas_cost = gas_cost_summary.storage_cost,
269            storage_gas_rebate = gas_cost_summary.storage_rebate,
270            "Finished execution of transaction with status {:?}",
271            status
272        );
273
274        // Genesis writes a special digest to indicate that an object was created during
275        // genesis and not written by any normal transaction - remove that from the
276        // dependencies
277        transaction_dependencies.remove(&TransactionDigest::genesis_marker());
278
279        if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
280            temporary_store
281                .check_ownership_invariants(
282                    &transaction_signer,
283                    &sponsor,
284                    &mut gas_charger,
285                    &mutable_inputs,
286                    is_epoch_change,
287                )
288                .unwrap()
289        } // else, in dev inspect mode and anything goes--don't check
290
291        let (inner, effects) = temporary_store.into_effects(
292            shared_object_refs,
293            &transaction_digest,
294            transaction_dependencies,
295            gas_cost_summary,
296            status,
297            &mut gas_charger,
298            *epoch_id,
299        );
300
301        metrics.vm_telemetry_metrics.try_update(|vm_metrics| {
302            let t = move_vm.get_telemetry_report();
303            vm_metrics
304                .move_vm_package_cache_count
305                .set(t.package_cache_count as i64);
306            vm_metrics
307                .move_vm_total_arena_size_bytes
308                .set(t.total_arena_size as i64);
309            vm_metrics.move_vm_module_count.set(t.module_count as i64);
310            vm_metrics
311                .move_vm_function_count
312                .set(t.function_count as i64);
313            vm_metrics.move_vm_type_count.set(t.type_count as i64);
314            vm_metrics.move_vm_interner_size.set(t.interner_size as i64);
315            vm_metrics
316                .move_vm_vtable_cache_count
317                .set(t.vtable_cache_count as i64);
318            vm_metrics
319                .move_vm_vtable_cache_hits
320                .set(t.vtable_cache_hits as i64);
321            vm_metrics
322                .move_vm_vtable_cache_misses
323                .set(t.vtable_cache_misses as i64);
324            vm_metrics
325                .move_vm_load_time_ms
326                .set(t.total_load_time as i64);
327            vm_metrics.move_vm_load_count.set(t.load_count as i64);
328            vm_metrics
329                .move_vm_validation_time_ms
330                .set(t.total_validation_time as i64);
331            vm_metrics
332                .move_vm_validation_count
333                .set(t.validation_count as i64);
334            vm_metrics.move_vm_jit_time_ms.set(t.total_jit_time as i64);
335            vm_metrics.move_vm_jit_count.set(t.jit_count as i64);
336            vm_metrics
337                .move_vm_execution_time_ms
338                .set(t.total_execution_time as i64);
339            vm_metrics
340                .move_vm_execution_count
341                .set(t.execution_count as i64);
342            vm_metrics
343                .move_vm_interpreter_time_ms
344                .set(t.total_interpreter_time as i64);
345            vm_metrics
346                .move_vm_interpreter_count
347                .set(t.interpreter_count as i64);
348            vm_metrics
349                .move_vm_max_callstack_size
350                .set(t.max_callstack_size as i64);
351            vm_metrics
352                .move_vm_max_valuestack_size
353                .set(t.max_valuestack_size as i64);
354            vm_metrics.move_vm_total_time_ms.set(t.total_time as i64);
355            vm_metrics.move_vm_total_count.set(t.total_count as i64);
356        });
357
358        (
359            inner,
360            gas_charger.into_gas_status(),
361            effects,
362            timings,
363            execution_result,
364        )
365    }
366
367    pub fn execute_genesis_state_update(
368        store: &dyn BackingStore,
369        protocol_config: &ProtocolConfig,
370        metrics: Arc<ExecutionMetrics>,
371        move_vm: &Arc<MoveRuntime>,
372        tx_context: Rc<RefCell<TxContext>>,
373        input_objects: CheckedInputObjects,
374        pt: ProgrammableTransaction,
375    ) -> Result<InnerTemporaryStore, ExecutionError> {
376        let input_objects = input_objects.into_inner();
377        let mut temporary_store = TemporaryStore::new(
378            store,
379            input_objects,
380            vec![],
381            tx_context.borrow().digest(),
382            protocol_config,
383            0,
384        );
385        let mut gas_charger = GasCharger::new_unmetered(tx_context.borrow().digest());
386        SPT::execute::<execution_mode::Genesis>(
387            protocol_config,
388            metrics,
389            move_vm,
390            &mut temporary_store,
391            store.as_backing_package_store(),
392            tx_context,
393            &mut gas_charger,
394            None,
395            pt,
396            &mut None,
397        )
398        .map_err(|(e, _)| e)?;
399        temporary_store.update_object_version_and_prev_tx();
400        Ok(temporary_store.into_inner(BTreeMap::new()))
401    }
402
403    #[instrument(name = "tx_execute", level = "debug", skip_all)]
404    fn execute_transaction<Mode: ExecutionMode>(
405        store: &dyn BackingStore,
406        temporary_store: &mut TemporaryStore<'_>,
407        transaction_kind: TransactionKind,
408        rewritten_inputs: Option<Vec<bool>>,
409        gas_charger: &mut GasCharger,
410        tx_ctx: Rc<RefCell<TxContext>>,
411        move_vm: &Arc<MoveRuntime>,
412        protocol_config: &ProtocolConfig,
413        metrics: Arc<ExecutionMetrics>,
414        enable_expensive_checks: bool,
415        execution_params: ExecutionOrEarlyError,
416        trace_builder_opt: &mut Option<MoveTraceBuilder>,
417        is_gasless: bool,
418    ) -> (
419        GasCostSummary,
420        Result<Mode::ExecutionResults, Mode::Error>,
421        Vec<ExecutionTiming>,
422    ) {
423        // At this point no charges have been applied yet
424        debug_assert!(
425            gas_charger.no_charges(),
426            "No gas charges must be applied yet"
427        );
428
429        let is_genesis_tx = matches!(transaction_kind, TransactionKind::Genesis(_));
430        let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();
431        let digest = tx_ctx.borrow().digest();
432        let withdrawal_reservations =
433            if is_gasless && protocol_config.gasless_verify_remaining_balance() {
434                gasless_withdrawal_reservations(&transaction_kind, &tx_ctx.borrow())
435            } else {
436                None
437            };
438
439        // We must charge object read here during transaction execution, because if this fails
440        // we must still ensure an effect is committed and all objects versions incremented
441        let result = gas_charger.charge_input_objects(temporary_store);
442
443        let result: ResultWithTimings<Mode::ExecutionResults, Mode::Error> =
444            result.map_err(|e| (e.into(), vec![])).and_then(
445                |()| -> ResultWithTimings<Mode::ExecutionResults, Mode::Error> {
446                    let mut execution_result: ResultWithTimings<
447                        Mode::ExecutionResults,
448                        Mode::Error,
449                    > = match execution_params {
450                        ExecutionOrEarlyError::Err(early_execution_error) => Err((
451                            ExecutionError::new(early_execution_error, None).into(),
452                            vec![],
453                        )),
454                        ExecutionOrEarlyError::Ok(()) => execution_loop::<Mode>(
455                            store,
456                            temporary_store,
457                            transaction_kind,
458                            rewritten_inputs,
459                            tx_ctx,
460                            move_vm,
461                            gas_charger,
462                            protocol_config,
463                            metrics.clone(),
464                            trace_builder_opt,
465                        ),
466                    };
467
468                    let meter_check = check_meter_limit::<Mode>(
469                        temporary_store,
470                        gas_charger,
471                        protocol_config,
472                        metrics.clone(),
473                    );
474                    if let Err(e) = meter_check {
475                        execution_result = Err((e, vec![]));
476                    }
477
478                    if execution_result.is_ok() {
479                        let gas_check = check_written_objects_limit::<Mode>(
480                            temporary_store,
481                            gas_charger,
482                            protocol_config,
483                            metrics,
484                        );
485                        if let Err(e) = gas_check {
486                            execution_result = Err((e, vec![]));
487                        }
488                    }
489
490                    execution_result
491                },
492            );
493
494        let (mut result, timings) = match result {
495            Ok((r, t)) => (Ok(r), t),
496            Err((e, t)) => (Err(e), t),
497        };
498        if is_gasless
499            && result.is_ok()
500            && let Err(msg) = temporary_store
501                .check_gasless_execution_requirements(withdrawal_reservations.as_ref())
502        {
503            result = Err(
504                ExecutionError::new_with_source(ExecutionErrorKind::InsufficientGas, msg).into(),
505            );
506        }
507
508        let cost_summary = gas_charger.charge_gas(temporary_store, &mut result);
509        // For advance epoch transaction, we need to provide epoch rewards and rebates as extra
510        // information provided to check_sui_conserved, because we mint rewards, and burn
511        // the rebates. We also need to pass in the unmetered_storage_rebate because storage
512        // rebate is not reflected in the storage_rebate of gas summary. This is a bit confusing.
513        // We could probably clean up the code a bit.
514        // Put all the storage rebate accumulated in the system transaction
515        // to the 0x5 object so that it's not lost.
516        temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
517
518        if let Err(e) = run_conservation_checks::<Mode>(
519            temporary_store,
520            gas_charger,
521            digest,
522            move_vm,
523            protocol_config.simple_conservation_checks(),
524            enable_expensive_checks,
525            &cost_summary,
526            is_genesis_tx,
527            advance_epoch_gas_summary,
528        ) {
529            // FIXME: we cannot fail the transaction if this is an epoch change transaction.
530            result = Err(e);
531        }
532
533        (cost_summary, result, timings)
534    }
535
536    #[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
537    fn run_conservation_checks<Mode: ExecutionMode>(
538        temporary_store: &mut TemporaryStore<'_>,
539        gas_charger: &mut GasCharger,
540        tx_digest: TransactionDigest,
541        move_vm: &Arc<MoveRuntime>,
542        simple_conservation_checks: bool,
543        enable_expensive_checks: bool,
544        cost_summary: &GasCostSummary,
545        is_genesis_tx: bool,
546        advance_epoch_gas_summary: Option<(u64, u64)>,
547    ) -> Result<(), Mode::Error> {
548        let mut result: Result<(), Mode::Error> = Ok(());
549        if !is_genesis_tx && !Mode::skip_conservation_checks() {
550            // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
551            let conservation_result = {
552                temporary_store
553                    .check_sui_conserved(simple_conservation_checks, cost_summary)
554                    .and_then(|()| {
555                        if enable_expensive_checks {
556                            // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
557                            let mut layout_resolver = TypeLayoutResolver::new(
558                                move_vm,
559                                temporary_store.protocol_config(),
560                                Box::new(&*temporary_store),
561                            );
562                            temporary_store.check_sui_conserved_expensive(
563                                cost_summary,
564                                advance_epoch_gas_summary,
565                                &mut layout_resolver,
566                            )
567                        } else {
568                            Ok(())
569                        }
570                    })
571            };
572            if let Err(conservation_err) = conservation_result {
573                // conservation violated. try to avoid panic by dumping all writes, charging for gas, re-checking
574                // conservation, and surfacing an aborted transaction with an invariant violation if all of that works
575                result = Err(conservation_err.into());
576                gas_charger.reset(temporary_store);
577                gas_charger.charge_gas(temporary_store, &mut result);
578                // check conservation once more
579                if let Err(recovery_err) = {
580                    temporary_store
581                        .check_sui_conserved(simple_conservation_checks, cost_summary)
582                        .and_then(|()| {
583                            if enable_expensive_checks {
584                                // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
585                                let mut layout_resolver = TypeLayoutResolver::new(
586                                    move_vm,
587                                    temporary_store.protocol_config(),
588                                    Box::new(&*temporary_store),
589                                );
590                                temporary_store.check_sui_conserved_expensive(
591                                    cost_summary,
592                                    advance_epoch_gas_summary,
593                                    &mut layout_resolver,
594                                )
595                            } else {
596                                Ok(())
597                            }
598                        })
599                } {
600                    // if we still fail, it's a problem with gas
601                    // charging that happens even in the "aborted" case--no other option but panic.
602                    // we will create or destroy SUI otherwise
603                    panic!(
604                        "SUI conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
605                        tx_digest,
606                        recovery_err,
607                        gas_charger.summary()
608                    )
609                }
610            }
611        } // else, we're in the genesis transaction which mints the SUI supply, and hence does not satisfy SUI conservation, or
612        // we're in the non-production dev inspect mode which allows us to violate conservation
613        result
614    }
615
616    #[instrument(name = "check_meter_limit", level = "debug", skip_all)]
617    fn check_meter_limit<Mode: ExecutionMode>(
618        temporary_store: &mut TemporaryStore<'_>,
619        gas_charger: &mut GasCharger,
620        protocol_config: &ProtocolConfig,
621        metrics: Arc<ExecutionMetrics>,
622    ) -> Result<(), Mode::Error> {
623        let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
624
625        // Check if a limit threshold was crossed.
626        // For metered transactions, there is not soft limit.
627        // For system transactions, we allow a soft limit with alerting, and a hard limit where we terminate
628        match check_limit_by_meter!(
629            !gas_charger.is_unmetered(),
630            effects_estimated_size,
631            protocol_config.max_serialized_tx_effects_size_bytes(),
632            protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
633            metrics.limits_metrics.excessive_estimated_effects_size
634        ) {
635            LimitThresholdCrossed::None => Ok(()),
636            LimitThresholdCrossed::Soft(_, limit) => {
637                warn!(
638                    effects_estimated_size = effects_estimated_size,
639                    soft_limit = limit,
640                    "Estimated transaction effects size crossed soft limit",
641                );
642                Ok(())
643            }
644            LimitThresholdCrossed::Hard(_, lim) => Err(ExecutionError::new_with_source(
645                ExecutionErrorKind::EffectsTooLarge {
646                    current_size: effects_estimated_size as u64,
647                    max_size: lim as u64,
648                },
649                "Transaction effects are too large",
650            )
651            .into()),
652        }
653    }
654
655    #[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
656    fn check_written_objects_limit<Mode: ExecutionMode>(
657        temporary_store: &mut TemporaryStore<'_>,
658        gas_charger: &mut GasCharger,
659        protocol_config: &ProtocolConfig,
660        metrics: Arc<ExecutionMetrics>,
661    ) -> Result<(), Mode::Error> {
662        if let (Some(normal_lim), Some(system_lim)) = (
663            protocol_config.max_size_written_objects_as_option(),
664            protocol_config.max_size_written_objects_system_tx_as_option(),
665        ) {
666            let written_objects_size = temporary_store.written_objects_size();
667
668            match check_limit_by_meter!(
669                !gas_charger.is_unmetered(),
670                written_objects_size,
671                normal_lim,
672                system_lim,
673                metrics.limits_metrics.excessive_written_objects_size
674            ) {
675                LimitThresholdCrossed::None => (),
676                LimitThresholdCrossed::Soft(_, limit) => {
677                    warn!(
678                        written_objects_size = written_objects_size,
679                        soft_limit = limit,
680                        "Written objects size crossed soft limit",
681                    )
682                }
683                LimitThresholdCrossed::Hard(_, lim) => {
684                    return Err(ExecutionError::new_with_source(
685                        ExecutionErrorKind::WrittenObjectsTooLarge {
686                            current_size: written_objects_size as u64,
687                            max_size: lim as u64,
688                        },
689                        "Written objects size crossed hard limit",
690                    )
691                    .into());
692                }
693            };
694        }
695
696        Ok(())
697    }
698
699    fn gasless_withdrawal_reservations(
700        transaction_kind: &TransactionKind,
701        tx_ctx: &TxContext,
702    ) -> Option<BTreeMap<(SuiAddress, TypeTag), u64>> {
703        let TransactionKind::ProgrammableTransaction(pt) = transaction_kind else {
704            debug_fatal!("Gasless transaction must be a ProgrammableTransaction");
705            return None;
706        };
707        let sender = tx_ctx.sender();
708        let mut reservations = BTreeMap::<(SuiAddress, TypeTag), u64>::new();
709        for input in &pt.inputs {
710            let CallArg::FundsWithdrawal(fw) = input else {
711                continue;
712            };
713            let Some(coin_type) = fw.type_arg.get_balance_type_param() else {
714                debug_fatal!("expected Balance type for withdrawal");
715                continue;
716            };
717            let owner = match fw.withdraw_from {
718                WithdrawFrom::Sender => sender,
719                WithdrawFrom::Sponsor => {
720                    debug_fatal!("WithdrawFrom::Sponsor is not expected in gasless transactions");
721                    tx_ctx.sponsor().unwrap_or(sender)
722                }
723            };
724            let Reservation::MaxAmountU64(amount) = fw.reservation;
725            let entry = reservations.entry((owner, coin_type)).or_insert(0);
726            *entry = entry.saturating_add(amount);
727        }
728        Some(reservations)
729    }
730
731    #[instrument(level = "debug", skip_all)]
732    fn execution_loop<Mode: ExecutionMode>(
733        store: &dyn BackingStore,
734        temporary_store: &mut TemporaryStore<'_>,
735        transaction_kind: TransactionKind,
736        rewritten_inputs: Option<Vec<bool>>,
737        tx_ctx: Rc<RefCell<TxContext>>,
738        move_vm: &Arc<MoveRuntime>,
739        gas_charger: &mut GasCharger,
740        protocol_config: &ProtocolConfig,
741        metrics: Arc<ExecutionMetrics>,
742        trace_builder_opt: &mut Option<MoveTraceBuilder>,
743    ) -> ResultWithTimings<Mode::ExecutionResults, Mode::Error> {
744        let result = match transaction_kind {
745            TransactionKind::ChangeEpoch(change_epoch) => {
746                let builder = ProgrammableTransactionBuilder::new();
747                advance_epoch::<Mode>(
748                    builder,
749                    change_epoch,
750                    temporary_store,
751                    store,
752                    tx_ctx,
753                    move_vm,
754                    gas_charger,
755                    protocol_config,
756                    metrics,
757                    trace_builder_opt,
758                )
759                .map_err(|e| (e, vec![]))?;
760                Ok((Mode::empty_results(), vec![]))
761            }
762            TransactionKind::Genesis(GenesisTransaction { objects }) => {
763                if tx_ctx.borrow().epoch() != 0 {
764                    panic!("BUG: Genesis Transactions can only be executed in epoch 0");
765                }
766
767                for genesis_object in objects {
768                    match genesis_object {
769                        sui_types::transaction::GenesisObject::RawObject { data, owner } => {
770                            let object = ObjectInner {
771                                data,
772                                owner,
773                                previous_transaction: tx_ctx.borrow().digest(),
774                                storage_rebate: 0,
775                            };
776                            temporary_store.create_object(object.into());
777                        }
778                    }
779                }
780                Ok((Mode::empty_results(), vec![]))
781            }
782            TransactionKind::ConsensusCommitPrologue(prologue) => {
783                setup_consensus_commit::<Mode>(
784                    prologue.commit_timestamp_ms,
785                    temporary_store,
786                    store,
787                    tx_ctx,
788                    move_vm,
789                    gas_charger,
790                    protocol_config,
791                    metrics,
792                    trace_builder_opt,
793                )
794                .expect("ConsensusCommitPrologue cannot fail");
795                Ok((Mode::empty_results(), vec![]))
796            }
797            TransactionKind::ConsensusCommitPrologueV2(prologue) => {
798                setup_consensus_commit::<Mode>(
799                    prologue.commit_timestamp_ms,
800                    temporary_store,
801                    store,
802                    tx_ctx,
803                    move_vm,
804                    gas_charger,
805                    protocol_config,
806                    metrics,
807                    trace_builder_opt,
808                )
809                .expect("ConsensusCommitPrologueV2 cannot fail");
810                Ok((Mode::empty_results(), vec![]))
811            }
812            TransactionKind::ConsensusCommitPrologueV3(prologue) => {
813                setup_consensus_commit::<Mode>(
814                    prologue.commit_timestamp_ms,
815                    temporary_store,
816                    store,
817                    tx_ctx,
818                    move_vm,
819                    gas_charger,
820                    protocol_config,
821                    metrics,
822                    trace_builder_opt,
823                )
824                .expect("ConsensusCommitPrologueV3 cannot fail");
825                Ok((Mode::empty_results(), vec![]))
826            }
827            TransactionKind::ConsensusCommitPrologueV4(prologue) => {
828                setup_consensus_commit::<Mode>(
829                    prologue.commit_timestamp_ms,
830                    temporary_store,
831                    store,
832                    tx_ctx,
833                    move_vm,
834                    gas_charger,
835                    protocol_config,
836                    metrics,
837                    trace_builder_opt,
838                )
839                .expect("ConsensusCommitPrologue cannot fail");
840                Ok((Mode::empty_results(), vec![]))
841            }
842            TransactionKind::ProgrammableTransaction(pt) => SPT::execute::<Mode>(
843                protocol_config,
844                metrics,
845                move_vm,
846                temporary_store,
847                store.as_backing_package_store(),
848                tx_ctx,
849                gas_charger,
850                rewritten_inputs,
851                pt,
852                trace_builder_opt,
853            )
854            // TODO push Mode::Error lower into the call stack and remove into()
855            .map_err(|(e, timings)| (e.into(), timings)),
856            TransactionKind::ProgrammableSystemTransaction(pt) => {
857                SPT::execute::<execution_mode::System<Mode::Error>>(
858                    protocol_config,
859                    metrics,
860                    move_vm,
861                    temporary_store,
862                    store.as_backing_package_store(),
863                    tx_ctx,
864                    gas_charger,
865                    None,
866                    pt,
867                    trace_builder_opt,
868                )
869                // TODO push Mode::Error lower into the call stack and remove into()
870                .map_err(|(e, _)| (e.into(), vec![]))?;
871                Ok((Mode::empty_results(), vec![]))
872            }
873            TransactionKind::EndOfEpochTransaction(txns) => {
874                let mut builder = ProgrammableTransactionBuilder::new();
875                let len = txns.len();
876                for (i, tx) in txns.into_iter().enumerate() {
877                    match tx {
878                        EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
879                            assert_eq!(i, len - 1);
880                            advance_epoch::<Mode>(
881                                builder,
882                                change_epoch,
883                                temporary_store,
884                                store,
885                                tx_ctx,
886                                move_vm,
887                                gas_charger,
888                                protocol_config,
889                                metrics,
890                                trace_builder_opt,
891                            )
892                            .map_err(|e| (e, vec![]))?;
893                            return Ok((Mode::empty_results(), vec![]));
894                        }
895                        EndOfEpochTransactionKind::AuthenticatorStateCreate => {
896                            assert!(protocol_config.enable_jwk_consensus_updates());
897                            builder = setup_authenticator_state_create(builder);
898                        }
899                        EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
900                            assert!(protocol_config.enable_jwk_consensus_updates());
901
902                            // TODO: it would be nice if a failure of this function didn't cause
903                            // safe mode.
904                            builder = setup_authenticator_state_expire(builder, expire);
905                        }
906                        EndOfEpochTransactionKind::RandomnessStateCreate => {
907                            assert!(protocol_config.random_beacon());
908                            builder = setup_randomness_state_create(builder);
909                        }
910                        EndOfEpochTransactionKind::DenyListStateCreate => {
911                            assert!(protocol_config.enable_coin_deny_list_v1());
912                            builder = setup_coin_deny_list_state_create(builder);
913                        }
914                        EndOfEpochTransactionKind::BridgeStateCreate(chain_id) => {
915                            assert!(protocol_config.enable_bridge());
916                            builder = setup_bridge_create(builder, chain_id)
917                        }
918                        EndOfEpochTransactionKind::BridgeCommitteeInit(bridge_shared_version) => {
919                            assert!(protocol_config.enable_bridge());
920                            assert!(protocol_config.should_try_to_finalize_bridge_committee());
921                            builder = setup_bridge_committee_update(builder, bridge_shared_version)
922                        }
923                        EndOfEpochTransactionKind::StoreExecutionTimeObservations(estimates) => {
924                            if let PerObjectCongestionControlMode::ExecutionTimeEstimate(params) =
925                                protocol_config.per_object_congestion_control_mode()
926                            {
927                                if let Some(chunk_size) = params.observations_chunk_size {
928                                    builder = setup_store_execution_time_estimates_v2(
929                                        builder,
930                                        estimates,
931                                        chunk_size as usize,
932                                    );
933                                } else {
934                                    builder =
935                                        setup_store_execution_time_estimates(builder, estimates);
936                                }
937                            }
938                        }
939                        EndOfEpochTransactionKind::AccumulatorRootCreate => {
940                            assert!(protocol_config.create_root_accumulator_object());
941                            builder = setup_accumulator_root_create(builder);
942                        }
943                        EndOfEpochTransactionKind::WriteAccumulatorStorageCost(
944                            write_storage_cost,
945                        ) => {
946                            assert!(protocol_config.enable_accumulators());
947                            builder =
948                                setup_write_accumulator_storage_cost(builder, &write_storage_cost);
949                        }
950                        EndOfEpochTransactionKind::CoinRegistryCreate => {
951                            assert!(protocol_config.enable_coin_registry());
952                            builder = setup_coin_registry_create(builder);
953                        }
954                        EndOfEpochTransactionKind::DisplayRegistryCreate => {
955                            assert!(protocol_config.enable_display_registry());
956                            builder = setup_display_registry_create(builder);
957                        }
958                        EndOfEpochTransactionKind::AddressAliasStateCreate => {
959                            assert!(protocol_config.address_aliases());
960                            builder = setup_address_alias_state_create(builder);
961                        }
962                    }
963                }
964                unreachable!(
965                    "EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list"
966                )
967            }
968            TransactionKind::AuthenticatorStateUpdate(auth_state_update) => {
969                setup_authenticator_state_update::<Mode>(
970                    auth_state_update,
971                    temporary_store,
972                    store,
973                    tx_ctx,
974                    move_vm,
975                    gas_charger,
976                    protocol_config,
977                    metrics,
978                    trace_builder_opt,
979                )
980                .map_err(|e| (e, vec![]))?;
981                Ok((Mode::empty_results(), vec![]))
982            }
983            TransactionKind::RandomnessStateUpdate(randomness_state_update) => {
984                setup_randomness_state_update::<Mode>(
985                    randomness_state_update,
986                    temporary_store,
987                    store,
988                    tx_ctx,
989                    move_vm,
990                    gas_charger,
991                    protocol_config,
992                    metrics,
993                    trace_builder_opt,
994                )
995                .map_err(|e| (e, vec![]))?;
996                Ok((Mode::empty_results(), vec![]))
997            }
998        }?;
999        temporary_store
1000            .check_execution_results_consistency()
1001            // TODO push Mode::Error lower into the call stack and remove into()
1002            .map_err(|e| (e.into(), vec![]))?;
1003        Ok(result)
1004    }
1005
1006    fn mint_epoch_rewards_in_pt(
1007        builder: &mut ProgrammableTransactionBuilder,
1008        params: &AdvanceEpochParams,
1009    ) -> (Argument, Argument) {
1010        // Create storage rewards.
1011        let storage_charge_arg = builder
1012            .input(CallArg::Pure(
1013                bcs::to_bytes(&params.storage_charge).unwrap(),
1014            ))
1015            .unwrap();
1016        let storage_rewards = builder.programmable_move_call(
1017            SUI_FRAMEWORK_PACKAGE_ID,
1018            BALANCE_MODULE_NAME.to_owned(),
1019            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
1020            vec![GAS::type_tag()],
1021            vec![storage_charge_arg],
1022        );
1023
1024        // Create computation rewards.
1025        let computation_charge_arg = builder
1026            .input(CallArg::Pure(
1027                bcs::to_bytes(&params.computation_charge).unwrap(),
1028            ))
1029            .unwrap();
1030        let computation_rewards = builder.programmable_move_call(
1031            SUI_FRAMEWORK_PACKAGE_ID,
1032            BALANCE_MODULE_NAME.to_owned(),
1033            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
1034            vec![GAS::type_tag()],
1035            vec![computation_charge_arg],
1036        );
1037        (storage_rewards, computation_rewards)
1038    }
1039
1040    pub fn construct_advance_epoch_pt(
1041        mut builder: ProgrammableTransactionBuilder,
1042        params: &AdvanceEpochParams,
1043    ) -> Result<ProgrammableTransaction, ExecutionError> {
1044        // Step 1: Create storage and computation rewards.
1045        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
1046
1047        // Step 2: Advance the epoch.
1048        let mut arguments = vec![storage_rewards, computation_rewards];
1049        let call_arg_arguments = vec![
1050            CallArg::SUI_SYSTEM_MUT,
1051            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
1052            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
1053            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
1054            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
1055            CallArg::Pure(bcs::to_bytes(&params.storage_fund_reinvest_rate).unwrap()),
1056            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()),
1057            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()),
1058        ]
1059        .into_iter()
1060        .map(|a| builder.input(a))
1061        .collect::<Result<_, _>>();
1062
1063        assert_invariant!(
1064            call_arg_arguments.is_ok(),
1065            "Unable to generate args for advance_epoch transaction!"
1066        );
1067
1068        arguments.append(&mut call_arg_arguments.unwrap());
1069
1070        info!("Call arguments to advance_epoch transaction: {:?}", params);
1071
1072        let storage_rebates = builder.programmable_move_call(
1073            SUI_SYSTEM_PACKAGE_ID,
1074            SUI_SYSTEM_MODULE_NAME.to_owned(),
1075            ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
1076            vec![],
1077            arguments,
1078        );
1079
1080        // Step 3: Destroy the storage rebates.
1081        builder.programmable_move_call(
1082            SUI_FRAMEWORK_PACKAGE_ID,
1083            BALANCE_MODULE_NAME.to_owned(),
1084            BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
1085            vec![GAS::type_tag()],
1086            vec![storage_rebates],
1087        );
1088        Ok(builder.finish())
1089    }
1090
1091    pub fn construct_advance_epoch_safe_mode_pt(
1092        params: &AdvanceEpochParams,
1093    ) -> Result<ProgrammableTransaction, ExecutionError> {
1094        let mut builder = ProgrammableTransactionBuilder::new();
1095        // Step 1: Create storage and computation rewards.
1096        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
1097
1098        // Step 2: Advance the epoch.
1099        let mut arguments = vec![storage_rewards, computation_rewards];
1100
1101        let mut args = vec![
1102            CallArg::SUI_SYSTEM_MUT,
1103            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
1104            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
1105            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
1106            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
1107        ];
1108
1109        args.push(CallArg::Pure(
1110            bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap(),
1111        ));
1112
1113        let call_arg_arguments = args
1114            .into_iter()
1115            .map(|a| builder.input(a))
1116            .collect::<Result<_, _>>();
1117
1118        assert_invariant!(
1119            call_arg_arguments.is_ok(),
1120            "Unable to generate args for advance_epoch transaction!"
1121        );
1122
1123        arguments.append(&mut call_arg_arguments.unwrap());
1124
1125        info!("Call arguments to advance_epoch transaction: {:?}", params);
1126
1127        builder.programmable_move_call(
1128            SUI_SYSTEM_PACKAGE_ID,
1129            SUI_SYSTEM_MODULE_NAME.to_owned(),
1130            ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME.to_owned(),
1131            vec![],
1132            arguments,
1133        );
1134
1135        Ok(builder.finish())
1136    }
1137
1138    fn advance_epoch<Mode: ExecutionMode>(
1139        builder: ProgrammableTransactionBuilder,
1140        change_epoch: ChangeEpoch,
1141        temporary_store: &mut TemporaryStore<'_>,
1142        store: &dyn BackingStore,
1143        tx_ctx: Rc<RefCell<TxContext>>,
1144        move_vm: &Arc<MoveRuntime>,
1145        gas_charger: &mut GasCharger,
1146        protocol_config: &ProtocolConfig,
1147        metrics: Arc<ExecutionMetrics>,
1148        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1149    ) -> Result<(), Mode::Error> {
1150        let params = AdvanceEpochParams {
1151            epoch: change_epoch.epoch,
1152            next_protocol_version: change_epoch.protocol_version,
1153            storage_charge: change_epoch.storage_charge,
1154            computation_charge: change_epoch.computation_charge,
1155            storage_rebate: change_epoch.storage_rebate,
1156            non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
1157            storage_fund_reinvest_rate: protocol_config.storage_fund_reinvest_rate(),
1158            reward_slashing_rate: protocol_config.reward_slashing_rate(),
1159            epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
1160        };
1161        // TODO push Mode::Error lower into the call stack and remove (implicit) into()
1162        let advance_epoch_pt = construct_advance_epoch_pt(builder, &params)?;
1163        let result = SPT::execute::<execution_mode::System>(
1164            protocol_config,
1165            metrics.clone(),
1166            move_vm,
1167            temporary_store,
1168            store.as_backing_package_store(),
1169            tx_ctx.clone(),
1170            gas_charger,
1171            None,
1172            advance_epoch_pt,
1173            trace_builder_opt,
1174        );
1175
1176        #[cfg(msim)]
1177        let result = maybe_modify_result_for(result, change_epoch.epoch);
1178
1179        if let Err(err) = &result {
1180            tracing::error!(
1181                "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx data: {:?}",
1182                err.0,
1183                temporary_store.objects(),
1184                change_epoch,
1185            );
1186            temporary_store.drop_writes();
1187            // Must reset the storage rebate since we are re-executing.
1188            gas_charger.reset_storage_cost_and_rebate();
1189
1190            temporary_store.advance_epoch_safe_mode(&params, protocol_config);
1191        }
1192
1193        let new_vm = new_move_runtime(
1194            all_natives(/* silent */ true, protocol_config),
1195            protocol_config,
1196        )
1197        .expect("Failed to create new MoveRuntime");
1198        process_system_packages(
1199            change_epoch,
1200            temporary_store,
1201            store,
1202            tx_ctx,
1203            &new_vm,
1204            gas_charger,
1205            protocol_config,
1206            metrics,
1207            trace_builder_opt,
1208        );
1209        Ok(())
1210    }
1211
1212    fn process_system_packages(
1213        change_epoch: ChangeEpoch,
1214        temporary_store: &mut TemporaryStore<'_>,
1215        store: &dyn BackingStore,
1216        tx_ctx: Rc<RefCell<TxContext>>,
1217        move_vm: &MoveRuntime,
1218        gas_charger: &mut GasCharger,
1219        protocol_config: &ProtocolConfig,
1220        metrics: Arc<ExecutionMetrics>,
1221        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1222    ) {
1223        let digest = tx_ctx.borrow().digest();
1224        let binary_config = protocol_config.binary_config(None);
1225        for (version, modules, dependencies) in change_epoch.system_packages.into_iter() {
1226            let deserialized_modules: Vec<_> = modules
1227                .iter()
1228                .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
1229                .collect();
1230
1231            if version == OBJECT_START_VERSION {
1232                let package_id = deserialized_modules.first().unwrap().address();
1233                info!("adding new system package {package_id}");
1234
1235                let publish_pt = {
1236                    let mut b = ProgrammableTransactionBuilder::new();
1237                    b.command(Command::Publish(modules, dependencies));
1238                    b.finish()
1239                };
1240
1241                SPT::execute::<execution_mode::System>(
1242                    protocol_config,
1243                    metrics.clone(),
1244                    move_vm,
1245                    temporary_store,
1246                    store.as_backing_package_store(),
1247                    tx_ctx.clone(),
1248                    gas_charger,
1249                    None,
1250                    publish_pt,
1251                    trace_builder_opt,
1252                )
1253                .map_err(|(e, _)| e)
1254                .expect("System Package Publish must succeed");
1255            } else {
1256                let mut new_package = Object::new_system_package(
1257                    &deserialized_modules,
1258                    version,
1259                    dependencies,
1260                    digest,
1261                );
1262
1263                info!(
1264                    "upgraded system package {:?}",
1265                    new_package.compute_object_reference()
1266                );
1267
1268                // Decrement the version before writing the package so that the store can record the
1269                // version growing by one in the effects.
1270                new_package
1271                    .data
1272                    .try_as_package_mut()
1273                    .unwrap()
1274                    .decrement_version();
1275
1276                // upgrade of a previously existing framework module
1277                temporary_store.upgrade_system_package(new_package);
1278            }
1279        }
1280    }
1281
1282    /// Perform metadata updates in preparation for the transactions in the upcoming checkpoint:
1283    ///
1284    /// - Set the timestamp for the `Clock` shared object from the timestamp in the header from
1285    ///   consensus.
1286    fn setup_consensus_commit<Mode: ExecutionMode>(
1287        consensus_commit_timestamp_ms: CheckpointTimestamp,
1288        temporary_store: &mut TemporaryStore<'_>,
1289        store: &dyn BackingStore,
1290        tx_ctx: Rc<RefCell<TxContext>>,
1291        move_vm: &Arc<MoveRuntime>,
1292        gas_charger: &mut GasCharger,
1293        protocol_config: &ProtocolConfig,
1294        metrics: Arc<ExecutionMetrics>,
1295        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1296    ) -> Result<(), Mode::Error> {
1297        let pt = {
1298            let mut builder = ProgrammableTransactionBuilder::new();
1299            let res = builder.move_call(
1300                SUI_FRAMEWORK_ADDRESS.into(),
1301                CLOCK_MODULE_NAME.to_owned(),
1302                CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
1303                vec![],
1304                vec![
1305                    CallArg::CLOCK_MUT,
1306                    CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
1307                ],
1308            );
1309            assert_invariant!(
1310                res.is_ok(),
1311                "Unable to generate consensus_commit_prologue transaction!"
1312            );
1313            builder.finish()
1314        };
1315        SPT::execute::<execution_mode::System>(
1316            protocol_config,
1317            metrics,
1318            move_vm,
1319            temporary_store,
1320            store.as_backing_package_store(),
1321            tx_ctx,
1322            gas_charger,
1323            None,
1324            pt,
1325            trace_builder_opt,
1326        )
1327        // TODO push Mode::Error lower into the call stack and remove (implicit) into()
1328        .map_err(|(e, _)| e)?;
1329        Ok(())
1330    }
1331
1332    fn setup_authenticator_state_create(
1333        mut builder: ProgrammableTransactionBuilder,
1334    ) -> ProgrammableTransactionBuilder {
1335        builder
1336            .move_call(
1337                SUI_FRAMEWORK_ADDRESS.into(),
1338                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1339                AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
1340                vec![],
1341                vec![],
1342            )
1343            .expect("Unable to generate authenticator_state_create transaction!");
1344        builder
1345    }
1346
1347    fn setup_randomness_state_create(
1348        mut builder: ProgrammableTransactionBuilder,
1349    ) -> ProgrammableTransactionBuilder {
1350        builder
1351            .move_call(
1352                SUI_FRAMEWORK_ADDRESS.into(),
1353                RANDOMNESS_MODULE_NAME.to_owned(),
1354                RANDOMNESS_STATE_CREATE_FUNCTION_NAME.to_owned(),
1355                vec![],
1356                vec![],
1357            )
1358            .expect("Unable to generate randomness_state_create transaction!");
1359        builder
1360    }
1361
1362    fn setup_bridge_create(
1363        mut builder: ProgrammableTransactionBuilder,
1364        chain_id: ChainIdentifier,
1365    ) -> ProgrammableTransactionBuilder {
1366        let bridge_uid = builder
1367            .input(CallArg::Pure(UID::new(SUI_BRIDGE_OBJECT_ID).to_bcs_bytes()))
1368            .expect("Unable to create Bridge object UID!");
1369
1370        let bridge_chain_id = if chain_id == get_mainnet_chain_identifier() {
1371            BridgeChainId::SuiMainnet as u8
1372        } else if chain_id == get_testnet_chain_identifier() {
1373            BridgeChainId::SuiTestnet as u8
1374        } else {
1375            // How do we distinguish devnet from other test envs?
1376            BridgeChainId::SuiCustom as u8
1377        };
1378
1379        let bridge_chain_id = builder.pure(bridge_chain_id).unwrap();
1380        builder.programmable_move_call(
1381            BRIDGE_ADDRESS.into(),
1382            BRIDGE_MODULE_NAME.to_owned(),
1383            BRIDGE_CREATE_FUNCTION_NAME.to_owned(),
1384            vec![],
1385            vec![bridge_uid, bridge_chain_id],
1386        );
1387        builder
1388    }
1389
1390    fn setup_bridge_committee_update(
1391        mut builder: ProgrammableTransactionBuilder,
1392        bridge_shared_version: SequenceNumber,
1393    ) -> ProgrammableTransactionBuilder {
1394        let bridge = builder
1395            .obj(ObjectArg::SharedObject {
1396                id: SUI_BRIDGE_OBJECT_ID,
1397                initial_shared_version: bridge_shared_version,
1398                mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1399            })
1400            .expect("Unable to create Bridge object arg!");
1401        let system_state = builder
1402            .obj(ObjectArg::SUI_SYSTEM_MUT)
1403            .expect("Unable to create System State object arg!");
1404
1405        let voting_power = builder.programmable_move_call(
1406            SUI_SYSTEM_PACKAGE_ID,
1407            SUI_SYSTEM_MODULE_NAME.to_owned(),
1408            ident_str!("validator_voting_powers").to_owned(),
1409            vec![],
1410            vec![system_state],
1411        );
1412
1413        // Hardcoding min stake participation to 75.00%
1414        // TODO: We need to set a correct value or make this configurable.
1415        let min_stake_participation_percentage = builder
1416            .input(CallArg::Pure(
1417                bcs::to_bytes(&BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER).unwrap(),
1418            ))
1419            .unwrap();
1420
1421        builder.programmable_move_call(
1422            BRIDGE_ADDRESS.into(),
1423            BRIDGE_MODULE_NAME.to_owned(),
1424            BRIDGE_INIT_COMMITTEE_FUNCTION_NAME.to_owned(),
1425            vec![],
1426            vec![bridge, voting_power, min_stake_participation_percentage],
1427        );
1428        builder
1429    }
1430
1431    fn setup_authenticator_state_update<Mode: ExecutionMode>(
1432        update: AuthenticatorStateUpdate,
1433        temporary_store: &mut TemporaryStore<'_>,
1434        store: &dyn BackingStore,
1435        tx_ctx: Rc<RefCell<TxContext>>,
1436        move_vm: &Arc<MoveRuntime>,
1437        gas_charger: &mut GasCharger,
1438        protocol_config: &ProtocolConfig,
1439        metrics: Arc<ExecutionMetrics>,
1440        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1441    ) -> Result<(), Mode::Error> {
1442        let pt = {
1443            let mut builder = ProgrammableTransactionBuilder::new();
1444            let res = builder.move_call(
1445                SUI_FRAMEWORK_ADDRESS.into(),
1446                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1447                AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1448                vec![],
1449                vec![
1450                    CallArg::Object(ObjectArg::SharedObject {
1451                        id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1452                        initial_shared_version: update.authenticator_obj_initial_shared_version,
1453                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1454                    }),
1455                    CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
1456                ],
1457            );
1458            assert_invariant!(
1459                res.is_ok(),
1460                "Unable to generate authenticator_state_update transaction!"
1461            );
1462            builder.finish()
1463        };
1464        SPT::execute::<execution_mode::System>(
1465            protocol_config,
1466            metrics,
1467            move_vm,
1468            temporary_store,
1469            store.as_backing_package_store(),
1470            tx_ctx,
1471            gas_charger,
1472            None,
1473            pt,
1474            trace_builder_opt,
1475        )
1476        .map_err(|(e, _)| e)?;
1477        Ok(())
1478    }
1479
1480    fn setup_authenticator_state_expire(
1481        mut builder: ProgrammableTransactionBuilder,
1482        expire: AuthenticatorStateExpire,
1483    ) -> ProgrammableTransactionBuilder {
1484        builder
1485            .move_call(
1486                SUI_FRAMEWORK_ADDRESS.into(),
1487                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1488                AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
1489                vec![],
1490                vec![
1491                    CallArg::Object(ObjectArg::SharedObject {
1492                        id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1493                        initial_shared_version: expire.authenticator_obj_initial_shared_version,
1494                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1495                    }),
1496                    CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
1497                ],
1498            )
1499            .expect("Unable to generate authenticator_state_expire transaction!");
1500        builder
1501    }
1502
1503    fn setup_randomness_state_update<Mode: ExecutionMode>(
1504        update: RandomnessStateUpdate,
1505        temporary_store: &mut TemporaryStore<'_>,
1506        store: &dyn BackingStore,
1507        tx_ctx: Rc<RefCell<TxContext>>,
1508        move_vm: &Arc<MoveRuntime>,
1509        gas_charger: &mut GasCharger,
1510        protocol_config: &ProtocolConfig,
1511        metrics: Arc<ExecutionMetrics>,
1512        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1513    ) -> Result<(), Mode::Error> {
1514        let pt = {
1515            let mut builder = ProgrammableTransactionBuilder::new();
1516            let res = builder.move_call(
1517                SUI_FRAMEWORK_ADDRESS.into(),
1518                RANDOMNESS_MODULE_NAME.to_owned(),
1519                RANDOMNESS_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1520                vec![],
1521                vec![
1522                    CallArg::Object(ObjectArg::SharedObject {
1523                        id: SUI_RANDOMNESS_STATE_OBJECT_ID,
1524                        initial_shared_version: update.randomness_obj_initial_shared_version,
1525                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1526                    }),
1527                    CallArg::Pure(bcs::to_bytes(&update.randomness_round).unwrap()),
1528                    CallArg::Pure(bcs::to_bytes(&update.random_bytes).unwrap()),
1529                ],
1530            );
1531            assert_invariant!(
1532                res.is_ok(),
1533                "Unable to generate randomness_state_update transaction!"
1534            );
1535            builder.finish()
1536        };
1537        SPT::execute::<execution_mode::System>(
1538            protocol_config,
1539            metrics,
1540            move_vm,
1541            temporary_store,
1542            store.as_backing_package_store(),
1543            tx_ctx,
1544            gas_charger,
1545            None,
1546            pt,
1547            trace_builder_opt,
1548        )
1549        .map_err(|(e, _)| e)?;
1550        Ok(())
1551    }
1552
1553    fn setup_coin_deny_list_state_create(
1554        mut builder: ProgrammableTransactionBuilder,
1555    ) -> ProgrammableTransactionBuilder {
1556        builder
1557            .move_call(
1558                SUI_FRAMEWORK_ADDRESS.into(),
1559                DENY_LIST_MODULE.to_owned(),
1560                DENY_LIST_CREATE_FUNC.to_owned(),
1561                vec![],
1562                vec![],
1563            )
1564            .expect("Unable to generate coin_deny_list_create transaction!");
1565        builder
1566    }
1567
1568    fn setup_store_execution_time_estimates(
1569        mut builder: ProgrammableTransactionBuilder,
1570        estimates: StoredExecutionTimeObservations,
1571    ) -> ProgrammableTransactionBuilder {
1572        let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
1573        // This is stored as a vector<u8> in Move, so we first convert to bytes before again
1574        // serializing inside the call to `pure`.
1575        let estimates_bytes = bcs::to_bytes(&estimates).unwrap();
1576        let estimates_arg = builder.pure(estimates_bytes).unwrap();
1577        builder.programmable_move_call(
1578            SUI_SYSTEM_PACKAGE_ID,
1579            SUI_SYSTEM_MODULE_NAME.to_owned(),
1580            ident_str!("store_execution_time_estimates").to_owned(),
1581            vec![],
1582            vec![system_state, estimates_arg],
1583        );
1584        builder
1585    }
1586
1587    fn setup_store_execution_time_estimates_v2(
1588        mut builder: ProgrammableTransactionBuilder,
1589        estimates: StoredExecutionTimeObservations,
1590        chunk_size: usize,
1591    ) -> ProgrammableTransactionBuilder {
1592        let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
1593
1594        let estimate_chunks = estimates.chunk_observations(chunk_size);
1595
1596        let chunk_bytes: Vec<Vec<u8>> = estimate_chunks
1597            .into_iter()
1598            .map(|chunk| bcs::to_bytes(&chunk).unwrap())
1599            .collect();
1600
1601        let chunks_arg = builder.pure(chunk_bytes).unwrap();
1602
1603        builder.programmable_move_call(
1604            SUI_SYSTEM_PACKAGE_ID,
1605            SUI_SYSTEM_MODULE_NAME.to_owned(),
1606            ident_str!("store_execution_time_estimates_v2").to_owned(),
1607            vec![],
1608            vec![system_state, chunks_arg],
1609        );
1610        builder
1611    }
1612
1613    fn setup_accumulator_root_create(
1614        mut builder: ProgrammableTransactionBuilder,
1615    ) -> ProgrammableTransactionBuilder {
1616        builder
1617            .move_call(
1618                SUI_FRAMEWORK_ADDRESS.into(),
1619                ACCUMULATOR_ROOT_MODULE.to_owned(),
1620                ACCUMULATOR_ROOT_CREATE_FUNC.to_owned(),
1621                vec![],
1622                vec![],
1623            )
1624            .expect("Unable to generate accumulator_root_create transaction!");
1625        builder
1626    }
1627
1628    fn setup_write_accumulator_storage_cost(
1629        mut builder: ProgrammableTransactionBuilder,
1630        write_storage_cost: &WriteAccumulatorStorageCost,
1631    ) -> ProgrammableTransactionBuilder {
1632        let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
1633        let storage_cost_arg = builder.pure(write_storage_cost.storage_cost).unwrap();
1634        builder.programmable_move_call(
1635            SUI_SYSTEM_PACKAGE_ID,
1636            SUI_SYSTEM_MODULE_NAME.to_owned(),
1637            ident_str!("write_accumulator_storage_cost").to_owned(),
1638            vec![],
1639            vec![system_state, storage_cost_arg],
1640        );
1641        builder
1642    }
1643
1644    fn setup_coin_registry_create(
1645        mut builder: ProgrammableTransactionBuilder,
1646    ) -> ProgrammableTransactionBuilder {
1647        builder
1648            .move_call(
1649                SUI_FRAMEWORK_ADDRESS.into(),
1650                ident_str!("coin_registry").to_owned(),
1651                ident_str!("create").to_owned(),
1652                vec![],
1653                vec![],
1654            )
1655            .expect("Unable to generate coin_registry_create transaction!");
1656        builder
1657    }
1658
1659    fn setup_display_registry_create(
1660        mut builder: ProgrammableTransactionBuilder,
1661    ) -> ProgrammableTransactionBuilder {
1662        builder
1663            .move_call(
1664                SUI_FRAMEWORK_ADDRESS.into(),
1665                ident_str!("display_registry").to_owned(),
1666                ident_str!("create").to_owned(),
1667                vec![],
1668                vec![],
1669            )
1670            .expect("Unable to generate display_registry_create transaction!");
1671        builder
1672    }
1673
1674    fn setup_address_alias_state_create(
1675        mut builder: ProgrammableTransactionBuilder,
1676    ) -> ProgrammableTransactionBuilder {
1677        builder
1678            .move_call(
1679                SUI_FRAMEWORK_ADDRESS.into(),
1680                ident_str!("address_alias").to_owned(),
1681                ident_str!("create").to_owned(),
1682                vec![],
1683                vec![],
1684            )
1685            .expect("Unable to generate address_alias_state_create transaction!");
1686        builder
1687    }
1688}