Skip to main content

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