sui_adapter_v2/
execution_engine.rs

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