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