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