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