1pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7mod checked {
8
9 use crate::adapter::new_move_runtime;
10 use crate::execution_mode::{self, ExecutionMode};
11 use crate::execution_value::SuiResolver;
12 use crate::gas_charger::{PaymentKind, PaymentMethod};
13 use move_binary_format::CompiledModule;
14 use move_trace_format::format::MoveTraceBuilder;
15 use move_vm_runtime::runtime::MoveRuntime;
16 use mysten_common::{assert_reachable, debug_fatal, in_test_configuration};
17 use std::collections::{BTreeMap, BTreeSet};
18 use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc};
19 use sui_types::accumulator_root::{ACCUMULATOR_ROOT_CREATE_FUNC, ACCUMULATOR_ROOT_MODULE};
20 use sui_types::balance::{
21 BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
22 BALANCE_MODULE_NAME,
23 };
24 use sui_types::coin_reservation::ParsedDigest;
25 use sui_types::execution_params::ExecutionOrEarlyError;
26 use sui_types::gas_coin::GAS;
27 use sui_types::messages_checkpoint::CheckpointTimestamp;
28 use sui_types::metrics::ExecutionMetrics;
29 use sui_types::object::OBJECT_START_VERSION;
30 use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
31 use sui_types::randomness_state::{
32 RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_CREATE_FUNCTION_NAME,
33 RANDOMNESS_STATE_UPDATE_FUNCTION_NAME,
34 };
35 use sui_types::{BRIDGE_ADDRESS, SUI_BRIDGE_OBJECT_ID, SUI_RANDOMNESS_STATE_OBJECT_ID};
36 use tracing::{info, instrument, trace, warn};
37
38 use crate::static_programmable_transactions as SPT;
39 use crate::sui_types::gas::SuiGasStatusAPI;
40 use crate::{gas_charger::GasCharger, temporary_store::TemporaryStore};
41 use move_core_types::ident_str;
42 use move_core_types::language_storage::TypeTag;
43 use sui_move_natives::all_natives;
44 use sui_protocol_config::{
45 Chain, LimitThresholdCrossed, PerObjectCongestionControlMode, ProtocolConfig,
46 check_limit_by_meter,
47 };
48 use sui_types::authenticator_state::{
49 AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME, AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME,
50 AUTHENTICATOR_STATE_MODULE_NAME, AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
51 };
52 use sui_types::base_types::{ObjectID, SequenceNumber};
53 use sui_types::bridge::BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER;
54 use sui_types::bridge::{
55 BRIDGE_CREATE_FUNCTION_NAME, BRIDGE_INIT_COMMITTEE_FUNCTION_NAME, BRIDGE_MODULE_NAME,
56 BridgeChainId,
57 };
58 use sui_types::clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME};
59 use sui_types::committee::EpochId;
60 use sui_types::deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE};
61 use sui_types::digests::{
62 ChainIdentifier, get_mainnet_chain_identifier, get_testnet_chain_identifier,
63 };
64 use sui_types::effects::TransactionEffects;
65 use sui_types::error::{ExecutionError, ExecutionErrorTrait};
66 use sui_types::execution::{ExecutionTiming, ResultWithTimings, SharedInput};
67 use sui_types::execution_status::{ExecutionErrorKind, ExecutionStatus};
68 use sui_types::gas::GasCostSummary;
69 use sui_types::gas::SuiGasStatus;
70 use sui_types::id::UID;
71 use sui_types::inner_temporary_store::InnerTemporaryStore;
72 use sui_types::storage::BackingStore;
73 #[cfg(msim)]
74 use sui_types::sui_system_state::advance_epoch_result_injection::maybe_modify_result_for;
75 use sui_types::sui_system_state::{ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME, AdvanceEpochParams};
76 use sui_types::transaction::{
77 Argument, AuthenticatorStateExpire, AuthenticatorStateUpdate, CallArg, ChangeEpoch,
78 Command, EndOfEpochTransactionKind, GasData, GenesisTransaction, ObjectArg,
79 ProgrammableTransaction, Reservation, StoredExecutionTimeObservations, TransactionKind,
80 WithdrawFrom, WriteAccumulatorStorageCost, is_gasless_transaction,
81 };
82 use sui_types::transaction::{CheckedInputObjects, RandomnessStateUpdate};
83 use sui_types::{
84 SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_FRAMEWORK_ADDRESS, SUI_FRAMEWORK_PACKAGE_ID,
85 SUI_SYSTEM_PACKAGE_ID,
86 base_types::{SuiAddress, TransactionDigest, TxContext},
87 object::{Object, ObjectInner},
88 sui_system_state::{ADVANCE_EPOCH_FUNCTION_NAME, SUI_SYSTEM_MODULE_NAME},
89 };
90
91 fn head_error_is_insufficient_funds_for_withdraw(
96 execution_params: &ExecutionOrEarlyError,
97 ) -> bool {
98 execution_params.early_errors().is_some_and(|errors| {
99 matches!(
100 errors.head,
101 ExecutionErrorKind::InsufficientFundsForWithdraw
102 )
103 })
104 }
105
106 fn payment_kind(
107 gas_data: &GasData,
108 transaction_kind: &TransactionKind,
109 protocol_config: &ProtocolConfig,
110 ) -> PaymentKind {
111 if gas_data.is_unmetered() || transaction_kind.is_system_tx() {
112 PaymentKind::unmetered()
113 } else if protocol_config.enable_gasless()
114 && is_gasless_transaction(gas_data, transaction_kind)
115 {
116 PaymentKind::gasless()
117 } else if gas_data.payment.is_empty() {
118 PaymentKind::smash(vec![PaymentMethod::AddressBalance(
119 gas_data.owner,
120 gas_data.budget,
121 )])
122 .expect("unable to create a payment kind with a single address balance")
123 } else {
124 let payment_methods = gas_data
125 .payment
126 .iter()
127 .map(|entry| {
128 if let Ok(parsed) = ParsedDigest::try_from(entry.2) {
129 PaymentMethod::AddressBalance(gas_data.owner, parsed.reservation_amount())
130 } else {
131 PaymentMethod::Coin(*entry)
132 }
133 })
134 .collect();
135 PaymentKind::smash(payment_methods).expect(
136 "unable to create a payment kind from payment methods. \
137 Should not be possible wit ha non-empty vector",
138 )
139 }
140 }
141
142 type ExecutionOutput<Mode> = (
143 InnerTemporaryStore,
144 SuiGasStatus,
145 TransactionEffects,
146 Vec<ExecutionTiming>,
147 Result<<Mode as ExecutionMode>::ExecutionResults, <Mode as ExecutionMode>::Error>,
148 );
149 #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
150 pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
151 store: &dyn BackingStore,
152 input_objects: CheckedInputObjects,
153 gas_data: GasData,
154 gas_status: SuiGasStatus,
155 transaction_kind: TransactionKind,
156 rewritten_inputs: Option<Vec<bool>>,
157 transaction_signer: SuiAddress,
158 transaction_digest: TransactionDigest,
159 move_vm: &Arc<MoveRuntime>,
160 epoch_id: &EpochId,
161 epoch_timestamp_ms: u64,
162 protocol_config: &ProtocolConfig,
163 metrics: Arc<ExecutionMetrics>,
164 enable_expensive_checks: bool,
165 execution_params: ExecutionOrEarlyError,
166 trace_builder_opt: &mut Option<MoveTraceBuilder>,
167 ) -> ExecutionOutput<Mode> {
168 let input_objects = input_objects.into_inner();
169 let mutable_inputs = if enable_expensive_checks {
170 input_objects.all_mutable_inputs().keys().copied().collect()
171 } else {
172 HashSet::new()
173 };
174 let shared_object_refs = input_objects.filter_shared_objects();
175 let receiving_objects = transaction_kind.receiving_objects();
176 let transaction_dependencies = input_objects.transaction_dependencies();
177
178 let temporary_store = TemporaryStore::new(
179 store,
180 input_objects,
181 receiving_objects,
182 transaction_digest,
183 protocol_config,
184 *epoch_id,
185 );
186
187 legacy::execute_transaction_inner::<Mode>(
189 store,
190 temporary_store,
191 gas_data,
192 gas_status,
193 transaction_kind,
194 rewritten_inputs,
195 transaction_signer,
196 transaction_digest,
197 move_vm,
198 epoch_id,
199 epoch_timestamp_ms,
200 protocol_config,
201 metrics,
202 enable_expensive_checks,
203 execution_params,
204 trace_builder_opt,
205 shared_object_refs,
206 transaction_dependencies,
207 mutable_inputs,
208 )
209 }
210
211 fn update_vm_telemetry_metrics(metrics: &ExecutionMetrics, move_vm: &MoveRuntime) {
212 metrics.vm_telemetry_metrics.try_update(|vm_metrics| {
213 let t = move_vm.get_telemetry_report();
214 vm_metrics
215 .move_vm_package_cache_count
216 .set(t.package_cache_count as i64);
217 vm_metrics
218 .move_vm_total_arena_size_bytes
219 .set(t.total_arena_size as i64);
220 vm_metrics.move_vm_module_count.set(t.module_count as i64);
221 vm_metrics
222 .move_vm_function_count
223 .set(t.function_count as i64);
224 vm_metrics.move_vm_type_count.set(t.type_count as i64);
225 vm_metrics.move_vm_interner_size.set(t.interner_size as i64);
226 vm_metrics
227 .move_vm_vtable_cache_count
228 .set(t.vtable_cache_count as i64);
229 vm_metrics
230 .move_vm_vtable_cache_hits
231 .set(t.vtable_cache_hits as i64);
232 vm_metrics
233 .move_vm_vtable_cache_misses
234 .set(t.vtable_cache_misses as i64);
235 vm_metrics
236 .move_vm_load_time_ms
237 .set(t.total_load_time as i64);
238 vm_metrics.move_vm_load_count.set(t.load_count as i64);
239 vm_metrics
240 .move_vm_validation_time_ms
241 .set(t.total_validation_time as i64);
242 vm_metrics
243 .move_vm_validation_count
244 .set(t.validation_count as i64);
245 vm_metrics.move_vm_jit_time_ms.set(t.total_jit_time as i64);
246 vm_metrics.move_vm_jit_count.set(t.jit_count as i64);
247 vm_metrics
248 .move_vm_execution_time_ms
249 .set(t.total_execution_time as i64);
250 vm_metrics
251 .move_vm_execution_count
252 .set(t.execution_count as i64);
253 vm_metrics
254 .move_vm_interpreter_time_ms
255 .set(t.total_interpreter_time as i64);
256 vm_metrics
257 .move_vm_interpreter_count
258 .set(t.interpreter_count as i64);
259 vm_metrics
260 .move_vm_max_callstack_size
261 .set(t.max_callstack_size as i64);
262 vm_metrics
263 .move_vm_max_valuestack_size
264 .set(t.max_valuestack_size as i64);
265 vm_metrics.move_vm_total_time_ms.set(t.total_time as i64);
266 vm_metrics.move_vm_total_count.set(t.total_count as i64);
267 });
268 }
269
270 pub fn execute_genesis_state_update(
271 store: &dyn BackingStore,
272 protocol_config: &ProtocolConfig,
273 metrics: Arc<ExecutionMetrics>,
274 move_vm: &Arc<MoveRuntime>,
275 tx_context: Rc<RefCell<TxContext>>,
276 input_objects: CheckedInputObjects,
277 pt: ProgrammableTransaction,
278 ) -> Result<InnerTemporaryStore, ExecutionError> {
279 let input_objects = input_objects.into_inner();
280 let mut temporary_store = TemporaryStore::new(
281 store,
282 input_objects,
283 vec![],
284 tx_context.borrow().digest(),
285 protocol_config,
286 0,
287 );
288 let mut gas_charger = GasCharger::new_unmetered(tx_context.borrow().digest());
289 SPT::execute::<execution_mode::Genesis>(
290 protocol_config,
291 metrics,
292 move_vm,
293 &mut temporary_store,
294 store.as_backing_package_store(),
295 tx_context,
296 &mut gas_charger,
297 None,
298 pt,
299 &mut None,
300 )
301 .map_err(|(e, _)| e)?;
302 temporary_store.update_object_version_and_prev_tx();
303 Ok(temporary_store.into_inner(BTreeMap::new()))
304 }
305
306 mod legacy {
309 use super::*;
310
311 pub(super) const ADDRESS_BALANCE_SMASH_FIX_MIN_ACCUMULATOR_VERSION: SequenceNumber =
318 SequenceNumber::from_u64(692949576);
319
320 pub(super) const ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION:
330 SequenceNumber = SequenceNumber::from_u64(693531074);
331
332 pub(super) fn should_filter_address_balance_gas_smash(
337 execution_params: &ExecutionOrEarlyError,
338 protocol_config: &ProtocolConfig,
339 ) -> bool {
340 if !head_error_is_insufficient_funds_for_withdraw(execution_params) {
341 return false;
342 }
343 debug_assert!(
344 !protocol_config.early_exit_on_iffw(),
345 "Should not reach gas smashing filtering address balances if IFFW early exit is enabled"
346 );
347 in_test_configuration()
350 || protocol_config.early_exit_on_iffw()
351 || (protocol_config.chain() == Chain::Mainnet
352 && execution_params
353 .accumulator_version()
354 .is_some_and(|v| v >= ADDRESS_BALANCE_SMASH_FIX_MIN_ACCUMULATOR_VERSION))
355 }
356
357 pub(super) fn should_short_circuit_insufficient_funds(
362 execution_params: &ExecutionOrEarlyError,
363 protocol_config: &ProtocolConfig,
364 ) -> bool {
365 if !execution_params.early_errors().is_some_and(|errors| {
367 errors
368 .iter()
369 .any(|e| matches!(e, ExecutionErrorKind::InsufficientFundsForWithdraw))
370 }) {
371 return false;
372 }
373
374 if in_test_configuration() {
377 return true;
378 }
379
380 protocol_config.early_exit_on_iffw()
382 || (protocol_config.chain() == Chain::Mainnet
383 && execution_params.accumulator_version().is_some_and(|v| {
384 v >= ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION
385 }))
386 }
387
388 #[allow(clippy::too_many_arguments)]
390 pub(super) fn execute_transaction_inner<Mode: ExecutionMode>(
391 store: &dyn BackingStore,
392 mut temporary_store: TemporaryStore<'_>,
393 mut gas_data: GasData,
394 gas_status: SuiGasStatus,
395 transaction_kind: TransactionKind,
396 rewritten_inputs: Option<Vec<bool>>,
397 transaction_signer: SuiAddress,
398 transaction_digest: TransactionDigest,
399 move_vm: &Arc<MoveRuntime>,
400 epoch_id: &EpochId,
401 epoch_timestamp_ms: u64,
402 protocol_config: &ProtocolConfig,
403 metrics: Arc<ExecutionMetrics>,
404 enable_expensive_checks: bool,
405 execution_params: ExecutionOrEarlyError,
406 trace_builder_opt: &mut Option<MoveTraceBuilder>,
407 shared_object_refs: Vec<SharedInput>,
408 mut transaction_dependencies: BTreeSet<TransactionDigest>,
409 mutable_inputs: HashSet<ObjectID>,
410 ) -> ExecutionOutput<Mode> {
411 if should_short_circuit_insufficient_funds(&execution_params, protocol_config) {
418 assert_reachable!("IFFW short-circuit fired");
419 temporary_store.ensure_active_inputs_mutated();
420 transaction_dependencies.remove(&TransactionDigest::genesis_marker());
421
422 let execution_error: Mode::Error =
423 ExecutionError::from_kind(ExecutionErrorKind::InsufficientFundsForWithdraw)
424 .into();
425 let status = ExecutionStatus::new_failure(execution_error.to_execution_failure());
426 let gas_meter = GasCharger::new(
427 transaction_digest,
428 PaymentKind::gasless(),
429 gas_status,
430 &mut temporary_store,
431 protocol_config,
432 );
433
434 let gas_coin = gas_meter.gas_coin();
435 let (inner, effects) = temporary_store.into_effects(
436 shared_object_refs,
437 &transaction_digest,
438 transaction_dependencies,
439 GasCostSummary::default(),
440 status,
441 gas_coin,
442 *epoch_id,
443 );
444
445 return (
446 inner,
447 gas_meter.into_gas_status(),
448 effects,
449 vec![],
450 Err(execution_error),
451 );
452 }
453
454 let sponsor = {
455 let gas_owner = gas_data.owner;
456 if gas_owner == transaction_signer {
457 None
458 } else {
459 Some(gas_owner)
460 }
461 };
462 let gas_price = gas_status.gas_price();
463 let rgp = gas_status.reference_gas_price();
464
465 if should_filter_address_balance_gas_smash(&execution_params, protocol_config)
469 && gas_data.payment.len() > 1
470 && ParsedDigest::try_from(gas_data.payment[0].2).is_err()
471 {
472 gas_data
473 .payment
474 .retain(|entry| ParsedDigest::try_from(entry.2).is_err());
475 }
476
477 let mut gas_charger = GasCharger::new(
478 transaction_digest,
479 payment_kind(&gas_data, &transaction_kind, protocol_config),
480 gas_status,
481 &mut temporary_store,
482 protocol_config,
483 );
484
485 let tx_ctx = TxContext::new_from_components(
486 &transaction_signer,
487 &transaction_digest,
488 epoch_id,
489 epoch_timestamp_ms,
490 rgp,
491 gas_price,
492 gas_data.budget,
493 sponsor,
494 protocol_config,
495 );
496 let tx_ctx = Rc::new(RefCell::new(tx_ctx));
497
498 let is_gasless = protocol_config.enable_gasless()
499 && is_gasless_transaction(&gas_data, &transaction_kind);
500 let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
501
502 temporary_store.set_invariant_inputs(&transaction_kind, &gas_data, transaction_signer);
506
507 let (gas_cost_summary, mut execution_result, timings) = execute_transaction::<Mode>(
508 store,
509 &mut temporary_store,
510 transaction_kind,
511 rewritten_inputs,
512 &mut gas_charger,
513 tx_ctx,
514 move_vm,
515 protocol_config,
516 metrics.clone(),
517 execution_params,
518 trace_builder_opt,
519 is_gasless,
520 );
521
522 if let Err(e) = run_invariant_checks::<Mode>(
525 &mut temporary_store,
526 &mut gas_charger,
527 transaction_digest,
528 move_vm,
529 protocol_config,
530 enable_expensive_checks,
531 &gas_cost_summary,
532 &transaction_signer,
533 &sponsor,
534 &mutable_inputs,
535 is_epoch_change,
536 ) {
537 execution_result = Err(e);
539 }
540
541 let status = if let Err(error) = &execution_result {
542 ExecutionStatus::new_failure(error.to_execution_failure())
543 } else {
544 ExecutionStatus::Success
545 };
546
547 #[skip_checked_arithmetic]
548 trace!(
549 tx_digest = ?transaction_digest,
550 computation_gas_cost = gas_cost_summary.computation_cost,
551 storage_gas_cost = gas_cost_summary.storage_cost,
552 storage_gas_rebate = gas_cost_summary.storage_rebate,
553 "Finished execution of transaction with status {:?}",
554 status
555 );
556
557 transaction_dependencies.remove(&TransactionDigest::genesis_marker());
561
562 let gas_coin = gas_charger.gas_coin();
563 let (inner, effects) = temporary_store.into_effects(
564 shared_object_refs,
565 &transaction_digest,
566 transaction_dependencies,
567 gas_cost_summary,
568 status,
569 gas_coin,
570 *epoch_id,
571 );
572
573 if !Mode::TRACK_EXECUTION {
576 update_vm_telemetry_metrics(&metrics, move_vm);
577 }
578
579 (
580 inner,
581 gas_charger.into_gas_status(),
582 effects,
583 timings,
584 execution_result,
585 )
586 }
587
588 #[instrument(name = "tx_execute", level = "debug", skip_all)]
589 fn execute_transaction<Mode: ExecutionMode>(
590 store: &dyn BackingStore,
591 temporary_store: &mut TemporaryStore<'_>,
592 transaction_kind: TransactionKind,
593 rewritten_inputs: Option<Vec<bool>>,
594 gas_charger: &mut GasCharger,
595 tx_ctx: Rc<RefCell<TxContext>>,
596 move_vm: &Arc<MoveRuntime>,
597 protocol_config: &ProtocolConfig,
598 metrics: Arc<ExecutionMetrics>,
599 execution_params: ExecutionOrEarlyError,
600 trace_builder_opt: &mut Option<MoveTraceBuilder>,
601 is_gasless: bool,
602 ) -> (
603 GasCostSummary,
604 Result<Mode::ExecutionResults, Mode::Error>,
605 Vec<ExecutionTiming>,
606 ) {
607 debug_assert!(
609 gas_charger.no_charges(),
610 "No gas charges must be applied yet"
611 );
612
613 let withdrawal_reservations =
614 if is_gasless && protocol_config.gasless_verify_remaining_balance() {
615 gasless_withdrawal_reservations(&transaction_kind, &tx_ctx.borrow())
616 } else {
617 None
618 };
619
620 let result = gas_charger.charge_input_objects_legacy(temporary_store);
623
624 let result: ResultWithTimings<Mode::ExecutionResults, Mode::Error> =
625 result.map_err(|e| (e.into(), vec![])).and_then(
626 |()| -> ResultWithTimings<Mode::ExecutionResults, Mode::Error> {
627 let mut execution_result: ResultWithTimings<
628 Mode::ExecutionResults,
629 Mode::Error,
630 > = match execution_params.into_early_errors() {
631 Some(early_execution_errors) => {
632 Err((Mode::Error::from_kind(early_execution_errors.head), vec![]))
633 }
634 None => execution_loop::<Mode>(
635 store,
636 temporary_store,
637 transaction_kind,
638 rewritten_inputs,
639 tx_ctx,
640 move_vm,
641 gas_charger,
642 protocol_config,
643 metrics.clone(),
644 trace_builder_opt,
645 ),
646 };
647
648 let meter_check = check_meter_limit::<Mode>(
649 temporary_store,
650 gas_charger,
651 protocol_config,
652 metrics.clone(),
653 );
654 if let Err(e) = meter_check {
655 execution_result = Err((e, vec![]));
656 }
657
658 if execution_result.is_ok() {
659 let gas_check = check_written_objects_limit::<Mode>(
660 temporary_store,
661 gas_charger,
662 protocol_config,
663 metrics,
664 );
665 if let Err(e) = gas_check {
666 execution_result = Err((e, vec![]));
667 }
668 }
669
670 execution_result
671 },
672 );
673
674 let (mut result, timings) = match result {
675 Ok((r, t)) => (Ok(r), t),
676 Err((e, t)) => (Err(e), t),
677 };
678 if is_gasless
679 && result.is_ok()
680 && let Err(msg) = temporary_store
681 .check_gasless_execution_requirements(withdrawal_reservations.as_ref())
682 {
683 result = Err(Mode::Error::new_with_source(
684 ExecutionErrorKind::InsufficientGas,
685 msg,
686 ));
687 }
688
689 if result.is_ok()
702 && let Err(e) = temporary_store.check_accumulator_amounts_representable()
703 {
704 result = Err(e.into());
705 }
706
707 let cost_summary =
708 gas_charger.legacy_charge_gas(temporary_store, protocol_config, &mut result);
709 temporary_store
717 .conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
718
719 (cost_summary, result, timings)
720 }
721
722 #[allow(clippy::too_many_arguments)]
735 fn run_invariant_checks<Mode: ExecutionMode>(
736 temporary_store: &mut TemporaryStore<'_>,
737 gas_charger: &mut GasCharger,
738 tx_digest: TransactionDigest,
739 move_vm: &Arc<MoveRuntime>,
740 protocol_config: &ProtocolConfig,
741 enable_expensive_checks: bool,
742 cost_summary: &GasCostSummary,
743 sender: &SuiAddress,
744 sponsor: &Option<SuiAddress>,
745 mutable_inputs: &HashSet<ObjectID>,
746 is_epoch_change: bool,
747 ) -> Result<(), Mode::Error> {
748 let conservation = run_conservation_checks::<Mode>(
749 temporary_store,
750 gas_charger,
751 tx_digest,
752 move_vm,
753 protocol_config,
754 enable_expensive_checks,
755 cost_summary,
756 );
757 if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
758 temporary_store
759 .check_ownership_invariants(
760 sender,
761 sponsor,
762 gas_charger,
763 mutable_inputs,
764 is_epoch_change,
765 )
766 .unwrap()
767 } conservation
769 }
770
771 #[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
778 fn run_conservation_checks<Mode: ExecutionMode>(
779 temporary_store: &mut TemporaryStore<'_>,
780 gas_charger: &mut GasCharger,
781 tx_digest: TransactionDigest,
782 move_vm: &Arc<MoveRuntime>,
783 protocol_config: &ProtocolConfig,
784 enable_expensive_checks: bool,
785 cost_summary: &GasCostSummary,
786 ) -> Result<(), Mode::Error> {
787 let Err(conservation_err) = temporary_store.check_conservation_invariants::<Mode>(
788 move_vm,
789 enable_expensive_checks,
790 cost_summary,
791 ) else {
792 return Ok(());
793 };
794
795 let mut result: Result<(), Mode::Error> = Err(conservation_err.into());
799 gas_charger.reset(temporary_store);
800 gas_charger.legacy_charge_gas(temporary_store, protocol_config, &mut result);
801 if let Err(recovery_err) = temporary_store.check_conservation_invariants::<Mode>(
802 move_vm,
803 enable_expensive_checks,
804 cost_summary,
805 ) {
806 panic!(
810 "SUI conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
811 tx_digest,
812 recovery_err,
813 gas_charger.summary()
814 )
815 }
816 result
817 }
818 }
819
820 #[instrument(name = "check_meter_limit", level = "debug", skip_all)]
821 fn check_meter_limit<Mode: ExecutionMode>(
822 temporary_store: &mut TemporaryStore<'_>,
823 gas_charger: &mut GasCharger,
824 protocol_config: &ProtocolConfig,
825 metrics: Arc<ExecutionMetrics>,
826 ) -> Result<(), Mode::Error> {
827 let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
828
829 match check_limit_by_meter!(
833 !gas_charger.is_unmetered(),
834 effects_estimated_size,
835 protocol_config.max_serialized_tx_effects_size_bytes(),
836 protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
837 metrics.limits_metrics.excessive_estimated_effects_size
838 ) {
839 LimitThresholdCrossed::None => Ok(()),
840 LimitThresholdCrossed::Soft(_, limit) => {
841 warn!(
842 effects_estimated_size = effects_estimated_size,
843 soft_limit = limit,
844 "Estimated transaction effects size crossed soft limit",
845 );
846 Ok(())
847 }
848 LimitThresholdCrossed::Hard(_, lim) => Err(Mode::Error::new_with_source(
849 ExecutionErrorKind::EffectsTooLarge {
850 current_size: effects_estimated_size as u64,
851 max_size: lim as u64,
852 },
853 "Transaction effects are too large",
854 )),
855 }
856 }
857
858 #[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
859 fn check_written_objects_limit<Mode: ExecutionMode>(
860 temporary_store: &mut TemporaryStore<'_>,
861 gas_charger: &mut GasCharger,
862 protocol_config: &ProtocolConfig,
863 metrics: Arc<ExecutionMetrics>,
864 ) -> Result<(), Mode::Error> {
865 if let (Some(normal_lim), Some(system_lim)) = (
866 protocol_config.max_size_written_objects_as_option(),
867 protocol_config.max_size_written_objects_system_tx_as_option(),
868 ) {
869 let written_objects_size = temporary_store.written_objects_size();
870
871 match check_limit_by_meter!(
872 !gas_charger.is_unmetered(),
873 written_objects_size,
874 normal_lim,
875 system_lim,
876 metrics.limits_metrics.excessive_written_objects_size
877 ) {
878 LimitThresholdCrossed::None => (),
879 LimitThresholdCrossed::Soft(_, limit) => {
880 warn!(
881 written_objects_size = written_objects_size,
882 soft_limit = limit,
883 "Written objects size crossed soft limit",
884 )
885 }
886 LimitThresholdCrossed::Hard(_, lim) => {
887 return Err(Mode::Error::new_with_source(
888 ExecutionErrorKind::WrittenObjectsTooLarge {
889 current_size: written_objects_size as u64,
890 max_size: lim as u64,
891 },
892 "Written objects size crossed hard limit",
893 ));
894 }
895 };
896 }
897
898 Ok(())
899 }
900
901 fn gasless_withdrawal_reservations(
902 transaction_kind: &TransactionKind,
903 tx_ctx: &TxContext,
904 ) -> Option<BTreeMap<(SuiAddress, TypeTag), u64>> {
905 let TransactionKind::ProgrammableTransaction(pt) = transaction_kind else {
906 debug_fatal!("Gasless transaction must be a ProgrammableTransaction");
907 return None;
908 };
909 let sender = tx_ctx.sender();
910 let mut reservations = BTreeMap::<(SuiAddress, TypeTag), u64>::new();
911 for input in &pt.inputs {
912 let CallArg::FundsWithdrawal(fw) = input else {
913 continue;
914 };
915 let Some(coin_type) = fw.type_arg.get_balance_type_param() else {
916 debug_fatal!("expected Balance type for withdrawal");
917 continue;
918 };
919 let owner = match fw.withdraw_from {
920 WithdrawFrom::Sender => sender,
921 WithdrawFrom::Sponsor => {
922 debug_fatal!("WithdrawFrom::Sponsor is not expected in gasless transactions");
923 tx_ctx.sponsor().unwrap_or(sender)
924 }
925 };
926 let Reservation::MaxAmountU64(amount) = fw.reservation;
927 let entry = reservations.entry((owner, coin_type)).or_insert(0);
928 *entry = entry.saturating_add(amount);
929 }
930 Some(reservations)
931 }
932
933 #[instrument(level = "debug", skip_all)]
934 fn execution_loop<Mode: ExecutionMode>(
935 store: &dyn BackingStore,
936 temporary_store: &mut TemporaryStore<'_>,
937 transaction_kind: TransactionKind,
938 rewritten_inputs: Option<Vec<bool>>,
939 tx_ctx: Rc<RefCell<TxContext>>,
940 move_vm: &Arc<MoveRuntime>,
941 gas_charger: &mut GasCharger,
942 protocol_config: &ProtocolConfig,
943 metrics: Arc<ExecutionMetrics>,
944 trace_builder_opt: &mut Option<MoveTraceBuilder>,
945 ) -> ResultWithTimings<Mode::ExecutionResults, Mode::Error> {
946 let result = match transaction_kind {
947 TransactionKind::ChangeEpoch(change_epoch) => {
948 let builder = ProgrammableTransactionBuilder::new();
949 advance_epoch::<Mode>(
950 builder,
951 change_epoch,
952 temporary_store,
953 store,
954 tx_ctx,
955 move_vm,
956 gas_charger,
957 protocol_config,
958 metrics,
959 trace_builder_opt,
960 )
961 .map_err(|e| (e, vec![]))?;
962 Ok((Mode::empty_results(), vec![]))
963 }
964 TransactionKind::Genesis(GenesisTransaction { objects }) => {
965 if tx_ctx.borrow().epoch() != 0 {
966 panic!("BUG: Genesis Transactions can only be executed in epoch 0");
967 }
968
969 for genesis_object in objects {
970 match genesis_object {
971 sui_types::transaction::GenesisObject::RawObject { data, owner } => {
972 let object = ObjectInner {
973 data,
974 owner,
975 previous_transaction: tx_ctx.borrow().digest(),
976 storage_rebate: 0,
977 };
978 temporary_store.create_object(object.into());
979 }
980 }
981 }
982 Ok((Mode::empty_results(), vec![]))
983 }
984 TransactionKind::ConsensusCommitPrologue(prologue) => {
985 setup_consensus_commit::<Mode>(
986 prologue.commit_timestamp_ms,
987 temporary_store,
988 store,
989 tx_ctx,
990 move_vm,
991 gas_charger,
992 protocol_config,
993 metrics,
994 trace_builder_opt,
995 )
996 .expect("ConsensusCommitPrologue cannot fail");
997 Ok((Mode::empty_results(), vec![]))
998 }
999 TransactionKind::ConsensusCommitPrologueV2(prologue) => {
1000 setup_consensus_commit::<Mode>(
1001 prologue.commit_timestamp_ms,
1002 temporary_store,
1003 store,
1004 tx_ctx,
1005 move_vm,
1006 gas_charger,
1007 protocol_config,
1008 metrics,
1009 trace_builder_opt,
1010 )
1011 .expect("ConsensusCommitPrologueV2 cannot fail");
1012 Ok((Mode::empty_results(), vec![]))
1013 }
1014 TransactionKind::ConsensusCommitPrologueV3(prologue) => {
1015 setup_consensus_commit::<Mode>(
1016 prologue.commit_timestamp_ms,
1017 temporary_store,
1018 store,
1019 tx_ctx,
1020 move_vm,
1021 gas_charger,
1022 protocol_config,
1023 metrics,
1024 trace_builder_opt,
1025 )
1026 .expect("ConsensusCommitPrologueV3 cannot fail");
1027 Ok((Mode::empty_results(), vec![]))
1028 }
1029 TransactionKind::ConsensusCommitPrologueV4(prologue) => {
1030 setup_consensus_commit::<Mode>(
1031 prologue.commit_timestamp_ms,
1032 temporary_store,
1033 store,
1034 tx_ctx,
1035 move_vm,
1036 gas_charger,
1037 protocol_config,
1038 metrics,
1039 trace_builder_opt,
1040 )
1041 .expect("ConsensusCommitPrologue cannot fail");
1042 Ok((Mode::empty_results(), vec![]))
1043 }
1044 TransactionKind::ProgrammableTransaction(pt) => SPT::execute::<Mode>(
1045 protocol_config,
1046 metrics,
1047 move_vm,
1048 temporary_store,
1049 store.as_backing_package_store(),
1050 tx_ctx,
1051 gas_charger,
1052 rewritten_inputs,
1053 pt,
1054 trace_builder_opt,
1055 ),
1056 TransactionKind::ProgrammableSystemTransaction(pt) => {
1057 SPT::execute::<execution_mode::System<Mode::Error>>(
1058 protocol_config,
1059 metrics,
1060 move_vm,
1061 temporary_store,
1062 store.as_backing_package_store(),
1063 tx_ctx,
1064 gas_charger,
1065 None,
1066 pt,
1067 trace_builder_opt,
1068 )
1069 .map_err(|(e, _)| (e, vec![]))?;
1070 Ok((Mode::empty_results(), vec![]))
1071 }
1072 TransactionKind::EndOfEpochTransaction(txns) => {
1073 let mut builder = ProgrammableTransactionBuilder::new();
1074 let len = txns.len();
1075 for (i, tx) in txns.into_iter().enumerate() {
1076 match tx {
1077 EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
1078 assert_eq!(i, len - 1);
1079 advance_epoch::<Mode>(
1080 builder,
1081 change_epoch,
1082 temporary_store,
1083 store,
1084 tx_ctx,
1085 move_vm,
1086 gas_charger,
1087 protocol_config,
1088 metrics,
1089 trace_builder_opt,
1090 )
1091 .map_err(|e| (e, vec![]))?;
1092 return Ok((Mode::empty_results(), vec![]));
1093 }
1094 EndOfEpochTransactionKind::AuthenticatorStateCreate => {
1095 assert!(protocol_config.enable_jwk_consensus_updates());
1096 builder = setup_authenticator_state_create(builder);
1097 }
1098 EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
1099 assert!(protocol_config.enable_jwk_consensus_updates());
1100
1101 builder = setup_authenticator_state_expire(builder, expire);
1104 }
1105 EndOfEpochTransactionKind::RandomnessStateCreate => {
1106 assert!(protocol_config.random_beacon());
1107 builder = setup_randomness_state_create(builder);
1108 }
1109 EndOfEpochTransactionKind::DenyListStateCreate => {
1110 assert!(protocol_config.enable_coin_deny_list_v1());
1111 builder = setup_coin_deny_list_state_create(builder);
1112 }
1113 EndOfEpochTransactionKind::BridgeStateCreate(chain_id) => {
1114 assert!(protocol_config.enable_bridge());
1115 builder = setup_bridge_create(builder, chain_id)
1116 }
1117 EndOfEpochTransactionKind::BridgeCommitteeInit(bridge_shared_version) => {
1118 assert!(protocol_config.enable_bridge());
1119 assert!(protocol_config.should_try_to_finalize_bridge_committee());
1120 builder = setup_bridge_committee_update(builder, bridge_shared_version)
1121 }
1122 EndOfEpochTransactionKind::StoreExecutionTimeObservations(estimates) => {
1123 if let PerObjectCongestionControlMode::ExecutionTimeEstimate(params) =
1124 protocol_config.per_object_congestion_control_mode()
1125 {
1126 if let Some(chunk_size) = params.observations_chunk_size {
1127 builder = setup_store_execution_time_estimates_v2(
1128 builder,
1129 estimates,
1130 chunk_size as usize,
1131 );
1132 } else {
1133 builder =
1134 setup_store_execution_time_estimates(builder, estimates);
1135 }
1136 }
1137 }
1138 EndOfEpochTransactionKind::AccumulatorRootCreate => {
1139 assert!(protocol_config.create_root_accumulator_object());
1140 builder = setup_accumulator_root_create(builder);
1141 }
1142 EndOfEpochTransactionKind::WriteAccumulatorStorageCost(
1143 write_storage_cost,
1144 ) => {
1145 assert!(protocol_config.enable_accumulators());
1146 builder =
1147 setup_write_accumulator_storage_cost(builder, &write_storage_cost);
1148 }
1149 EndOfEpochTransactionKind::CoinRegistryCreate => {
1150 assert!(protocol_config.enable_coin_registry());
1151 builder = setup_coin_registry_create(builder);
1152 }
1153 EndOfEpochTransactionKind::DisplayRegistryCreate => {
1154 assert!(protocol_config.enable_display_registry());
1155 builder = setup_display_registry_create(builder);
1156 }
1157 EndOfEpochTransactionKind::AddressAliasStateCreate => {
1158 assert!(protocol_config.address_aliases());
1159 builder = setup_address_alias_state_create(builder);
1160 }
1161 }
1162 }
1163 unreachable!(
1164 "EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list"
1165 )
1166 }
1167 TransactionKind::AuthenticatorStateUpdate(auth_state_update) => {
1168 setup_authenticator_state_update::<Mode>(
1169 auth_state_update,
1170 temporary_store,
1171 store,
1172 tx_ctx,
1173 move_vm,
1174 gas_charger,
1175 protocol_config,
1176 metrics,
1177 trace_builder_opt,
1178 )
1179 .map_err(|e| (e, vec![]))?;
1180 Ok((Mode::empty_results(), vec![]))
1181 }
1182 TransactionKind::RandomnessStateUpdate(randomness_state_update) => {
1183 setup_randomness_state_update::<Mode>(
1184 randomness_state_update,
1185 temporary_store,
1186 store,
1187 tx_ctx,
1188 move_vm,
1189 gas_charger,
1190 protocol_config,
1191 metrics,
1192 trace_builder_opt,
1193 )
1194 .map_err(|e| (e, vec![]))?;
1195 Ok((Mode::empty_results(), vec![]))
1196 }
1197 }?;
1198 temporary_store
1199 .check_execution_results_consistency::<Mode>()
1200 .map_err(|e| (e, vec![]))?;
1201 Ok(result)
1202 }
1203
1204 fn mint_epoch_rewards_in_pt(
1205 builder: &mut ProgrammableTransactionBuilder,
1206 params: &AdvanceEpochParams,
1207 ) -> (Argument, Argument) {
1208 let storage_charge_arg = builder
1210 .input(CallArg::Pure(
1211 bcs::to_bytes(¶ms.storage_charge).unwrap(),
1212 ))
1213 .unwrap();
1214 let storage_rewards = builder.programmable_move_call(
1215 SUI_FRAMEWORK_PACKAGE_ID,
1216 BALANCE_MODULE_NAME.to_owned(),
1217 BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
1218 vec![GAS::type_tag()],
1219 vec![storage_charge_arg],
1220 );
1221
1222 let computation_charge_arg = builder
1224 .input(CallArg::Pure(
1225 bcs::to_bytes(¶ms.computation_charge).unwrap(),
1226 ))
1227 .unwrap();
1228 let computation_rewards = builder.programmable_move_call(
1229 SUI_FRAMEWORK_PACKAGE_ID,
1230 BALANCE_MODULE_NAME.to_owned(),
1231 BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
1232 vec![GAS::type_tag()],
1233 vec![computation_charge_arg],
1234 );
1235 (storage_rewards, computation_rewards)
1236 }
1237
1238 pub fn construct_advance_epoch_pt<Mode: ExecutionMode>(
1239 mut builder: ProgrammableTransactionBuilder,
1240 params: &AdvanceEpochParams,
1241 ) -> Result<ProgrammableTransaction, Mode::Error> {
1242 let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
1244
1245 let mut arguments = vec![storage_rewards, computation_rewards];
1247 let call_arg_arguments = vec![
1248 CallArg::SUI_SYSTEM_MUT,
1249 CallArg::Pure(bcs::to_bytes(¶ms.epoch).unwrap()),
1250 CallArg::Pure(bcs::to_bytes(¶ms.next_protocol_version.as_u64()).unwrap()),
1251 CallArg::Pure(bcs::to_bytes(¶ms.storage_rebate).unwrap()),
1252 CallArg::Pure(bcs::to_bytes(¶ms.non_refundable_storage_fee).unwrap()),
1253 CallArg::Pure(bcs::to_bytes(¶ms.storage_fund_reinvest_rate).unwrap()),
1254 CallArg::Pure(bcs::to_bytes(¶ms.reward_slashing_rate).unwrap()),
1255 CallArg::Pure(bcs::to_bytes(¶ms.epoch_start_timestamp_ms).unwrap()),
1256 ]
1257 .into_iter()
1258 .map(|a| builder.input(a))
1259 .collect::<Result<_, _>>();
1260
1261 assert_invariant!(
1262 call_arg_arguments.is_ok(),
1263 "Unable to generate args for advance_epoch transaction!"
1264 );
1265
1266 arguments.append(&mut call_arg_arguments.unwrap());
1267
1268 info!("Call arguments to advance_epoch transaction: {:?}", params);
1269
1270 let storage_rebates = builder.programmable_move_call(
1271 SUI_SYSTEM_PACKAGE_ID,
1272 SUI_SYSTEM_MODULE_NAME.to_owned(),
1273 ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
1274 vec![],
1275 arguments,
1276 );
1277
1278 builder.programmable_move_call(
1280 SUI_FRAMEWORK_PACKAGE_ID,
1281 BALANCE_MODULE_NAME.to_owned(),
1282 BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
1283 vec![GAS::type_tag()],
1284 vec![storage_rebates],
1285 );
1286 Ok(builder.finish())
1287 }
1288
1289 pub fn construct_advance_epoch_safe_mode_pt(
1290 params: &AdvanceEpochParams,
1291 ) -> Result<ProgrammableTransaction, ExecutionError> {
1292 let mut builder = ProgrammableTransactionBuilder::new();
1293 let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
1295
1296 let mut arguments = vec![storage_rewards, computation_rewards];
1298
1299 let mut args = vec![
1300 CallArg::SUI_SYSTEM_MUT,
1301 CallArg::Pure(bcs::to_bytes(¶ms.epoch).unwrap()),
1302 CallArg::Pure(bcs::to_bytes(¶ms.next_protocol_version.as_u64()).unwrap()),
1303 CallArg::Pure(bcs::to_bytes(¶ms.storage_rebate).unwrap()),
1304 CallArg::Pure(bcs::to_bytes(¶ms.non_refundable_storage_fee).unwrap()),
1305 ];
1306
1307 args.push(CallArg::Pure(
1308 bcs::to_bytes(¶ms.epoch_start_timestamp_ms).unwrap(),
1309 ));
1310
1311 let call_arg_arguments = args
1312 .into_iter()
1313 .map(|a| builder.input(a))
1314 .collect::<Result<_, _>>();
1315
1316 assert_invariant!(
1317 call_arg_arguments.is_ok(),
1318 "Unable to generate args for advance_epoch transaction!"
1319 );
1320
1321 arguments.append(&mut call_arg_arguments.unwrap());
1322
1323 info!("Call arguments to advance_epoch transaction: {:?}", params);
1324
1325 builder.programmable_move_call(
1326 SUI_SYSTEM_PACKAGE_ID,
1327 SUI_SYSTEM_MODULE_NAME.to_owned(),
1328 ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME.to_owned(),
1329 vec![],
1330 arguments,
1331 );
1332
1333 Ok(builder.finish())
1334 }
1335
1336 fn advance_epoch<Mode: ExecutionMode>(
1337 builder: ProgrammableTransactionBuilder,
1338 change_epoch: ChangeEpoch,
1339 temporary_store: &mut TemporaryStore<'_>,
1340 store: &dyn BackingStore,
1341 tx_ctx: Rc<RefCell<TxContext>>,
1342 move_vm: &Arc<MoveRuntime>,
1343 gas_charger: &mut GasCharger,
1344 protocol_config: &ProtocolConfig,
1345 metrics: Arc<ExecutionMetrics>,
1346 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1347 ) -> Result<(), Mode::Error> {
1348 let params = AdvanceEpochParams {
1349 epoch: change_epoch.epoch,
1350 next_protocol_version: change_epoch.protocol_version,
1351 storage_charge: change_epoch.storage_charge,
1352 computation_charge: change_epoch.computation_charge,
1353 storage_rebate: change_epoch.storage_rebate,
1354 non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
1355 storage_fund_reinvest_rate: protocol_config.storage_fund_reinvest_rate(),
1356 reward_slashing_rate: protocol_config.reward_slashing_rate(),
1357 epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
1358 };
1359 let advance_epoch_pt = construct_advance_epoch_pt::<Mode>(builder, ¶ms)?;
1360 let result = SPT::execute::<execution_mode::System<Mode::Error>>(
1361 protocol_config,
1362 metrics.clone(),
1363 move_vm,
1364 temporary_store,
1365 store.as_backing_package_store(),
1366 tx_ctx.clone(),
1367 gas_charger,
1368 None,
1369 advance_epoch_pt,
1370 trace_builder_opt,
1371 );
1372
1373 #[cfg(msim)]
1374 let result = maybe_modify_result_for(result, change_epoch.epoch);
1375
1376 if let Err(err) = &result {
1377 tracing::error!(
1378 "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx data: {:?}",
1379 err.0,
1380 temporary_store.objects(),
1381 change_epoch,
1382 );
1383 temporary_store.drop_writes();
1384 gas_charger.reset_storage_cost_and_rebate();
1386
1387 temporary_store.advance_epoch_safe_mode(¶ms, protocol_config);
1388 }
1389
1390 let new_vm = new_move_runtime(
1391 all_natives(true, protocol_config),
1392 protocol_config,
1393 )
1394 .expect("Failed to create new MoveRuntime");
1395 process_system_packages(
1396 change_epoch,
1397 temporary_store,
1398 store,
1399 tx_ctx,
1400 &new_vm,
1401 gas_charger,
1402 protocol_config,
1403 metrics,
1404 trace_builder_opt,
1405 );
1406 Ok(())
1407 }
1408
1409 fn process_system_packages(
1410 change_epoch: ChangeEpoch,
1411 temporary_store: &mut TemporaryStore<'_>,
1412 store: &dyn BackingStore,
1413 tx_ctx: Rc<RefCell<TxContext>>,
1414 move_vm: &MoveRuntime,
1415 gas_charger: &mut GasCharger,
1416 protocol_config: &ProtocolConfig,
1417 metrics: Arc<ExecutionMetrics>,
1418 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1419 ) {
1420 let digest = tx_ctx.borrow().digest();
1421 let binary_config = protocol_config.binary_config(None);
1422 for (version, modules, dependencies) in change_epoch.system_packages.into_iter() {
1423 let deserialized_modules: Vec<_> = modules
1424 .iter()
1425 .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
1426 .collect();
1427
1428 if version == OBJECT_START_VERSION {
1429 let package_id = deserialized_modules.first().unwrap().address();
1430 info!("adding new system package {package_id}");
1431
1432 let publish_pt = {
1433 let mut b = ProgrammableTransactionBuilder::new();
1434 b.command(Command::Publish(modules, dependencies));
1435 b.finish()
1436 };
1437
1438 SPT::execute::<execution_mode::System>(
1439 protocol_config,
1440 metrics.clone(),
1441 move_vm,
1442 temporary_store,
1443 store.as_backing_package_store(),
1444 tx_ctx.clone(),
1445 gas_charger,
1446 None,
1447 publish_pt,
1448 trace_builder_opt,
1449 )
1450 .map_err(|(e, _)| e)
1451 .expect("System Package Publish must succeed");
1452 } else {
1453 let mut new_package = Object::new_system_package(
1454 &deserialized_modules,
1455 version,
1456 dependencies,
1457 digest,
1458 );
1459
1460 info!(
1461 "upgraded system package {:?}",
1462 new_package.compute_object_reference()
1463 );
1464
1465 new_package
1468 .data
1469 .try_as_package_mut()
1470 .unwrap()
1471 .decrement_version();
1472
1473 temporary_store.upgrade_system_package(new_package);
1475 }
1476 }
1477 }
1478
1479 fn setup_consensus_commit<Mode: ExecutionMode>(
1484 consensus_commit_timestamp_ms: CheckpointTimestamp,
1485 temporary_store: &mut TemporaryStore<'_>,
1486 store: &dyn BackingStore,
1487 tx_ctx: Rc<RefCell<TxContext>>,
1488 move_vm: &Arc<MoveRuntime>,
1489 gas_charger: &mut GasCharger,
1490 protocol_config: &ProtocolConfig,
1491 metrics: Arc<ExecutionMetrics>,
1492 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1493 ) -> Result<(), Mode::Error> {
1494 let pt = {
1495 let mut builder = ProgrammableTransactionBuilder::new();
1496 let res = builder.move_call(
1497 SUI_FRAMEWORK_ADDRESS.into(),
1498 CLOCK_MODULE_NAME.to_owned(),
1499 CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
1500 vec![],
1501 vec![
1502 CallArg::CLOCK_MUT,
1503 CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
1504 ],
1505 );
1506 assert_invariant!(
1507 res.is_ok(),
1508 "Unable to generate consensus_commit_prologue transaction!"
1509 );
1510 builder.finish()
1511 };
1512 SPT::execute::<execution_mode::System<Mode::Error>>(
1513 protocol_config,
1514 metrics,
1515 move_vm,
1516 temporary_store,
1517 store.as_backing_package_store(),
1518 tx_ctx,
1519 gas_charger,
1520 None,
1521 pt,
1522 trace_builder_opt,
1523 )
1524 .map_err(|(e, _)| e)?;
1525 Ok(())
1526 }
1527
1528 fn setup_authenticator_state_create(
1529 mut builder: ProgrammableTransactionBuilder,
1530 ) -> ProgrammableTransactionBuilder {
1531 builder
1532 .move_call(
1533 SUI_FRAMEWORK_ADDRESS.into(),
1534 AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1535 AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
1536 vec![],
1537 vec![],
1538 )
1539 .expect("Unable to generate authenticator_state_create transaction!");
1540 builder
1541 }
1542
1543 fn setup_randomness_state_create(
1544 mut builder: ProgrammableTransactionBuilder,
1545 ) -> ProgrammableTransactionBuilder {
1546 builder
1547 .move_call(
1548 SUI_FRAMEWORK_ADDRESS.into(),
1549 RANDOMNESS_MODULE_NAME.to_owned(),
1550 RANDOMNESS_STATE_CREATE_FUNCTION_NAME.to_owned(),
1551 vec![],
1552 vec![],
1553 )
1554 .expect("Unable to generate randomness_state_create transaction!");
1555 builder
1556 }
1557
1558 fn setup_bridge_create(
1559 mut builder: ProgrammableTransactionBuilder,
1560 chain_id: ChainIdentifier,
1561 ) -> ProgrammableTransactionBuilder {
1562 let bridge_uid = builder
1563 .input(CallArg::Pure(UID::new(SUI_BRIDGE_OBJECT_ID).to_bcs_bytes()))
1564 .expect("Unable to create Bridge object UID!");
1565
1566 let bridge_chain_id = if chain_id == get_mainnet_chain_identifier() {
1567 BridgeChainId::SuiMainnet as u8
1568 } else if chain_id == get_testnet_chain_identifier() {
1569 BridgeChainId::SuiTestnet as u8
1570 } else {
1571 BridgeChainId::SuiCustom as u8
1573 };
1574
1575 let bridge_chain_id = builder.pure(bridge_chain_id).unwrap();
1576 builder.programmable_move_call(
1577 BRIDGE_ADDRESS.into(),
1578 BRIDGE_MODULE_NAME.to_owned(),
1579 BRIDGE_CREATE_FUNCTION_NAME.to_owned(),
1580 vec![],
1581 vec![bridge_uid, bridge_chain_id],
1582 );
1583 builder
1584 }
1585
1586 fn setup_bridge_committee_update(
1587 mut builder: ProgrammableTransactionBuilder,
1588 bridge_shared_version: SequenceNumber,
1589 ) -> ProgrammableTransactionBuilder {
1590 let bridge = builder
1591 .obj(ObjectArg::SharedObject {
1592 id: SUI_BRIDGE_OBJECT_ID,
1593 initial_shared_version: bridge_shared_version,
1594 mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1595 })
1596 .expect("Unable to create Bridge object arg!");
1597 let system_state = builder
1598 .obj(ObjectArg::SUI_SYSTEM_MUT)
1599 .expect("Unable to create System State object arg!");
1600
1601 let voting_power = builder.programmable_move_call(
1602 SUI_SYSTEM_PACKAGE_ID,
1603 SUI_SYSTEM_MODULE_NAME.to_owned(),
1604 ident_str!("validator_voting_powers").to_owned(),
1605 vec![],
1606 vec![system_state],
1607 );
1608
1609 let min_stake_participation_percentage = builder
1612 .input(CallArg::Pure(
1613 bcs::to_bytes(&BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER).unwrap(),
1614 ))
1615 .unwrap();
1616
1617 builder.programmable_move_call(
1618 BRIDGE_ADDRESS.into(),
1619 BRIDGE_MODULE_NAME.to_owned(),
1620 BRIDGE_INIT_COMMITTEE_FUNCTION_NAME.to_owned(),
1621 vec![],
1622 vec![bridge, voting_power, min_stake_participation_percentage],
1623 );
1624 builder
1625 }
1626
1627 fn setup_authenticator_state_update<Mode: ExecutionMode>(
1628 update: AuthenticatorStateUpdate,
1629 temporary_store: &mut TemporaryStore<'_>,
1630 store: &dyn BackingStore,
1631 tx_ctx: Rc<RefCell<TxContext>>,
1632 move_vm: &Arc<MoveRuntime>,
1633 gas_charger: &mut GasCharger,
1634 protocol_config: &ProtocolConfig,
1635 metrics: Arc<ExecutionMetrics>,
1636 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1637 ) -> Result<(), Mode::Error> {
1638 let pt = {
1639 let mut builder = ProgrammableTransactionBuilder::new();
1640 let res = builder.move_call(
1641 SUI_FRAMEWORK_ADDRESS.into(),
1642 AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1643 AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1644 vec![],
1645 vec![
1646 CallArg::Object(ObjectArg::SharedObject {
1647 id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1648 initial_shared_version: update.authenticator_obj_initial_shared_version,
1649 mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1650 }),
1651 CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
1652 ],
1653 );
1654 assert_invariant!(
1655 res.is_ok(),
1656 "Unable to generate authenticator_state_update transaction!"
1657 );
1658 builder.finish()
1659 };
1660 SPT::execute::<execution_mode::System<Mode::Error>>(
1661 protocol_config,
1662 metrics,
1663 move_vm,
1664 temporary_store,
1665 store.as_backing_package_store(),
1666 tx_ctx,
1667 gas_charger,
1668 None,
1669 pt,
1670 trace_builder_opt,
1671 )
1672 .map_err(|(e, _)| e)?;
1673 Ok(())
1674 }
1675
1676 fn setup_authenticator_state_expire(
1677 mut builder: ProgrammableTransactionBuilder,
1678 expire: AuthenticatorStateExpire,
1679 ) -> ProgrammableTransactionBuilder {
1680 builder
1681 .move_call(
1682 SUI_FRAMEWORK_ADDRESS.into(),
1683 AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1684 AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
1685 vec![],
1686 vec![
1687 CallArg::Object(ObjectArg::SharedObject {
1688 id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1689 initial_shared_version: expire.authenticator_obj_initial_shared_version,
1690 mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1691 }),
1692 CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
1693 ],
1694 )
1695 .expect("Unable to generate authenticator_state_expire transaction!");
1696 builder
1697 }
1698
1699 fn setup_randomness_state_update<Mode: ExecutionMode>(
1700 update: RandomnessStateUpdate,
1701 temporary_store: &mut TemporaryStore<'_>,
1702 store: &dyn BackingStore,
1703 tx_ctx: Rc<RefCell<TxContext>>,
1704 move_vm: &Arc<MoveRuntime>,
1705 gas_charger: &mut GasCharger,
1706 protocol_config: &ProtocolConfig,
1707 metrics: Arc<ExecutionMetrics>,
1708 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1709 ) -> Result<(), Mode::Error> {
1710 let pt = {
1711 let mut builder = ProgrammableTransactionBuilder::new();
1712 let res = builder.move_call(
1713 SUI_FRAMEWORK_ADDRESS.into(),
1714 RANDOMNESS_MODULE_NAME.to_owned(),
1715 RANDOMNESS_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1716 vec![],
1717 vec![
1718 CallArg::Object(ObjectArg::SharedObject {
1719 id: SUI_RANDOMNESS_STATE_OBJECT_ID,
1720 initial_shared_version: update.randomness_obj_initial_shared_version,
1721 mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1722 }),
1723 CallArg::Pure(bcs::to_bytes(&update.randomness_round).unwrap()),
1724 CallArg::Pure(bcs::to_bytes(&update.random_bytes).unwrap()),
1725 ],
1726 );
1727 assert_invariant!(
1728 res.is_ok(),
1729 "Unable to generate randomness_state_update transaction!"
1730 );
1731 builder.finish()
1732 };
1733 SPT::execute::<execution_mode::System<Mode::Error>>(
1734 protocol_config,
1735 metrics,
1736 move_vm,
1737 temporary_store,
1738 store.as_backing_package_store(),
1739 tx_ctx,
1740 gas_charger,
1741 None,
1742 pt,
1743 trace_builder_opt,
1744 )
1745 .map_err(|(e, _)| e)?;
1746 Ok(())
1747 }
1748
1749 fn setup_coin_deny_list_state_create(
1750 mut builder: ProgrammableTransactionBuilder,
1751 ) -> ProgrammableTransactionBuilder {
1752 builder
1753 .move_call(
1754 SUI_FRAMEWORK_ADDRESS.into(),
1755 DENY_LIST_MODULE.to_owned(),
1756 DENY_LIST_CREATE_FUNC.to_owned(),
1757 vec![],
1758 vec![],
1759 )
1760 .expect("Unable to generate coin_deny_list_create transaction!");
1761 builder
1762 }
1763
1764 fn setup_store_execution_time_estimates(
1765 mut builder: ProgrammableTransactionBuilder,
1766 estimates: StoredExecutionTimeObservations,
1767 ) -> ProgrammableTransactionBuilder {
1768 let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
1769 let estimates_bytes = bcs::to_bytes(&estimates).unwrap();
1772 let estimates_arg = builder.pure(estimates_bytes).unwrap();
1773 builder.programmable_move_call(
1774 SUI_SYSTEM_PACKAGE_ID,
1775 SUI_SYSTEM_MODULE_NAME.to_owned(),
1776 ident_str!("store_execution_time_estimates").to_owned(),
1777 vec![],
1778 vec![system_state, estimates_arg],
1779 );
1780 builder
1781 }
1782
1783 fn setup_store_execution_time_estimates_v2(
1784 mut builder: ProgrammableTransactionBuilder,
1785 estimates: StoredExecutionTimeObservations,
1786 chunk_size: usize,
1787 ) -> ProgrammableTransactionBuilder {
1788 let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
1789
1790 let estimate_chunks = estimates.chunk_observations(chunk_size);
1791
1792 let chunk_bytes: Vec<Vec<u8>> = estimate_chunks
1793 .into_iter()
1794 .map(|chunk| bcs::to_bytes(&chunk).unwrap())
1795 .collect();
1796
1797 let chunks_arg = builder.pure(chunk_bytes).unwrap();
1798
1799 builder.programmable_move_call(
1800 SUI_SYSTEM_PACKAGE_ID,
1801 SUI_SYSTEM_MODULE_NAME.to_owned(),
1802 ident_str!("store_execution_time_estimates_v2").to_owned(),
1803 vec![],
1804 vec![system_state, chunks_arg],
1805 );
1806 builder
1807 }
1808
1809 fn setup_accumulator_root_create(
1810 mut builder: ProgrammableTransactionBuilder,
1811 ) -> ProgrammableTransactionBuilder {
1812 builder
1813 .move_call(
1814 SUI_FRAMEWORK_ADDRESS.into(),
1815 ACCUMULATOR_ROOT_MODULE.to_owned(),
1816 ACCUMULATOR_ROOT_CREATE_FUNC.to_owned(),
1817 vec![],
1818 vec![],
1819 )
1820 .expect("Unable to generate accumulator_root_create transaction!");
1821 builder
1822 }
1823
1824 fn setup_write_accumulator_storage_cost(
1825 mut builder: ProgrammableTransactionBuilder,
1826 write_storage_cost: &WriteAccumulatorStorageCost,
1827 ) -> ProgrammableTransactionBuilder {
1828 let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
1829 let storage_cost_arg = builder.pure(write_storage_cost.storage_cost).unwrap();
1830 builder.programmable_move_call(
1831 SUI_SYSTEM_PACKAGE_ID,
1832 SUI_SYSTEM_MODULE_NAME.to_owned(),
1833 ident_str!("write_accumulator_storage_cost").to_owned(),
1834 vec![],
1835 vec![system_state, storage_cost_arg],
1836 );
1837 builder
1838 }
1839
1840 fn setup_coin_registry_create(
1841 mut builder: ProgrammableTransactionBuilder,
1842 ) -> ProgrammableTransactionBuilder {
1843 builder
1844 .move_call(
1845 SUI_FRAMEWORK_ADDRESS.into(),
1846 ident_str!("coin_registry").to_owned(),
1847 ident_str!("create").to_owned(),
1848 vec![],
1849 vec![],
1850 )
1851 .expect("Unable to generate coin_registry_create transaction!");
1852 builder
1853 }
1854
1855 fn setup_display_registry_create(
1856 mut builder: ProgrammableTransactionBuilder,
1857 ) -> ProgrammableTransactionBuilder {
1858 builder
1859 .move_call(
1860 SUI_FRAMEWORK_ADDRESS.into(),
1861 ident_str!("display_registry").to_owned(),
1862 ident_str!("create").to_owned(),
1863 vec![],
1864 vec![],
1865 )
1866 .expect("Unable to generate display_registry_create transaction!");
1867 builder
1868 }
1869
1870 fn setup_address_alias_state_create(
1871 mut builder: ProgrammableTransactionBuilder,
1872 ) -> ProgrammableTransactionBuilder {
1873 builder
1874 .move_call(
1875 SUI_FRAMEWORK_ADDRESS.into(),
1876 ident_str!("address_alias").to_owned(),
1877 ident_str!("create").to_owned(),
1878 vec![],
1879 vec![],
1880 )
1881 .expect("Unable to generate address_alias_state_create transaction!");
1882 builder
1883 }
1884
1885 #[cfg(test)]
1886 mod address_balance_smash_gate_tests {
1887 use super::legacy::{
1888 ADDRESS_BALANCE_SMASH_FIX_MIN_ACCUMULATOR_VERSION,
1889 should_filter_address_balance_gas_smash,
1890 };
1891 use nonempty::NonEmpty;
1892 use sui_protocol_config::{Chain, ProtocolConfig, ProtocolVersion};
1893 use sui_types::base_types::SequenceNumber;
1894 use sui_types::execution_params::ExecutionOrEarlyError;
1895 use sui_types::execution_status::ExecutionErrorKind;
1896
1897 fn config_without_flag() -> ProtocolConfig {
1901 let config = ProtocolConfig::get_for_version(ProtocolVersion::new(125), Chain::Unknown);
1902 assert!(!config.early_exit_on_iffw());
1903 config
1904 }
1905
1906 fn version(n: u64) -> Option<SequenceNumber> {
1907 Some(SequenceNumber::from_u64(n))
1908 }
1909
1910 #[test]
1911 fn applies_at_or_above_activation_version() {
1912 let activation = ADDRESS_BALANCE_SMASH_FIX_MIN_ACCUMULATOR_VERSION.value();
1913 for v in [activation, activation + 1] {
1914 assert!(should_filter_address_balance_gas_smash(
1915 &ExecutionOrEarlyError::failed(
1916 NonEmpty::new(ExecutionErrorKind::InsufficientFundsForWithdraw),
1917 version(v),
1918 ),
1919 &config_without_flag(),
1920 ));
1921 }
1922 }
1923
1924 #[test]
1925 fn preserves_old_behavior_below_activation_version() {
1926 let below = ADDRESS_BALANCE_SMASH_FIX_MIN_ACCUMULATOR_VERSION.value() - 1;
1931 assert!(should_filter_address_balance_gas_smash(
1932 &ExecutionOrEarlyError::failed(
1933 NonEmpty::new(ExecutionErrorKind::InsufficientFundsForWithdraw),
1934 version(below),
1935 ),
1936 &config_without_flag(),
1937 ));
1938 }
1939
1940 #[test]
1941 fn inert_without_accumulator_version() {
1942 let above = version(ADDRESS_BALANCE_SMASH_FIX_MIN_ACCUMULATOR_VERSION.value() + 1);
1944 assert!(!should_filter_address_balance_gas_smash(
1945 &ExecutionOrEarlyError::ok(above),
1946 &config_without_flag(),
1947 ));
1948 assert!(!should_filter_address_balance_gas_smash(
1949 &ExecutionOrEarlyError::failed(
1950 NonEmpty::new(ExecutionErrorKind::CertificateDenied),
1951 above
1952 ),
1953 &config_without_flag(),
1954 ));
1955 assert!(should_filter_address_balance_gas_smash(
1959 &ExecutionOrEarlyError::failed(
1960 NonEmpty::new(ExecutionErrorKind::InsufficientFundsForWithdraw),
1961 None,
1962 ),
1963 &config_without_flag(),
1964 ));
1965 }
1966 }
1967
1968 #[cfg(test)]
1969 mod address_balance_smash_short_circuit_tests {
1970 use super::legacy::{
1971 ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION,
1972 should_short_circuit_insufficient_funds,
1973 };
1974 use nonempty::NonEmpty;
1975 use sui_protocol_config::{Chain, ProtocolConfig, ProtocolVersion};
1976 use sui_types::base_types::SequenceNumber;
1977 use sui_types::execution_params::ExecutionOrEarlyError;
1978 use sui_types::execution_status::ExecutionErrorKind;
1979
1980 const FLAG_ACTIVATION_PROTOCOL_VERSION: u64 = 126;
1984
1985 fn config_with_flag() -> ProtocolConfig {
1986 ProtocolConfig::get_for_max_version_UNSAFE()
1987 }
1988
1989 fn config_without_flag() -> ProtocolConfig {
1990 ProtocolConfig::get_for_version(
1991 ProtocolVersion::new(FLAG_ACTIVATION_PROTOCOL_VERSION - 1),
1992 Chain::Unknown,
1993 )
1994 }
1995
1996 fn iffw(accumulator_version: Option<SequenceNumber>) -> ExecutionOrEarlyError {
1997 ExecutionOrEarlyError::failed(
1998 NonEmpty::new(ExecutionErrorKind::InsufficientFundsForWithdraw),
1999 accumulator_version,
2000 )
2001 }
2002
2003 fn version(n: u64) -> Option<SequenceNumber> {
2004 Some(SequenceNumber::from_u64(n))
2005 }
2006
2007 #[test]
2008 fn flag_fixtures_match_protocol_gating() {
2009 assert!(config_with_flag().early_exit_on_iffw());
2012 assert!(!config_without_flag().early_exit_on_iffw());
2013 }
2014
2015 #[test]
2016 fn short_circuits_at_or_above_activation_version() {
2017 let activation = ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION.value();
2020 for config in [config_with_flag(), config_without_flag()] {
2021 assert!(should_short_circuit_insufficient_funds(
2022 &iffw(version(activation)),
2023 &config
2024 ));
2025 if let Some(next) = activation.checked_add(1) {
2026 assert!(should_short_circuit_insufficient_funds(
2027 &iffw(version(next)),
2028 &config
2029 ));
2030 }
2031 }
2032 }
2033
2034 #[test]
2035 fn preserves_hotfix_behavior_below_activation_version() {
2036 let below = ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION.value() - 1;
2040 assert!(should_short_circuit_insufficient_funds(
2041 &iffw(version(below)),
2042 &config_without_flag()
2043 ));
2044 }
2045
2046 #[test]
2047 fn flag_forces_short_circuit_below_activation_version() {
2048 let below = ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION.value() - 1;
2051 assert!(should_short_circuit_insufficient_funds(
2052 &iffw(version(below)),
2053 &config_with_flag()
2054 ));
2055 }
2056
2057 #[test]
2058 fn no_accumulator_version_short_circuits_in_test_configuration() {
2059 assert!(should_short_circuit_insufficient_funds(
2064 &iffw(None),
2065 &config_without_flag(),
2066 ));
2067 }
2068
2069 #[test]
2070 fn no_accumulator_version_short_circuits_with_protocol_flag() {
2071 assert!(should_short_circuit_insufficient_funds(
2074 &iffw(None),
2075 &config_with_flag(),
2076 ));
2077 }
2078
2079 #[test]
2080 fn iffw_short_circuit_applies_even_when_iffw_is_not_head_error() {
2081 let errors = NonEmpty::from((
2084 ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable,
2085 vec![ExecutionErrorKind::InsufficientFundsForWithdraw],
2086 ));
2087
2088 assert!(should_short_circuit_insufficient_funds(
2090 &ExecutionOrEarlyError::failed(errors.clone(), None),
2091 &config_with_flag(),
2092 ));
2093
2094 assert!(should_short_circuit_insufficient_funds(
2096 &ExecutionOrEarlyError::failed(
2097 errors,
2098 version(ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION.value()),
2099 ),
2100 &config_without_flag(),
2101 ));
2102 }
2103
2104 #[test]
2105 fn non_head_iffw_short_circuits_in_test_configuration() {
2106 let errors = NonEmpty::from((
2111 ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable,
2112 vec![ExecutionErrorKind::InsufficientFundsForWithdraw],
2113 ));
2114
2115 assert!(should_short_circuit_insufficient_funds(
2116 &ExecutionOrEarlyError::failed(errors, None),
2117 &config_without_flag(),
2118 ));
2119 }
2120
2121 #[test]
2122 fn requires_insufficient_funds_error() {
2123 for config in [config_with_flag(), config_without_flag()] {
2126 assert!(!should_short_circuit_insufficient_funds(
2127 &ExecutionOrEarlyError::ok(version(
2128 ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION.value()
2129 )),
2130 &config
2131 ));
2132 assert!(!should_short_circuit_insufficient_funds(
2133 &ExecutionOrEarlyError::ok(None),
2134 &config
2135 ));
2136 assert!(!should_short_circuit_insufficient_funds(
2137 &ExecutionOrEarlyError::failed(
2138 NonEmpty::new(ExecutionErrorKind::CertificateDenied),
2139 version(
2140 ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION.value()
2141 ),
2142 ),
2143 &config
2144 ));
2145 assert!(!should_short_circuit_insufficient_funds(
2146 &ExecutionOrEarlyError::failed(
2147 NonEmpty::new(ExecutionErrorKind::CertificateDenied),
2148 None,
2149 ),
2150 &config
2151 ));
2152 }
2153 }
2154 }
2155}