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