1use crate::{
5 adapter,
6 execution_mode::ExecutionMode,
7 execution_value::ExecutionState,
8 gas_charger::{GasCharger, GasPayment, PaymentLocation},
9 gas_meter::SuiGasMeter,
10 sp,
11 static_programmable_transactions::{
12 env::Env,
13 execution::{
14 self, trace_utils,
15 values::{Local, Locals, Value},
16 },
17 linkage::resolved_linkage::{ExecutableLinkage, ResolvedLinkage},
18 loading::ast::{Datatype, DeserializedPackage, PackagePayload},
19 typing::ast::{self as T, Type},
20 },
21};
22use indexmap::{IndexMap, IndexSet};
23use move_binary_format::{
24 CompiledModule,
25 compatibility::{Compatibility, InclusionCheck},
26 errors::{Location, PartialVMError, PartialVMResult, VMResult},
27 file_format::FunctionDefinitionIndex,
28 normalized,
29};
30use move_core_types::{
31 account_address::AccountAddress,
32 identifier::IdentStr,
33 language_storage::{ModuleId, StructTag},
34 u256::U256,
35};
36use move_trace_format::format::MoveTraceBuilder;
37use move_vm_runtime::{
38 execution::{
39 Type as VMType, TypeSubst as _,
40 values::{VMValueCast, Value as VMValue},
41 vm::{LoadedFunctionInformation, MoveVM},
42 },
43 natives::extensions::NativeExtensions,
44 shared::{
45 gas::{GasMeter as _, SimpleInstruction},
46 linkage_context::LinkageHash,
47 },
48 validation::verification::ast::Package as VerifiedPackage,
49};
50use mysten_common::ZipDebugEqIteratorExt;
51use mysten_common::debug_fatal;
52use nonempty::nonempty;
53use quick_cache::unsync::Cache as QCache;
54use serde::{Deserialize, de::DeserializeSeed};
55use std::{
56 cell::RefCell,
57 collections::{BTreeMap, BTreeSet},
58 fmt,
59 rc::Rc,
60 sync::Arc,
61};
62use sui_move_natives::object_runtime::{
63 self, LoadedRuntimeObject, MoveAccumulatorAction, MoveAccumulatorEvent, MoveAccumulatorValue,
64 ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
65};
66use sui_protocol_config::ProtocolConfig;
67use sui_types::{
68 TypeTag,
69 accumulator_event::AccumulatorEvent,
70 accumulator_root::{self, AccumulatorObjId},
71 balance::Balance,
72 base_types::{
73 MoveObjectType, ObjectID, RESOLVED_ASCII_STR, RESOLVED_UTF8_STR, SequenceNumber,
74 SuiAddress, TxContext,
75 },
76 effects::{AccumulatorAddress, AccumulatorValue, AccumulatorWriteV1},
77 error::{ExecutionError, ExecutionErrorTrait, SafeIndex, command_argument_error},
78 event::Event,
79 execution::{ExecutionResults, ExecutionResultsV2},
80 execution_status::{CommandArgumentError, ExecutionErrorKind, PackageUpgradeError},
81 metrics::ExecutionMetrics,
82 move_package::{
83 MovePackage, UpgradeCap, UpgradePolicy, UpgradeReceipt, UpgradeTicket,
84 normalize_deserialized_modules,
85 },
86 object::{MoveObject, Object, ObjectPermissions, Owner},
87 storage::{BackingPackageStore, DenyListResult, PackageObject, get_package_objects},
88};
89use sui_verifier::INIT_FN_NAME;
90use tracing::instrument;
91
92const PUBLISH_INIT_EXPECTED_STACK_HEIGHT: u64 = 0;
94const UPGRADE_INIT_EXPECTED_STACK_HEIGHT: u64 = 1;
97
98macro_rules! unwrap {
99 ($e:expr, $($args:expr),* $(,)?) => {
100 match $e {
101 Some(v) => v,
102 None => {
103 invariant_violation!("Unexpected none: {}", format!($($args),*))
104 }
105 }
106
107 };
108}
109
110#[macro_export]
111macro_rules! object_runtime {
112 ($context:ident) => {
113 $context
114 .native_extensions
115 .try_borrow()
116 .map_err(|_| {
117 make_invariant_violation!(
118 "Should be able to borrow object runtime native extension"
119 )
120 })?
121 .get::<sui_move_natives::object_runtime::ObjectRuntime>()
122 .map_err(|e| {
123 $context
124 .env
125 .convert_vm_error(e.finish(move_binary_format::errors::Location::Undefined))
126 })
127 };
128}
129
130macro_rules! object_runtime_mut {
131 ($context:ident) => {
132 $context
133 .native_extensions
134 .try_borrow_mut()
135 .map_err(|_| {
136 make_invariant_violation!(
137 "Should be able to borrow object runtime native extension"
138 )
139 })?
140 .get_mut::<ObjectRuntime>()
141 .map_err(|e| $context.env.convert_vm_error(e.finish(Location::Undefined)))
142 };
143}
144
145macro_rules! charge_gas_ {
146 ($gas_charger:expr, $env:expr, $call:ident($($args:expr),*)) => {{
147 SuiGasMeter($gas_charger.move_gas_status_mut())
148 .$call($($args),*)
149 .map_err(|e| $env.convert_vm_error(e.finish(Location::Undefined)))
150 }};
151 ($gas_charger:expr, $env:expr, $case:ident, $value_view:expr) => {
152 charge_gas_!($gas_charger, $env, $case($value_view))
153 };
154}
155
156macro_rules! charge_gas {
157 ($context:ident, $case:ident, $value_view:expr) => {{ charge_gas_!($context.gas_charger, $context.env, $case, $value_view) }};
158}
159
160macro_rules! with_vm {
163 ($self:ident, $linkage:expr, $error:ty, $body:expr) => {{
164 let link_context = $linkage.linkage_context::<$error>()?;
165 let linkage_hash = link_context.to_linkage_hash();
166 let mut vm = if let Some((_, vm)) = $self.executable_vm_cache.remove(&linkage_hash) {
167 vm
168 } else {
169 let data_store = &$self.env.linkable_store.package_store;
170 $self
171 .env
172 .vm
173 .make_vm_with_native_extensions(
174 data_store,
175 link_context.clone(),
176 $self.native_extensions.clone(),
177 )
178 .map_err(|e| $self.env.convert_linked_vm_error(e, $linkage))?
179 };
180 let result = $body(&mut vm)?;
181 $self.executable_vm_cache.insert(linkage_hash, vm);
182 Ok(result)
183 }};
184}
185
186#[derive(Debug)]
188pub struct CtxValue(Value);
189
190#[derive(Clone, Debug)]
191pub struct InputObjectMetadata {
192 pub newly_created: bool,
193 pub id: ObjectID,
194 pub refined_permissions: ObjectPermissions,
195 pub owner: Owner,
196 pub version: SequenceNumber,
197 pub type_: Type,
198}
199
200#[derive(Debug, Clone, Copy)]
204pub(crate) enum GasCoinTransfer {
205 TransferObjects,
207 SendFunds {
209 recipient: AccountAddress,
211 },
212}
213
214#[derive(Copy, Clone)]
215enum UsageKind {
216 Move,
217 Copy,
218 Borrow,
219}
220
221struct Locations {
223 tx_context_value: Locals,
225 gas: Option<(GasPayment, InputObjectMetadata, Locals)>,
227 input_object_metadata: Vec<(T::InputIndex, InputObjectMetadata)>,
229 object_inputs: Locals,
230 input_withdrawal_metadata: Vec<T::WithdrawalInput>,
231 withdrawal_inputs: Locals,
232 pure_input_bytes: IndexSet<Vec<u8>>,
233 pure_input_metadata: Vec<T::PureInput>,
234 pure_inputs: Locals,
235 receiving_input_metadata: Vec<T::ReceivingInput>,
236 receiving_inputs: Locals,
237 results: Vec<Locals>,
241}
242
243enum ResolvedLocation<'a> {
244 Local(Local<'a>),
245 Pure {
246 bytes: &'a [u8],
247 metadata: &'a T::PureInput,
248 local: Local<'a>,
249 },
250 Receiving {
251 metadata: &'a T::ReceivingInput,
252 local: Local<'a>,
253 },
254}
255
256pub struct Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas, 'extension, Mode>
258where
259 Mode: ExecutionMode,
260{
261 pub env: &'env Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
262 pub metrics: Arc<ExecutionMetrics>,
264 pub native_extensions: NativeExtensions<'env>,
265 pub tx_context: Rc<RefCell<TxContext>>,
268 pub gas_charger: &'gas mut GasCharger,
270 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
272 locations: Locations,
274 gas_coin_transfer: Option<GasCoinTransfer>,
276 executable_vm_cache: QCache<LinkageHash, MoveVM<'env>>,
278}
279
280impl Locations {
281 fn resolve(&mut self, location: T::Location) -> Result<ResolvedLocation<'_>, ExecutionError> {
284 Ok(match location {
285 T::Location::TxContext => ResolvedLocation::Local(self.tx_context_value.local(0)?),
286 T::Location::GasCoin => {
287 let (_, _, gas_locals) = unwrap!(self.gas.as_mut(), "Gas coin not provided");
288 ResolvedLocation::Local(gas_locals.local(0)?)
289 }
290 T::Location::ObjectInput(i) => ResolvedLocation::Local(self.object_inputs.local(i)?),
291 T::Location::WithdrawalInput(i) => {
292 ResolvedLocation::Local(self.withdrawal_inputs.local(i)?)
293 }
294 T::Location::Result(i, j) => {
295 let result = unwrap!(self.results.get_mut(i as usize), "bounds already verified");
296 ResolvedLocation::Local(result.local(j)?)
297 }
298 T::Location::PureInput(i) => {
299 let local = self.pure_inputs.local(i)?;
300 let metadata = &self.pure_input_metadata.safe_get(i as usize)?;
301 let bytes = self
302 .pure_input_bytes
303 .get_index(metadata.byte_index)
304 .ok_or_else(|| {
305 make_invariant_violation!(
306 "Pure input {} bytes out of bounds at index {}",
307 metadata.original_input_index.0,
308 metadata.byte_index,
309 )
310 })?;
311 ResolvedLocation::Pure {
312 bytes,
313 metadata,
314 local,
315 }
316 }
317 T::Location::ReceivingInput(i) => ResolvedLocation::Receiving {
318 metadata: self.receiving_input_metadata.safe_get(i as usize)?,
319 local: self.receiving_inputs.local(i)?,
320 },
321 })
322 }
323}
324
325impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas, 'extension, Mode>
326 Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas, 'extension, Mode>
327where
328 Mode: ExecutionMode,
329{
330 #[instrument(name = "Context::new", level = "trace", skip_all)]
331 pub fn new(
332 env: &'env Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
333 metrics: Arc<ExecutionMetrics>,
334 tx_context: Rc<RefCell<TxContext>>,
335 gas_charger: &'gas mut GasCharger,
336 payment_location: Option<GasPayment>,
337 pure_input_bytes: IndexSet<Vec<u8>>,
338 object_inputs: Vec<T::ObjectInput>,
339 input_withdrawal_metadata: Vec<T::WithdrawalInput>,
340 pure_input_metadata: Vec<T::PureInput>,
341 receiving_input_metadata: Vec<T::ReceivingInput>,
342 ) -> Result<Self, Mode::Error>
343 where
344 'pc: 'state,
345 {
346 let mut input_object_map = BTreeMap::new();
347 let mut input_object_metadata = Vec::with_capacity(object_inputs.len());
348 let mut object_values = Vec::with_capacity(object_inputs.len());
349 for object_input in object_inputs {
350 let (i, m, v) = load_object_arg(gas_charger, env, &mut input_object_map, object_input)?;
351 input_object_metadata.push((i, m));
352 object_values.push(Some(v));
353 }
354 let object_inputs = Locals::new(object_values)?;
355 let mut withdrawal_values = Vec::with_capacity(input_withdrawal_metadata.len());
356 for withdrawal_input in &input_withdrawal_metadata {
357 let v = load_withdrawal_arg(gas_charger, env, withdrawal_input)?;
358 withdrawal_values.push(Some(v));
359 }
360 let withdrawal_inputs = Locals::new(withdrawal_values)?;
361 let pure_inputs = Locals::new_invalid(pure_input_metadata.len())?;
362 let receiving_inputs = Locals::new_invalid(receiving_input_metadata.len())?;
363 let mut new_gas_coin_id = None;
364 let gas = match payment_location {
365 Some(gas_payment)
366 if matches!(gas_payment.location, PaymentLocation::AddressBalance(_))
367 && !env.protocol_config.gasless_transaction_drop_safety() =>
368 {
369 None
370 }
371 Some(gas_payment) => {
372 let ty = env.gas_coin_type()?;
373 let (gas_metadata, gas_value) = match gas_payment.location {
374 PaymentLocation::AddressBalance(sui_address) => {
375 assert_invariant!(
376 env.protocol_config.enable_address_balance_gas_payments(),
377 "Address balance gas payments must be enabled to have an address \
378 balance payment location"
379 );
380 let max_gas_in_balance = gas_charger.gas_budget();
381 assert_invariant!(
382 gas_payment.amount >= max_gas_in_balance,
383 "not enough gas to pay. How did we get this far?"
384 );
385 let id = tx_context.borrow_mut().fresh_id();
386 new_gas_coin_id = Some(id);
387
388 let metadata = InputObjectMetadata {
389 newly_created: true,
390 id,
391 refined_permissions: ObjectPermissions::ALL,
392 owner: Owner::AddressOwner(sui_address),
393 version: SequenceNumber::new(),
394 type_: ty,
395 };
396 let coin = Value::coin(id, gas_payment.amount);
397 (metadata, coin)
398 }
399 PaymentLocation::Coin(gas_coin_id) => load_object_arg_impl(
400 gas_charger,
401 env,
402 &mut input_object_map,
403 gas_coin_id,
404 ObjectPermissions::ALL,
405 ty,
406 )?,
407 };
408 let mut gas_locals = Locals::new([Some(gas_value)])?;
409 let mut gas_local = gas_locals.local(0)?;
410 let gas_ref = gas_local.borrow()?;
411 let max_gas_in_balance = gas_charger.gas_budget();
413 gas_ref.coin_ref_subtract_balance(max_gas_in_balance)?;
414 Some((gas_payment, gas_metadata, gas_locals))
415 }
416 None => None,
417 };
418 let native_extensions = adapter::new_native_extensions(
419 env.state_view.as_child_resolver(),
420 input_object_map,
421 !gas_charger.is_unmetered(),
422 env.protocol_config,
423 metrics.clone(),
424 tx_context.clone(),
425 )?;
426 if let Some(new_gas_coin_id) = new_gas_coin_id {
427 native_extensions
430 .try_borrow_mut()
431 .map_err(|_| {
432 make_invariant_violation!(
433 "Should be able to borrow object runtime native extension"
434 )
435 })?
436 .get_mut::<ObjectRuntime>()
437 .and_then(|object_runtime| object_runtime.new_id(new_gas_coin_id))
438 .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
439 }
440
441 debug_assert_eq!(gas_charger.move_gas_status().stack_height_current(), 0);
442 let tx_context_value = Locals::new(vec![Some(Value::new_tx_context(
443 tx_context.borrow().digest(),
444 )?)])?;
445 Ok(Self {
446 env,
447 metrics,
448 native_extensions,
449 tx_context,
450 gas_charger,
451 user_events: vec![],
452 locations: Locations {
453 tx_context_value,
454 gas,
455 input_object_metadata,
456 object_inputs,
457 input_withdrawal_metadata,
458 withdrawal_inputs,
459 pure_input_bytes,
460 pure_input_metadata,
461 pure_inputs,
462 receiving_input_metadata,
463 receiving_inputs,
464 results: vec![],
465 },
466 gas_coin_transfer: None,
467 executable_vm_cache: QCache::new(1024),
468 })
469 }
470
471 pub(crate) fn record_gas_coin_transfer(
472 &mut self,
473 transfer: GasCoinTransfer,
474 ) -> Result<(), Mode::Error> {
475 assert_invariant!(
477 !matches!(transfer, GasCoinTransfer::SendFunds { .. })
478 || self.env.protocol_config.enable_accumulators(),
479 "Gas coin transfers with send_funds are not allowed unless accumulators are enabled"
480 );
481 if self.gas_coin_transfer.is_some() {
482 invariant_violation!("Gas coin destination set more than once");
483 }
484 self.gas_coin_transfer = Some(transfer);
485 Ok(())
486 }
487
488 pub fn finish(mut self) -> Result<ExecutionResults, Mode::Error> {
489 assert_invariant!(
490 !self.locations.tx_context_value.local(0)?.is_invalid()?,
491 "tx context value should be present"
492 );
493 let gas_coin_transfer = self.gas_coin_transfer;
494 let gas = std::mem::take(&mut self.locations.gas);
495 let object_input_metadata = std::mem::take(&mut self.locations.input_object_metadata);
496 let mut object_inputs =
497 std::mem::replace(&mut self.locations.object_inputs, Locals::new_invalid(0)?);
498 let mut created_input_object_ids = BTreeSet::new();
499 let mut loaded_runtime_objects = BTreeMap::new();
500 let mut by_value_shared_objects = BTreeSet::new();
501 let mut consensus_owner_objects = BTreeMap::new();
502 let mut gas_payment_location = None;
503 let gas = gas
504 .map(|(payment_location, m, mut g)| {
505 gas_payment_location = Some(payment_location);
506 let value_opt = g.local(0)?.move_if_valid()?;
507 let moved = value_opt.is_none();
508 assert_invariant!(
509 moved == gas_coin_transfer.is_some(),
510 "Gas coin moved requires gas coin transfer to be recorded, and vice versa"
511 );
512 Result::<_, ExecutionError>::Ok((m, value_opt))
513 })
514 .transpose()?;
515
516 let gas_id_opt = gas.as_ref().map(|(m, _)| m.id);
517 let object_inputs = object_input_metadata
518 .into_iter()
519 .enumerate()
520 .map(|(i, (_, m))| {
521 let v_opt = object_inputs.local(checked_as!(i, u16)?)?.move_if_valid()?;
522 Ok((m, v_opt))
523 })
524 .collect::<Result<Vec<_>, ExecutionError>>()?;
525 for (metadata, value_opt) in object_inputs.into_iter().chain(gas) {
526 let InputObjectMetadata {
527 newly_created,
528 id,
529 refined_permissions,
530 owner,
531 version,
532 type_,
533 } = metadata;
534 if !refined_permissions.can_use_mutably() {
535 continue;
536 }
537
538 if newly_created {
539 created_input_object_ids.insert(id);
540 } else {
541 loaded_runtime_objects.insert(
542 id,
543 LoadedRuntimeObject {
544 version,
545 is_modified: true,
546 },
547 );
548 }
549 if let Some(object) = value_opt {
550 self.transfer_object_(
551 owner,
552 type_,
553 CtxValue(object),
554 true,
555 )?;
556 } else if owner.is_shared() {
557 by_value_shared_objects.insert(id);
558 } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
559 consensus_owner_objects.insert(id, owner.clone());
560 }
561 }
562
563 let Self {
564 env,
565 native_extensions,
566 tx_context,
567 gas_charger,
568 user_events,
569 ..
570 } = self;
571 let ref_context: &RefCell<TxContext> = &tx_context;
572 let tx_context: &TxContext = &ref_context.borrow();
573 let tx_digest = ref_context.borrow().digest();
574
575 let object_runtime: ObjectRuntime = native_extensions
576 .try_borrow_mut()
577 .map_err(|_| {
578 make_invariant_violation!(
579 "Should be able to borrow object runtime native extension at the end of execution"
580 )
581 })?
582 .remove()
583 .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
584
585 let RuntimeResults {
586 mut writes,
587 user_events: remaining_events,
588 loaded_child_objects,
589 mut created_object_ids,
590 deleted_object_ids,
591 mut accumulator_events,
592 settlement_input_sui,
593 settlement_output_sui,
594 } = object_runtime.finish()?;
595 assert_invariant!(
596 loaded_runtime_objects
597 .keys()
598 .all(|id| !created_object_ids.contains(id)),
599 "Loaded input objects should not be in the created objects set"
600 );
601 assert_invariant!(
604 remaining_events.is_empty(),
605 "Events should be taken after every Move call"
606 );
607 if let Some(gas_id) = gas_id_opt {
609 assert_invariant!(
611 !deleted_object_ids.contains(&gas_id)
612 || gas_coin_transfer.is_some_and(|destination| matches!(
613 destination,
614 GasCoinTransfer::SendFunds { .. }
615 )),
616 "Gas coin should not be deleted"
617 );
618 let Some(gas_payment_location) = gas_payment_location else {
619 invariant_violation!("Gas payment should be specified if gas ID is present");
620 };
621 finish_gas_coin(
622 gas_charger,
623 &mut writes,
624 &mut created_object_ids,
625 &deleted_object_ids,
626 &mut accumulator_events,
627 gas_id,
628 gas_payment_location,
629 gas_coin_transfer,
630 )?;
631 }
632
633 loaded_runtime_objects.extend(loaded_child_objects);
634
635 let mut written_objects = BTreeMap::new();
636
637 let (writeout_vm, ty_linkage) =
638 Self::make_writeout_vm(env, writes.values().map(|(_, ty, _)| ty.clone()))?;
639
640 for (id, (recipient, ty, value)) in writes {
641 let (ty, layout) = Self::load_type_and_layout_from_struct_for_writeout(
642 env,
643 &writeout_vm,
644 &ty_linkage,
645 ty.clone().into(),
646 )?;
647 let abilities = ty.abilities();
648 let has_public_transfer = abilities.has_store();
649 let Some(bytes) = value.typed_serialize(&layout) else {
650 invariant_violation!("Failed to serialize already deserialized Move value");
651 };
652 let move_object = unsafe {
654 create_written_object::<Mode>(
655 env,
656 &loaded_runtime_objects,
657 id,
658 ty,
659 has_public_transfer,
660 bytes,
661 )?
662 };
663 let object = Object::new_move(move_object, recipient, tx_digest);
664 written_objects.insert(id, object);
665 }
666
667 for package in self
668 .env
669 .linkable_store
670 .package_store
671 .to_new_packages()
672 .into_iter()
673 {
674 let package_obj = Object::new_from_package(package, tx_digest);
675 let id = package_obj.id();
676 created_object_ids.insert(id);
677 written_objects.insert(id, package_obj);
678 }
679
680 Ok(execution::context::finish(
681 env.protocol_config,
682 env.state_view,
683 gas_charger,
684 tx_context,
685 &by_value_shared_objects,
686 &consensus_owner_objects,
687 loaded_runtime_objects,
688 written_objects,
689 created_object_ids,
690 deleted_object_ids,
691 user_events,
692 accumulator_events,
693 settlement_input_sui,
694 settlement_output_sui,
695 )?)
696 }
697
698 pub fn take_user_events(
699 &mut self,
700 vm: &MoveVM<'_>,
701 version_mid: ModuleId,
702 function_def_idx: FunctionDefinitionIndex,
703 instr_length: u16,
704 linkage: &ExecutableLinkage,
705 ) -> Result<(), Mode::Error> {
706 let events = object_runtime_mut!(self)?.take_user_events();
707 let Some(num_events) = self.user_events.len().checked_add(events.len()) else {
708 invariant_violation!("usize overflow, too many events emitted")
709 };
710 let max_events = self.env.protocol_config.max_num_event_emit();
711 if num_events as u64 > max_events {
712 let err = max_event_error(max_events)
713 .at_code_offset(function_def_idx, instr_length)
714 .finish(Location::Module(version_mid.clone()));
715 return Err(self.env.convert_linked_vm_error(err, linkage));
716 }
717 let new_events = events
718 .into_iter()
719 .map(|(tag, value)| {
720 let type_tag = TypeTag::Struct(Box::new(tag));
721 let layout = vm
722 .runtime_type_layout(&type_tag)
723 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
724 let Some(bytes) = value.typed_serialize(&layout) else {
725 invariant_violation!("Failed to serialize Move event");
726 };
727 let TypeTag::Struct(tag) = type_tag else {
728 unreachable!()
729 };
730 Ok((version_mid.clone(), *tag, bytes))
731 })
732 .collect::<Result<Vec<_>, Mode::Error>>()?;
733 self.user_events.extend(new_events);
734 Ok(())
735 }
736
737 fn make_writeout_vm<I>(
746 env: &Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
747 writes: I,
748 ) -> Result<(MoveVM<'extension>, ExecutableLinkage), Mode::Error>
749 where
750 I: IntoIterator<Item = MoveObjectType>,
751 {
752 let tys_addrs = writes
753 .into_iter()
754 .flat_map(|ty| StructTag::from(ty).all_addresses())
755 .map(ObjectID::from)
756 .collect::<BTreeSet<_>>();
757
758 let ty_linkage = ExecutableLinkage::type_linkage::<_, Mode::Error>(
759 env.linkage_analysis.config().clone(),
760 &tys_addrs,
761 env.linkable_store,
762 )?;
763 env.vm
764 .make_vm(
765 &env.linkable_store.package_store,
766 ty_linkage.linkage_context::<Mode::Error>()?,
767 )
768 .map_err(|e| env.convert_linked_vm_error(e, &ty_linkage))
769 .map(|vm| (vm, ty_linkage))
770 }
771
772 fn load_type_and_layout_from_struct_for_writeout(
777 env: &Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
778 vm: &MoveVM,
779 linkage: &ExecutableLinkage,
780 tag: StructTag,
781 ) -> Result<(Type, move_core_types::runtime_value::MoveTypeLayout), Mode::Error> {
782 let type_tag = TypeTag::Struct(Box::new(tag));
783 let vm_type = vm
784 .load_type(&type_tag)
785 .map_err(|e| env.convert_linked_vm_error(e, linkage))?;
786 let layout = vm
787 .runtime_type_layout(&type_tag)
788 .map_err(|e| env.convert_vm_error(e))?;
789 env.adapter_type_from_vm_type(vm, &vm_type)
790 .map(|ty| (ty, layout))
791 }
792
793 fn location(&mut self, usage: UsageKind, location: T::Location) -> Result<Value, Mode::Error> {
798 let resolved = self.locations.resolve(location)?;
799 let mut local = match resolved {
800 ResolvedLocation::Local(l) => l,
801 ResolvedLocation::Pure {
802 bytes,
803 metadata,
804 mut local,
805 } => {
806 if local.is_invalid()? {
807 let v = load_pure_value(self.gas_charger, self.env, bytes, metadata)?;
808 local.store(v)?;
809 }
810 local
811 }
812 ResolvedLocation::Receiving {
813 metadata,
814 mut local,
815 } => {
816 if local.is_invalid()? {
817 let v = load_receiving_value(self.gas_charger, self.env, metadata)?;
818 local.store(v)?;
819 }
820 local
821 }
822 };
823 Ok(match usage {
824 UsageKind::Move => {
825 let value = local.move_()?;
826 charge_gas_!(self.gas_charger, self.env, charge_move_loc, &value)?;
827 value
828 }
829 UsageKind::Copy => {
830 let value = local.copy()?;
831 charge_gas_!(self.gas_charger, self.env, charge_copy_loc, &value)?;
832 value
833 }
834 UsageKind::Borrow => {
835 charge_gas_!(
836 self.gas_charger,
837 self.env,
838 charge_simple_instr(SimpleInstruction::MutBorrowLoc)
839 )?;
840 local.borrow()?
841 }
842 })
843 }
844
845 fn location_usage(&mut self, usage: T::Usage) -> Result<Value, Mode::Error> {
846 match usage {
847 T::Usage::Move(location) => self.location(UsageKind::Move, location),
848 T::Usage::Copy { location, .. } => self.location(UsageKind::Copy, location),
849 }
850 }
851
852 fn argument_value(&mut self, sp!(_, (arg_, _)): T::Argument) -> Result<Value, Mode::Error> {
853 match arg_ {
854 T::Argument__::Use(usage) => self.location_usage(usage),
855 T::Argument__::Freeze(usage) => self.location_usage(usage),
857 T::Argument__::Borrow(_, location) => self.location(UsageKind::Borrow, location),
858 T::Argument__::Read(usage) => {
859 let reference = self.location_usage(usage)?;
860 charge_gas!(self, charge_read_ref, &reference)?;
861 Ok(reference.read_ref()?)
862 }
863 }
864 }
865
866 pub fn argument<V>(&mut self, arg: T::Argument) -> Result<V, Mode::Error>
867 where
868 VMValue: VMValueCast<V>,
869 {
870 let before_height = self.gas_charger.move_gas_status().stack_height_current();
871 let value = self.argument_value(arg)?;
872 let after_height = self.gas_charger.move_gas_status().stack_height_current();
873 debug_assert_eq!(before_height.saturating_add(1), after_height);
874 let value: V = value.cast()?;
875 Ok(value)
876 }
877
878 pub fn arguments<V>(&mut self, args: Vec<T::Argument>) -> Result<Vec<V>, Mode::Error>
879 where
880 VMValue: VMValueCast<V>,
881 {
882 args.into_iter().map(|arg| self.argument(arg)).collect()
883 }
884
885 pub fn result(&mut self, result: Vec<Option<CtxValue>>) -> Result<(), Mode::Error> {
886 self.locations
887 .results
888 .push(Locals::new(result.into_iter().map(|v| v.map(|v| v.0)))?);
889 Ok(())
890 }
891
892 pub fn charge_command(
893 &mut self,
894 is_move_call: bool,
895 num_args: usize,
896 num_return: usize,
897 ) -> Result<(), Mode::Error> {
898 let move_gas_status = self.gas_charger.move_gas_status_mut();
899 let before_size = move_gas_status.stack_size_current();
900 let num_popped = if is_move_call {
904 num_args.checked_add(num_return).ok_or_else(|| {
905 make_invariant_violation!("usize overflow when charging gas for command",)
906 })?
907 } else {
908 num_args
909 };
910 move_gas_status
911 .charge(1, 0, num_popped as u64, 0, 1)
912 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
913 let after_size = move_gas_status.stack_size_current();
914 assert_invariant!(
915 before_size == after_size,
916 "We assume currently that the stack size is not decremented. \
917 If this changes, we need to actually account for it here"
918 );
919 Ok(())
920 }
921
922 pub fn copy_value(&mut self, value: &CtxValue) -> Result<CtxValue, Mode::Error> {
923 Ok(CtxValue(copy_value(self.gas_charger, self.env, &value.0)?))
924 }
925
926 pub fn new_coin(&mut self, amount: u64) -> Result<CtxValue, Mode::Error> {
927 let id = self.tx_context.borrow_mut().fresh_id();
928 object_runtime_mut!(self)?
929 .new_id(id)
930 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
931 Ok(CtxValue(Value::coin(id, amount)))
932 }
933
934 pub fn destroy_coin(&mut self, coin: CtxValue) -> Result<u64, Mode::Error> {
935 let (id, amount) = coin.0.unpack_coin()?;
936 object_runtime_mut!(self)?
937 .delete_id(id)
938 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
939 Ok(amount)
940 }
941
942 pub fn new_upgrade_cap(&mut self, version_id: ObjectID) -> Result<CtxValue, Mode::Error> {
943 let id = self.tx_context.borrow_mut().fresh_id();
944 object_runtime_mut!(self)?
945 .new_id(id)
946 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
947 let cap = UpgradeCap::new(id, version_id);
948 Ok(CtxValue(Value::upgrade_cap(cap)))
949 }
950
951 pub fn upgrade_receipt(
952 &self,
953 upgrade_ticket: UpgradeTicket,
954 upgraded_package_id: ObjectID,
955 ) -> CtxValue {
956 let receipt = UpgradeReceipt::new(upgrade_ticket, upgraded_package_id);
957 CtxValue(Value::upgrade_receipt(receipt))
958 }
959
960 pub fn vm_move_call(
965 &mut self,
966 function: T::LoadedFunction,
967 args: Vec<CtxValue>,
968 trace_builder_opt: &mut Option<MoveTraceBuilder>,
969 ) -> Result<Vec<CtxValue>, Mode::Error> {
970 with_vm!(self, &function.linkage, Mode::Error, |vm: &mut MoveVM<
971 'env,
972 >| {
973 let ty_args = function
974 .type_arguments
975 .iter()
976 .map(|ty| {
977 let tag: TypeTag = ty.clone().try_into().map_err(|e| {
978 Mode::Error::new_with_source(ExecutionErrorKind::VMInvariantViolation, e)
979 })?;
980 vm.load_type(&tag)
981 .map_err(|e| self.env.convert_linked_vm_error(e, &function.linkage))
982 })
983 .collect::<Result<Vec<_>, Mode::Error>>()?;
984 let result = self.execute_function_bypass_visibility_with_vm(
985 vm,
986 &function.original_mid,
987 &function.name,
988 ty_args,
989 args,
990 &function.linkage,
991 trace_builder_opt,
992 )?;
993 self.take_user_events(
994 vm,
995 function.version_mid,
996 function.definition_index,
997 function.instruction_length,
998 &function.linkage,
999 )?;
1000 Ok::<Vec<CtxValue>, Mode::Error>(result)
1001 })
1002 }
1003
1004 fn execute_function_bypass_visibility_with_vm(
1005 &mut self,
1006 vm: &mut MoveVM<'env>,
1007 original_mid: &ModuleId,
1008 function_name: &IdentStr,
1009 ty_args: Vec<VMType>,
1010 args: Vec<CtxValue>,
1011 linkage: &ExecutableLinkage,
1012 tracer: &mut Option<MoveTraceBuilder>,
1013 ) -> Result<Vec<CtxValue>, Mode::Error> {
1014 let gas_status = self.gas_charger.move_gas_status_mut();
1015 let values = vm
1016 .execute_function_bypass_visibility(
1017 original_mid,
1018 function_name,
1019 ty_args,
1020 args.into_iter().map(|v| v.0.into()).collect(),
1021 &mut SuiGasMeter(gas_status),
1022 tracer.as_mut(),
1023 )
1024 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
1025 Ok(values.into_iter().map(|v| CtxValue(v.into())).collect())
1026 }
1027
1028 pub fn deserialize_package(
1033 &mut self,
1034 package_payload: PackagePayload,
1035 dep_ids: &[ObjectID],
1036 ) -> Result<DeserializedPackage, Mode::Error> {
1037 Ok(match package_payload {
1038 PackagePayload::Deserialized(deserialized_pkg) => deserialized_pkg,
1039 PackagePayload::Serialized(module_bytes) => {
1040 assert_invariant!(
1044 !module_bytes.is_empty(),
1045 "empty package is checked in transaction input checker"
1046 );
1047 let total_bytes = module_bytes.iter().map(|v| v.len()).sum();
1048 self.gas_charger.charge_publish_package(total_bytes)?;
1049 self.env.deserialize_package(&module_bytes, dep_ids)?
1050 }
1051 })
1052 }
1053
1054 fn fetch_package(&mut self, dependency_id: &ObjectID) -> Result<Rc<MovePackage>, Mode::Error> {
1055 let [fetched_package] = self.fetch_packages(&[*dependency_id])?.try_into().map_err(
1056 |_| {
1057 make_invariant_violation!(
1058 "We should always fetch a single package for each object or return a dependency error."
1059 )
1060 },
1061 )?;
1062 Ok(fetched_package)
1063 }
1064
1065 fn fetch_packages(
1066 &mut self,
1067 dependency_ids: &[ObjectID],
1068 ) -> Result<Vec<Rc<MovePackage>>, Mode::Error> {
1069 let mut fetched = vec![];
1070 let mut missing = vec![];
1071
1072 let dependency_ids: BTreeSet<_> = dependency_ids.iter().collect();
1074
1075 for id in &dependency_ids {
1076 match self.env.linkable_store.get_move_package(id) {
1077 Err(e) => {
1078 return Err(Mode::Error::new_with_source(
1079 ExecutionErrorKind::PublishUpgradeMissingDependency,
1080 e,
1081 ));
1082 }
1083 Ok(Some(inner)) => {
1084 fetched.push(inner);
1085 }
1086 Ok(None) => {
1087 missing.push(*id);
1088 }
1089 }
1090 }
1091
1092 if missing.is_empty() {
1093 assert_invariant!(
1094 fetched.len() == dependency_ids.len(),
1095 "all dependencies should be fetched"
1096 );
1097 Ok(fetched)
1098 } else {
1099 let msg = format!(
1100 "Missing dependencies: {}",
1101 missing
1102 .into_iter()
1103 .map(|dep| format!("{}", dep))
1104 .collect::<Vec<_>>()
1105 .join(", ")
1106 );
1107 Err(Mode::Error::new_with_source(
1108 ExecutionErrorKind::PublishUpgradeMissingDependency,
1109 msg,
1110 ))
1111 }
1112 }
1113
1114 fn publish_and_verify_modules(
1115 &mut self,
1116 package_id: ObjectID,
1117 pkg: &MovePackage,
1118 modules: &[CompiledModule],
1119 linkage: &ExecutableLinkage,
1120 ) -> Result<(VerifiedPackage, MoveVM<'env>), Mode::Error> {
1121 let serialized_pkg = pkg.into_serialized_move_package().map_err(|e| {
1122 make_invariant_violation!("Failed to serialize package for verification: {}", e)
1123 })?;
1124 let data_store = &self.env.linkable_store.package_store;
1125 let vm = self
1126 .env
1127 .vm
1128 .validate_package(
1129 data_store,
1130 *package_id,
1131 serialized_pkg,
1132 &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
1133 self.native_extensions.clone(),
1134 )
1135 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
1136
1137 for module in modules {
1139 sui_verifier::verifier::sui_verify_module_unmetered(
1142 module,
1143 &BTreeMap::new(),
1144 &self
1145 .env
1146 .protocol_config
1147 .verifier_config(None),
1148 )?;
1149 }
1150
1151 Ok(vm)
1152 }
1153
1154 fn push_package_and_init_selected_modules<'a>(
1160 &mut self,
1161 package_id: ObjectID,
1162 package: MovePackage,
1163 verified_pkg: VerifiedPackage,
1164 vm: MoveVM<'env>,
1165 modules: impl IntoIterator<Item = &'a CompiledModule>,
1166 linkage: &ExecutableLinkage,
1167 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1168 expected_stack_height: u64,
1169 ) -> Result<(), Mode::Error> {
1170 self.env.linkable_store.package_store.push_package(
1171 package_id,
1172 Rc::new(package),
1173 verified_pkg,
1174 )?;
1175
1176 match self.init_selected_modules(
1177 vm,
1178 package_id,
1179 modules,
1180 linkage,
1181 trace_builder_opt,
1182 expected_stack_height,
1183 ) {
1184 Ok(()) => Ok(()),
1185 Err(e) => {
1186 self.env
1187 .linkable_store
1188 .package_store
1189 .pop_package(package_id)?;
1190 Err(e)
1191 }
1192 }
1193 }
1194
1195 fn init_selected_modules<'a>(
1196 &mut self,
1197 mut vm: MoveVM<'env>,
1198 package_id: ObjectID,
1199 modules: impl IntoIterator<Item = &'a CompiledModule>,
1200 linkage: &ExecutableLinkage,
1201 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1202 expected_stack_height: u64,
1203 ) -> Result<(), Mode::Error> {
1204 debug_assert_eq!(
1205 self.gas_charger.move_gas_status().stack_height_current(),
1206 expected_stack_height,
1207 );
1208 for module in modules {
1209 let Some((fdef_idx, fdef)) = module.find_function_def_by_name(INIT_FN_NAME.as_str())
1210 else {
1211 continue;
1212 };
1213 let fhandle = module.function_handle_at(fdef.function);
1214 let fparameters = module.signature_at(fhandle.parameters);
1215 assert_invariant!(
1216 fparameters.0.len() <= 2,
1217 "init function should have at most 2 parameters"
1218 );
1219 let has_otw = fparameters.0.len() == 2;
1220 let tx_context = self
1221 .location(UsageKind::Borrow, T::Location::TxContext)
1222 .map_err(|e| {
1223 make_invariant_violation!("Failed to get tx context for init function: {}", e)
1224 })?;
1225 charge_gas!(self, charge_store_loc, &tx_context)?;
1227
1228 let args = if has_otw {
1229 vec![CtxValue(Value::one_time_witness()?), CtxValue(tx_context)]
1230 } else {
1231 vec![CtxValue(tx_context)]
1232 };
1233 debug_assert_eq!(
1234 self.gas_charger.move_gas_status().stack_height_current(),
1235 expected_stack_height,
1236 );
1237 trace_utils::trace_move_call_start(trace_builder_opt);
1238 let return_values = self.execute_function_bypass_visibility_with_vm(
1239 &mut vm,
1240 &module.self_id(),
1241 INIT_FN_NAME,
1242 vec![],
1243 args,
1244 linkage,
1245 trace_builder_opt,
1246 )?;
1247 trace_utils::trace_move_call_end(trace_builder_opt);
1248
1249 let version_mid = ModuleId::new(package_id.into(), module.self_id().name().to_owned());
1250 self.take_user_events(
1251 &vm,
1252 version_mid,
1253 fdef_idx,
1254 fdef.code
1255 .as_ref()
1256 .map(|c| checked_as!(c.code.len(), u16))
1257 .transpose()?
1258 .unwrap_or(0),
1259 linkage,
1260 )?;
1261 assert_invariant!(
1262 return_values.is_empty(),
1263 "init should not have return values"
1264 );
1265 debug_assert_eq!(
1266 self.gas_charger.move_gas_status().stack_height_current(),
1267 expected_stack_height,
1268 );
1269 }
1270
1271 Ok(())
1272 }
1273
1274 pub fn publish_and_init_package(
1275 &mut self,
1276 mut modules: Vec<CompiledModule>,
1277 dep_ids: &[ObjectID],
1278 linkage: ResolvedLinkage,
1279 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1280 ) -> Result<ObjectID, Mode::Error> {
1281 let original_id = if Mode::packages_are_predefined() {
1282 (*modules.safe_get(0)?.self_id().address()).into()
1284 } else {
1285 let id = self.tx_context.borrow_mut().fresh_id();
1289 adapter::substitute_package_id(&mut modules, id)?;
1290 id
1291 };
1292
1293 let dependencies = self.fetch_packages(dep_ids)?;
1294 let package = MovePackage::new_initial(
1295 &modules,
1296 self.env.protocol_config,
1297 dependencies.iter().map(|p| p.as_ref()),
1298 )?;
1299 let package_id = package.id();
1300
1301 let linkage = ResolvedLinkage::update_for_publication(package_id, original_id, linkage);
1302
1303 let (pkg, vm) =
1304 self.publish_and_verify_modules(original_id, &package, &modules, &linkage)?;
1305 self.push_package_and_init_selected_modules(
1306 package_id,
1307 package,
1308 pkg,
1309 vm,
1310 &modules,
1311 &linkage,
1312 trace_builder_opt,
1313 PUBLISH_INIT_EXPECTED_STACK_HEIGHT,
1314 )?;
1315 Ok(original_id)
1316 }
1317
1318 pub fn upgrade(
1319 &mut self,
1320 mut modules: Vec<CompiledModule>,
1321 dep_ids: &[ObjectID],
1322 current_package_id: ObjectID,
1323 upgrade_ticket_policy: u8,
1324 linkage: ResolvedLinkage,
1325 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1326 ) -> Result<ObjectID, Mode::Error> {
1327 let current_move_package = self.fetch_package(¤t_package_id)?;
1329
1330 let original_id = current_move_package.original_package_id();
1331 adapter::substitute_package_id(&mut modules, original_id)?;
1332
1333 let version_id = self.tx_context.borrow_mut().fresh_id();
1338
1339 let dependencies = self.fetch_packages(dep_ids)?;
1340 let package = current_move_package.new_upgraded(
1341 version_id,
1342 &modules,
1343 self.env.protocol_config,
1344 dependencies.iter().map(|p| p.as_ref()),
1345 )?;
1346
1347 let linkage = ResolvedLinkage::update_for_publication(version_id, original_id, linkage);
1348 let (verified_pkg, vm) =
1349 self.publish_and_verify_modules(original_id, &package, &modules, &linkage)?;
1350
1351 check_compatibility(
1352 self.env.protocol_config,
1353 current_move_package.as_ref(),
1354 &modules,
1355 upgrade_ticket_policy,
1356 )?;
1357
1358 let current_module_names: BTreeSet<&str> = current_move_package
1361 .serialized_module_map()
1362 .keys()
1363 .map(|s| s.as_str())
1364 .collect();
1365 let new_modules = modules
1366 .iter()
1367 .filter(|m| {
1368 let name = m.identifier_at(m.self_handle().name).as_str();
1369 !current_module_names.contains(name)
1370 })
1371 .collect::<Vec<&CompiledModule>>();
1372
1373 if self.env.protocol_config.enable_init_on_upgrade() {
1374 self.push_package_and_init_selected_modules(
1375 version_id,
1376 package,
1377 verified_pkg,
1378 vm,
1379 new_modules.iter().copied(),
1380 &linkage,
1381 trace_builder_opt,
1382 UPGRADE_INIT_EXPECTED_STACK_HEIGHT,
1383 )?;
1384 } else {
1385 let new_module_has_init = new_modules.iter().any(|module| {
1386 module.function_defs.iter().any(|fdef| {
1387 let fhandle = module.function_handle_at(fdef.function);
1388 let fname = module.identifier_at(fhandle.name);
1389 fname == INIT_FN_NAME
1390 })
1391 });
1392 if new_module_has_init {
1393 return Err(Mode::Error::new_with_source(
1394 ExecutionErrorKind::FeatureNotYetSupported,
1395 "`init` in new modules on upgrade is not yet supported",
1396 ));
1397 }
1398
1399 self.env.linkable_store.package_store.push_package(
1400 version_id,
1401 Rc::new(package),
1402 verified_pkg,
1403 )?;
1404 }
1405
1406 Ok(version_id)
1407 }
1408
1409 pub fn transfer_object(
1414 &mut self,
1415 recipient: Owner,
1416 ty: Type,
1417 object: CtxValue,
1418 ) -> Result<(), Mode::Error> {
1419 self.transfer_object_(recipient, ty, object, false)
1420 }
1421
1422 fn transfer_object_(
1423 &mut self,
1424 recipient: Owner,
1425 ty: Type,
1426 object: CtxValue,
1427 end_of_transaction: bool,
1428 ) -> Result<(), Mode::Error> {
1429 let tag = TypeTag::try_from(ty)
1430 .map_err(|_| make_invariant_violation!("Unable to convert Type to TypeTag"))?;
1431 let TypeTag::Struct(tag) = tag else {
1432 invariant_violation!("Expected struct type tag");
1433 };
1434 let ty = MoveObjectType::from(*tag);
1435 object_runtime_mut!(self)?
1436 .transfer(recipient, ty, object.0.into(), end_of_transaction)
1437 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
1438 Ok(())
1439 }
1440
1441 #[allow(clippy::type_complexity)]
1446 pub fn argument_updates(
1447 &mut self,
1448 args: Vec<T::Argument>,
1449 ) -> Result<Vec<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, Mode::Error> {
1450 args.into_iter()
1451 .filter_map(|arg| self.argument_update(arg).transpose())
1452 .collect()
1453 }
1454
1455 #[allow(clippy::type_complexity)]
1456 fn argument_update(
1457 &mut self,
1458 sp!(_, (arg, ty)): T::Argument,
1459 ) -> Result<Option<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, Mode::Error> {
1460 use sui_types::transaction::Argument as TxArgument;
1461 let ty = match ty {
1462 Type::Reference(true, inner) => (*inner).clone(),
1463 ty => {
1464 debug_assert!(
1465 false,
1466 "Unexpected non reference type in location update: {ty:?}"
1467 );
1468 return Ok(None);
1469 }
1470 };
1471 let Ok(tag): Result<TypeTag, _> = ty.clone().try_into() else {
1472 invariant_violation!("unable to generate type tag from type")
1473 };
1474 let location = arg.location();
1475 let resolved = self.locations.resolve(location)?;
1476 let local = match resolved {
1477 ResolvedLocation::Local(local)
1478 | ResolvedLocation::Pure { local, .. }
1479 | ResolvedLocation::Receiving { local, .. } => local,
1480 };
1481 if local.is_invalid()? {
1482 return Ok(None);
1483 }
1484 let value = local.copy()?;
1486 let value = match arg {
1487 T::Argument__::Use(_) => {
1488 value.read_ref()?
1490 }
1491 T::Argument__::Borrow(_, _) => {
1492 value
1494 }
1495 T::Argument__::Freeze(_) => {
1496 invariant_violation!("freeze should not be used for a mutable reference")
1497 }
1498 T::Argument__::Read(_) => {
1499 invariant_violation!("read should not return a reference")
1500 }
1501 };
1502 let layout = self.env.runtime_layout(&ty)?;
1503 let Some(bytes) = value.typed_serialize(&layout) else {
1504 invariant_violation!("Failed to serialize Move value");
1505 };
1506 let arg = match location {
1507 T::Location::TxContext => return Ok(None),
1508 T::Location::GasCoin => TxArgument::GasCoin,
1509 T::Location::Result(i, j) => TxArgument::NestedResult(i, j),
1510 T::Location::ObjectInput(i) => TxArgument::Input(
1511 self.locations
1512 .input_object_metadata
1513 .safe_get(i as usize)?
1514 .0
1515 .0,
1516 ),
1517 T::Location::WithdrawalInput(i) => TxArgument::Input(
1518 self.locations
1519 .input_withdrawal_metadata
1520 .safe_get(i as usize)?
1521 .original_input_index
1522 .0,
1523 ),
1524 T::Location::PureInput(i) => TxArgument::Input(
1525 self.locations
1526 .pure_input_metadata
1527 .safe_get(i as usize)?
1528 .original_input_index
1529 .0,
1530 ),
1531 T::Location::ReceivingInput(i) => TxArgument::Input(
1532 self.locations
1533 .receiving_input_metadata
1534 .safe_get(i as usize)?
1535 .original_input_index
1536 .0,
1537 ),
1538 };
1539 Ok(Some((arg, bytes, tag)))
1540 }
1541
1542 pub fn tracked_results(
1543 &self,
1544 results: &[CtxValue],
1545 result_tys: &T::ResultType,
1546 ) -> Result<Vec<(Vec<u8>, TypeTag)>, Mode::Error> {
1547 assert_invariant!(
1548 results.len() == result_tys.len(),
1549 "results and result types should match"
1550 );
1551 results
1552 .iter()
1553 .zip_debug_eq(result_tys)
1554 .map(|(v, ty)| self.tracked_result(&v.0, ty.clone()))
1555 .collect()
1556 }
1557
1558 fn tracked_result(&self, result: &Value, ty: Type) -> Result<(Vec<u8>, TypeTag), Mode::Error> {
1559 let inner_value;
1560 let (v, ty) = match ty {
1561 Type::Reference(_, inner) => {
1562 inner_value = result.copy()?.read_ref()?;
1563 (&inner_value, (*inner).clone())
1564 }
1565 _ => (result, ty),
1566 };
1567 let layout = self.env.runtime_layout(&ty)?;
1568 let Some(bytes) = v.typed_serialize(&layout) else {
1569 invariant_violation!("Failed to serialize Move value");
1570 };
1571 let Ok(tag): Result<TypeTag, _> = ty.try_into() else {
1572 invariant_violation!("unable to generate type tag from type")
1573 };
1574 Ok((bytes, tag))
1575 }
1576}
1577
1578impl VMValueCast<CtxValue> for VMValue {
1579 fn cast(self) -> Result<CtxValue, PartialVMError> {
1580 Ok(CtxValue(self.into()))
1581 }
1582}
1583
1584impl CtxValue {
1585 pub fn vec_pack(ty: Type, values: Vec<CtxValue>) -> Result<CtxValue, ExecutionError> {
1586 Ok(CtxValue(Value::vec_pack(
1587 ty,
1588 values.into_iter().map(|v| v.0).collect(),
1589 )?))
1590 }
1591
1592 pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
1593 self.0.coin_ref_value()
1594 }
1595
1596 pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
1597 self.0.coin_ref_subtract_balance(amount)
1598 }
1599
1600 pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
1601 self.0.coin_ref_add_balance(amount)
1602 }
1603
1604 pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
1605 self.0.into_upgrade_ticket()
1606 }
1607
1608 pub fn to_address(&self) -> Result<AccountAddress, ExecutionError> {
1609 self.0.copy()?.cast()
1610 }
1611
1612 pub(super) fn inner_for_tracing(&self) -> &Value {
1614 &self.0
1615 }
1616}
1617
1618fn load_object_arg<Mode: ExecutionMode>(
1619 meter: &mut GasCharger,
1620 env: &Env<Mode>,
1621 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1622 input: T::ObjectInput,
1623) -> Result<(T::InputIndex, InputObjectMetadata, Value), Mode::Error> {
1624 let id = input.arg.id();
1625 let refined_permissions = input.arg.refined_permissions;
1626 let (metadata, value) = load_object_arg_impl(
1627 meter,
1628 env,
1629 input_object_map,
1630 id,
1631 refined_permissions,
1632 input.ty,
1633 )?;
1634 Ok((input.original_input_index, metadata, value))
1635}
1636
1637fn load_object_arg_impl<Mode: ExecutionMode>(
1638 meter: &mut GasCharger,
1639 env: &Env<Mode>,
1640 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1641 id: ObjectID,
1642 refined_permissions: ObjectPermissions,
1643 ty: T::Type,
1644) -> Result<(InputObjectMetadata, Value), Mode::Error> {
1645 let obj = env.read_object(&id)?;
1646 let owner = obj.owner.clone();
1647 let version = obj.version();
1648 let object_metadata = InputObjectMetadata {
1649 newly_created: false,
1650 id,
1651 refined_permissions,
1652 owner: owner.clone(),
1653 version,
1654 type_: ty.clone(),
1655 };
1656 let sui_types::object::ObjectInner {
1657 data: sui_types::object::Data::Move(move_obj),
1658 ..
1659 } = obj.as_inner()
1660 else {
1661 invariant_violation!("Expected a Move object");
1662 };
1663 assert_expected_move_object_type(&object_metadata.type_, move_obj.type_())?;
1664 let contained_uids = {
1665 let fully_annotated_layout = env.fully_annotated_layout(&ty)?;
1666 get_all_uids(&fully_annotated_layout, move_obj.contents()).map_err(|e| {
1667 make_invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1668 })?
1669 };
1670 input_object_map.insert(
1671 id,
1672 object_runtime::InputObject {
1673 contained_uids,
1674 version,
1675 owner,
1676 },
1677 );
1678
1679 let v = Value::deserialize(env, move_obj.contents(), ty)?;
1680 charge_gas_!(meter, env, charge_copy_loc, &v)?;
1681 charge_gas_!(meter, env, charge_store_loc, &v)?;
1682 Ok((object_metadata, v))
1683}
1684
1685fn load_withdrawal_arg<Mode: ExecutionMode>(
1686 meter: &mut GasCharger,
1687 env: &Env<Mode>,
1688 withdrawal: &T::WithdrawalInput,
1689) -> Result<Value, Mode::Error> {
1690 let T::WithdrawalInput {
1691 original_input_index: _,
1692 ty: _,
1693 owner,
1694 amount,
1695 } = withdrawal;
1696 let loaded = Value::funds_accumulator_withdrawal(*owner, *amount);
1697 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1698 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1699 Ok(loaded)
1700}
1701
1702fn load_pure_value<Mode: ExecutionMode>(
1703 meter: &mut GasCharger,
1704 env: &Env<Mode>,
1705 bytes: &[u8],
1706 metadata: &T::PureInput,
1707) -> Result<Value, Mode::Error> {
1708 let loaded = Value::deserialize(env, bytes, metadata.ty.clone())?;
1709 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1711 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1712 Ok(loaded)
1713}
1714
1715fn load_receiving_value<Mode: ExecutionMode>(
1716 meter: &mut GasCharger,
1717 env: &Env<Mode>,
1718 metadata: &T::ReceivingInput,
1719) -> Result<Value, Mode::Error> {
1720 let (id, version, _) = metadata.object_ref;
1721 let loaded = Value::receiving(id, version);
1722 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1723 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1724 Ok(loaded)
1725}
1726
1727fn copy_value<Mode: ExecutionMode>(
1728 meter: &mut GasCharger,
1729 env: &Env<Mode>,
1730 value: &Value,
1731) -> Result<Value, Mode::Error> {
1732 charge_gas_!(meter, env, charge_copy_loc, value)?;
1733 charge_gas_!(meter, env, charge_pop, value)?;
1734 Ok(value.copy()?)
1735}
1736
1737fn refund_max_gas_budget<OType>(
1741 writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1742 accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1743 gas_charger: &mut GasCharger,
1744 gas_id: ObjectID,
1745 gas_coin_transfer: Option<&GasCoinTransfer>,
1746) -> Result<(), ExecutionError> {
1747 match gas_coin_transfer {
1748 Some(GasCoinTransfer::SendFunds { recipient, .. }) => {
1749 assert_invariant!(
1752 !writes.contains_key(&gas_id),
1753 "Gas coin should not be in writes if it was used with send_funds"
1754 );
1755 balance_change_accumulator_event(
1756 accumulator_events,
1757 *recipient,
1758 checked_as!(gas_charger.gas_budget(), i64)?,
1759 )?;
1760 }
1761 Some(GasCoinTransfer::TransferObjects) | None => {
1762 let Some((_, _, value_ref)) = writes.get_mut(&gas_id) else {
1763 invariant_violation!("Gas object cannot be wrapped or destroyed")
1764 };
1765 let value = std::mem::replace(value_ref, VMValue::u8(0));
1767 let mut locals = Locals::new([Some(value.into())])?;
1768 let mut local = locals.local(0)?;
1769 let coin_value = local.borrow()?.coin_ref_value()?;
1770 let additional = gas_charger.gas_budget();
1771 if coin_value.checked_add(additional).is_none() {
1772 return Err(ExecutionError::new_with_source(
1773 ExecutionErrorKind::CoinBalanceOverflow,
1774 "Gas coin too large after returning the max gas budget",
1775 ));
1776 };
1777 local.borrow()?.coin_ref_add_balance(additional)?;
1778 *value_ref = local.move_()?.into();
1780 }
1781 };
1782 Ok(())
1783}
1784
1785fn finish_gas_coin<OType>(
1791 gas_charger: &mut GasCharger,
1792 writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1793 created_object_ids: &mut IndexSet<ObjectID>,
1794 deleted_object_ids: &IndexSet<ObjectID>,
1795 accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1796 gas_id: ObjectID,
1797 gas_payment: GasPayment,
1798 gas_coin_transfer: Option<GasCoinTransfer>,
1799) -> Result<(), ExecutionError> {
1800 refund_max_gas_budget(
1802 writes,
1803 accumulator_events,
1804 gas_charger,
1805 gas_id,
1806 gas_coin_transfer.as_ref(),
1807 )?;
1808
1809 match &gas_coin_transfer {
1815 Some(GasCoinTransfer::SendFunds { recipient, .. }) => {
1816 gas_charger.override_gas_charge_location(PaymentLocation::AddressBalance(
1817 (*recipient).into(),
1818 ))?;
1819 }
1820 Some(GasCoinTransfer::TransferObjects) => {
1821 gas_charger.override_gas_charge_location(PaymentLocation::Coin(gas_id))?;
1822 }
1823 None => (),
1824 }
1825
1826 let address = match gas_payment.location {
1828 PaymentLocation::Coin(_) => {
1829 assert_invariant!(
1831 !matches!(gas_coin_transfer, Some(GasCoinTransfer::SendFunds { .. }))
1832 || deleted_object_ids.contains(&gas_id),
1833 "send_funds transfer implies the coin should be deleted"
1834 );
1835 return Ok(());
1836 }
1837 PaymentLocation::AddressBalance(address) => address,
1838 };
1839
1840 let net_balance_change = if let Some(gas_coin_transfer) = gas_coin_transfer {
1841 match gas_coin_transfer {
1843 GasCoinTransfer::TransferObjects => {
1844 assert_invariant!(
1845 created_object_ids.contains(&gas_id),
1846 "ephemeral coin should be newly created"
1847 );
1848 assert_invariant!(
1849 !deleted_object_ids.contains(&gas_id),
1850 "ephemeral coin should not be deleted if transferred as an object"
1851 );
1852 assert_invariant!(
1853 writes.contains_key(&gas_id),
1854 "ephemeral coin should be in writes if transferred as an object"
1855 );
1856 }
1857 GasCoinTransfer::SendFunds { .. } => {
1858 assert_invariant!(
1859 !created_object_ids.contains(&gas_id),
1860 "ephemeral coin should not be newly created if transferred with send_funds"
1861 );
1862 assert_invariant!(
1863 !deleted_object_ids.contains(&gas_id),
1864 "ephemeral coin should not be deleted if transferred with send_funds"
1865 );
1866 assert_invariant!(
1867 !writes.contains_key(&gas_id),
1868 "ephemeral coin should not be in writes if transferred with send_funds"
1869 );
1870 }
1871 }
1872
1873 let Some(net_balance_change) = gas_payment
1878 .amount
1879 .try_into()
1880 .ok()
1881 .and_then(|i: i64| i.checked_neg())
1882 else {
1883 invariant_violation!("Gas payment amount cannot be represented as i64")
1884 };
1885 net_balance_change
1886 } else {
1887 let was_created = created_object_ids.shift_remove(&gas_id);
1891 assert_invariant!(was_created, "ephemeral coin should be newly created");
1892 let Some((_owner, _ty, value)) = writes.shift_remove(&gas_id) else {
1893 invariant_violation!("checked above that the gas coin was present")
1894 };
1895 let (_id, remaining_balance) = Value::from(value).unpack_coin()?;
1896 let Some(net_balance_change): Option<i64> = (remaining_balance as i128)
1900 .checked_sub(gas_payment.amount as i128)
1901 .and_then(|i| i.try_into().ok())
1902 else {
1903 invariant_violation!("Remaining balance could not be represented as i64")
1904 };
1905 net_balance_change
1906 };
1907 balance_change_accumulator_event(accumulator_events, address.into(), net_balance_change)?;
1908 Ok(())
1909}
1910
1911fn balance_change_accumulator_event(
1912 accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1913 address: AccountAddress,
1914 balance_change: i64,
1915) -> Result<(), ExecutionError> {
1916 if balance_change == 0 {
1917 return Ok(());
1918 }
1919 let balance_type = Balance::type_tag(sui_types::gas_coin::GAS::type_tag());
1920 let Some(accumulator_id) =
1921 accumulator_root::AccumulatorValue::get_field_id(address.into(), &balance_type).ok()
1922 else {
1923 invariant_violation!("Failed to compute accumulator field id")
1924 };
1925 let (action, value) = if balance_change < 0 {
1926 (
1927 MoveAccumulatorAction::Split,
1928 MoveAccumulatorValue::U64(balance_change.unsigned_abs()),
1929 )
1930 } else {
1931 (
1932 MoveAccumulatorAction::Merge,
1933 MoveAccumulatorValue::U64(balance_change as u64),
1934 )
1935 };
1936 accumulator_events.push(MoveAccumulatorEvent {
1937 accumulator_id: *accumulator_id.inner(),
1938 action,
1939 target_addr: address,
1940 target_ty: balance_type,
1941 value,
1942 });
1943 Ok(())
1944}
1945
1946unsafe fn create_written_object<Mode: ExecutionMode>(
1952 env: &Env<Mode>,
1953 objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1954 id: ObjectID,
1955 type_: Type,
1956 has_public_transfer: bool,
1957 contents: Vec<u8>,
1958) -> Result<MoveObject, ExecutionError> {
1959 debug_assert_eq!(
1960 id,
1961 MoveObject::id_opt(&contents).expect("object contents should start with an id")
1962 );
1963 let old_obj_ver = objects_modified_at
1964 .get(&id)
1965 .map(|obj: &LoadedRuntimeObject| obj.version);
1966
1967 let Ok(type_tag): Result<TypeTag, _> = type_.try_into() else {
1968 invariant_violation!("unable to generate type tag from type")
1969 };
1970
1971 let struct_tag = match type_tag {
1972 TypeTag::Struct(inner) => *inner,
1973 _ => invariant_violation!("Non struct type for object"),
1974 };
1975 unsafe {
1976 MoveObject::new_from_execution(
1977 struct_tag.into(),
1978 has_public_transfer,
1979 old_obj_ver.unwrap_or_default(),
1980 contents,
1981 env.protocol_config,
1982 Mode::packages_are_predefined(),
1983 )
1984 }
1985}
1986
1987pub fn subst_signature(
1989 signature: LoadedFunctionInformation,
1990 type_arguments: &[VMType],
1991) -> VMResult<LoadedFunctionInformation> {
1992 let LoadedFunctionInformation {
1993 parameters,
1994 return_,
1995 is_entry,
1996 is_native,
1997 visibility,
1998 index,
1999 instruction_count,
2000 } = signature;
2001 let parameters = parameters
2002 .into_iter()
2003 .map(|ty| ty.subst(type_arguments))
2004 .collect::<PartialVMResult<Vec<_>>>()
2005 .map_err(|err| err.finish(Location::Undefined))?;
2006 let return_ = return_
2007 .into_iter()
2008 .map(|ty| ty.subst(type_arguments))
2009 .collect::<PartialVMResult<Vec<_>>>()
2010 .map_err(|err| err.finish(Location::Undefined))?;
2011 Ok(LoadedFunctionInformation {
2012 parameters,
2013 return_,
2014 is_entry,
2015 is_native,
2016 visibility,
2017 index,
2018 instruction_count,
2019 })
2020}
2021
2022pub enum EitherError<E: ExecutionErrorTrait = ExecutionError> {
2023 CommandArgument(CommandArgumentError),
2024 Execution(E),
2025}
2026
2027impl<E: ExecutionErrorTrait> From<ExecutionError> for EitherError<E> {
2028 fn from(e: ExecutionError) -> Self {
2029 EitherError::Execution(e.into())
2030 }
2031}
2032
2033impl<E: ExecutionErrorTrait> From<CommandArgumentError> for EitherError<E> {
2034 fn from(e: CommandArgumentError) -> Self {
2035 EitherError::CommandArgument(e)
2036 }
2037}
2038
2039impl<E: ExecutionErrorTrait> EitherError<E> {
2040 pub fn into_execution_error(self, command_index: usize) -> E {
2041 match self {
2042 EitherError::CommandArgument(e) => command_argument_error(e, command_index).into(),
2043 EitherError::Execution(e) => e,
2044 }
2045 }
2046}
2047
2048#[derive(Debug)]
2056pub enum PrimitiveArgumentLayout {
2057 Option(Box<PrimitiveArgumentLayout>),
2059 Vector(Box<PrimitiveArgumentLayout>),
2061 Ascii,
2063 UTF8,
2065 Bool,
2067 U8,
2068 U16,
2069 U32,
2070 U64,
2071 U128,
2072 U256,
2073 Address,
2074}
2075
2076impl PrimitiveArgumentLayout {
2077 pub fn bcs_only(&self) -> bool {
2081 match self {
2082 PrimitiveArgumentLayout::Option(_)
2084 | PrimitiveArgumentLayout::Ascii
2085 | PrimitiveArgumentLayout::UTF8 => false,
2086 PrimitiveArgumentLayout::Bool
2088 | PrimitiveArgumentLayout::U8
2089 | PrimitiveArgumentLayout::U16
2090 | PrimitiveArgumentLayout::U32
2091 | PrimitiveArgumentLayout::U64
2092 | PrimitiveArgumentLayout::U128
2093 | PrimitiveArgumentLayout::U256
2094 | PrimitiveArgumentLayout::Address => true,
2095 PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
2097 }
2098 }
2099}
2100
2101pub fn bcs_argument_validate(
2105 bytes: &[u8],
2106 idx: u16,
2107 layout: PrimitiveArgumentLayout,
2108) -> Result<(), ExecutionError> {
2109 bcs::from_bytes_seed(&layout, bytes).map_err(|_| {
2110 ExecutionError::new_with_source(
2111 ExecutionErrorKind::command_argument_error(CommandArgumentError::InvalidBCSBytes, idx),
2112 format!("Function expects {layout} but provided argument's value does not match",),
2113 )
2114 })
2115}
2116
2117impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout {
2118 type Value = ();
2119 fn deserialize<D: serde::de::Deserializer<'d>>(
2120 self,
2121 deserializer: D,
2122 ) -> Result<Self::Value, D::Error> {
2123 use serde::de::Error;
2124 match self {
2125 PrimitiveArgumentLayout::Ascii => {
2126 let s: &str = serde::Deserialize::deserialize(deserializer)?;
2127 if !s.is_ascii() {
2128 Err(D::Error::custom("not an ascii string"))
2129 } else {
2130 Ok(())
2131 }
2132 }
2133 PrimitiveArgumentLayout::UTF8 => {
2134 deserializer.deserialize_string(serde::de::IgnoredAny)?;
2135 Ok(())
2136 }
2137 PrimitiveArgumentLayout::Option(layout) => {
2138 deserializer.deserialize_option(OptionElementVisitor(layout))
2139 }
2140 PrimitiveArgumentLayout::Vector(layout) => {
2141 deserializer.deserialize_seq(VectorElementVisitor(layout))
2142 }
2143 PrimitiveArgumentLayout::Bool => {
2146 deserializer.deserialize_bool(serde::de::IgnoredAny)?;
2147 Ok(())
2148 }
2149 PrimitiveArgumentLayout::U8 => {
2150 deserializer.deserialize_u8(serde::de::IgnoredAny)?;
2151 Ok(())
2152 }
2153 PrimitiveArgumentLayout::U16 => {
2154 deserializer.deserialize_u16(serde::de::IgnoredAny)?;
2155 Ok(())
2156 }
2157 PrimitiveArgumentLayout::U32 => {
2158 deserializer.deserialize_u32(serde::de::IgnoredAny)?;
2159 Ok(())
2160 }
2161 PrimitiveArgumentLayout::U64 => {
2162 deserializer.deserialize_u64(serde::de::IgnoredAny)?;
2163 Ok(())
2164 }
2165 PrimitiveArgumentLayout::U128 => {
2166 deserializer.deserialize_u128(serde::de::IgnoredAny)?;
2167 Ok(())
2168 }
2169 PrimitiveArgumentLayout::U256 => {
2170 U256::deserialize(deserializer)?;
2171 Ok(())
2172 }
2173 PrimitiveArgumentLayout::Address => {
2174 SuiAddress::deserialize(deserializer)?;
2175 Ok(())
2176 }
2177 }
2178 }
2179}
2180
2181struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2182
2183impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> {
2184 type Value = ();
2185
2186 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2187 formatter.write_str("Vector")
2188 }
2189
2190 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
2191 where
2192 A: serde::de::SeqAccess<'d>,
2193 {
2194 while seq.next_element_seed(self.0)?.is_some() {}
2195 Ok(())
2196 }
2197}
2198
2199struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2200
2201impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> {
2202 type Value = ();
2203
2204 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2205 formatter.write_str("Option")
2206 }
2207
2208 fn visit_none<E>(self) -> Result<Self::Value, E>
2209 where
2210 E: serde::de::Error,
2211 {
2212 Ok(())
2213 }
2214
2215 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
2216 where
2217 D: serde::Deserializer<'d>,
2218 {
2219 self.0.deserialize(deserializer)
2220 }
2221}
2222
2223impl fmt::Display for PrimitiveArgumentLayout {
2224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2225 match self {
2226 PrimitiveArgumentLayout::Vector(inner) => {
2227 write!(f, "vector<{inner}>")
2228 }
2229 PrimitiveArgumentLayout::Option(inner) => {
2230 write!(f, "std::option::Option<{inner}>")
2231 }
2232 PrimitiveArgumentLayout::Ascii => {
2233 write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2)
2234 }
2235 PrimitiveArgumentLayout::UTF8 => {
2236 write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2)
2237 }
2238 PrimitiveArgumentLayout::Bool => write!(f, "bool"),
2239 PrimitiveArgumentLayout::U8 => write!(f, "u8"),
2240 PrimitiveArgumentLayout::U16 => write!(f, "u16"),
2241 PrimitiveArgumentLayout::U32 => write!(f, "u32"),
2242 PrimitiveArgumentLayout::U64 => write!(f, "u64"),
2243 PrimitiveArgumentLayout::U128 => write!(f, "u128"),
2244 PrimitiveArgumentLayout::U256 => write!(f, "u256"),
2245 PrimitiveArgumentLayout::Address => write!(f, "address"),
2246 }
2247 }
2248}
2249
2250pub fn finish(
2251 protocol_config: &ProtocolConfig,
2252 state_view: &dyn ExecutionState,
2253 gas_charger: &mut GasCharger,
2254 tx_context: &TxContext,
2255 by_value_shared_objects: &BTreeSet<ObjectID>,
2256 consensus_owner_objects: &BTreeMap<ObjectID, Owner>,
2257 loaded_runtime_objects: BTreeMap<ObjectID, LoadedRuntimeObject>,
2258 written_objects: BTreeMap<ObjectID, Object>,
2259 created_object_ids: IndexSet<ObjectID>,
2260 deleted_object_ids: IndexSet<ObjectID>,
2261 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
2262 accumulator_events: Vec<MoveAccumulatorEvent>,
2263 settlement_input_sui: u64,
2264 settlement_output_sui: u64,
2265) -> Result<ExecutionResults, ExecutionError> {
2266 for id in by_value_shared_objects {
2271 if let Some(obj) = written_objects.get(id) {
2274 if !obj.is_shared() {
2275 return Err(ExecutionError::new(
2276 ExecutionErrorKind::SharedObjectOperationNotAllowed,
2277 Some(
2278 format!(
2279 "Shared object operation on {} not allowed: \
2280 cannot be frozen, transferred, or wrapped",
2281 id
2282 )
2283 .into(),
2284 ),
2285 ));
2286 }
2287 } else {
2288 if !deleted_object_ids.contains(id) {
2291 return Err(ExecutionError::new(
2292 ExecutionErrorKind::SharedObjectOperationNotAllowed,
2293 Some(
2294 format!(
2295 "Shared object operation on {} not allowed: \
2296 shared objects used by value must be re-shared if not deleted",
2297 id
2298 )
2299 .into(),
2300 ),
2301 ));
2302 }
2303 }
2304 }
2305
2306 for (id, original_owner) in consensus_owner_objects {
2308 let Owner::ConsensusAddressOwner { owner, .. } = original_owner else {
2309 panic!(
2310 "verified before adding to `consensus_owner_objects` that these are ConsensusAddressOwner"
2311 );
2312 };
2313 if tx_context.sender() != *owner {
2316 debug_fatal!(
2317 "transaction with a singly owned input object where the tx sender is not the owner should never be executed"
2318 );
2319 return Err(ExecutionError::new(
2320 ExecutionErrorKind::SharedObjectOperationNotAllowed,
2321 Some(
2322 format!(
2323 "Shared object operation on {} not allowed: \
2324 transaction with singly owned input object must be sent by the owner",
2325 id
2326 )
2327 .into(),
2328 ),
2329 ));
2330 }
2331 }
2339
2340 let user_events: Vec<Event> = user_events
2341 .into_iter()
2342 .map(|(module_id, tag, contents)| {
2343 Event::new(
2344 module_id.address(),
2345 module_id.name(),
2346 tx_context.sender(),
2347 tag,
2348 contents,
2349 )
2350 })
2351 .collect();
2352
2353 let mut receiving_funds_type_and_owners = BTreeMap::new();
2354 let accumulator_events = accumulator_events
2355 .into_iter()
2356 .map(|accum_event| {
2357 if let Some(ty) = Balance::maybe_get_balance_type_param(&accum_event.target_ty) {
2358 receiving_funds_type_and_owners
2359 .entry(ty)
2360 .or_insert_with(BTreeSet::new)
2361 .insert(accum_event.target_addr.into());
2362 }
2363 let value = match accum_event.value {
2364 MoveAccumulatorValue::U64(amount) => AccumulatorValue::Integer(amount),
2365 MoveAccumulatorValue::EventRef(event_idx) => {
2366 let Some(event) = user_events.get(checked_as!(event_idx, usize)?) else {
2367 invariant_violation!(
2368 "Could not find authenticated event at index {}",
2369 event_idx
2370 );
2371 };
2372 let digest = event.digest();
2373 AccumulatorValue::EventDigest(nonempty![(event_idx, digest)])
2374 }
2375 };
2376
2377 let address =
2378 AccumulatorAddress::new(accum_event.target_addr.into(), accum_event.target_ty);
2379
2380 let write = AccumulatorWriteV1 {
2381 address,
2382 operation: accum_event.action.into_sui_accumulator_action(),
2383 value,
2384 };
2385
2386 Ok(AccumulatorEvent::new(
2387 AccumulatorObjId::new_unchecked(accum_event.accumulator_id),
2388 write,
2389 ))
2390 })
2391 .collect::<Result<Vec<_>, ExecutionError>>()?;
2392
2393 for object in written_objects.values() {
2395 let coin_type = object.type_().and_then(|ty| ty.coin_type_maybe());
2396 let owner = object.owner.get_owner_address();
2397 if let (Some(ty), Ok(owner)) = (coin_type, owner) {
2398 receiving_funds_type_and_owners
2399 .entry(ty)
2400 .or_insert_with(BTreeSet::new)
2401 .insert(owner);
2402 }
2403 }
2404 let DenyListResult {
2405 result,
2406 num_non_gas_coin_owners,
2407 } = state_view.check_coin_deny_list(receiving_funds_type_and_owners);
2408 gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
2409 result?;
2410
2411 let created_object_ids: BTreeSet<ObjectID> = created_object_ids.into_iter().collect();
2412 let deleted_object_ids: BTreeSet<ObjectID> = deleted_object_ids.into_iter().collect();
2413 let modified_objects: BTreeSet<ObjectID> = loaded_runtime_objects
2414 .into_iter()
2415 .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
2416 .collect();
2417
2418 assert_invariant!(
2419 created_object_ids.is_disjoint(&deleted_object_ids),
2420 "Created and deleted object sets should be disjoint"
2421 );
2422 assert_invariant!(
2423 modified_objects.is_disjoint(&created_object_ids),
2424 "Modified and created object sets should be disjoint"
2425 );
2426 assert_invariant!(
2427 written_objects
2428 .keys()
2429 .all(|id| !deleted_object_ids.contains(id)),
2430 "Written objects should not be deleted"
2431 );
2432 Ok(ExecutionResults::V2(ExecutionResultsV2 {
2433 written_objects,
2434 modified_objects,
2435 created_object_ids,
2436 deleted_object_ids,
2437 user_events,
2438 accumulator_events,
2439 settlement_input_sui,
2440 settlement_output_sui,
2441 }))
2442}
2443
2444pub fn fetch_package(
2445 state_view: &impl BackingPackageStore,
2446 package_id: &ObjectID,
2447) -> Result<PackageObject, ExecutionError> {
2448 let mut fetched_packages = fetch_packages(state_view, vec![package_id])?;
2449 assert_invariant!(
2450 fetched_packages.len() == 1,
2451 "Number of fetched packages must match the number of package object IDs if successful."
2452 );
2453 match fetched_packages.pop() {
2454 Some(pkg) => Ok(pkg),
2455 None => invariant_violation!(
2456 "We should always fetch a package for each object or return a dependency error."
2457 ),
2458 }
2459}
2460
2461pub fn fetch_packages<'ctx, 'state>(
2462 state_view: &'state impl BackingPackageStore,
2463 package_ids: impl IntoIterator<Item = &'ctx ObjectID>,
2464) -> Result<Vec<PackageObject>, ExecutionError> {
2465 let package_ids: BTreeSet<_> = package_ids.into_iter().collect();
2466 match get_package_objects(state_view, package_ids) {
2467 Err(e) => Err(ExecutionError::new_with_source(
2468 ExecutionErrorKind::PublishUpgradeMissingDependency,
2469 e,
2470 )),
2471 Ok(Err(missing_deps)) => {
2472 let msg = format!(
2473 "Missing dependencies: {}",
2474 missing_deps
2475 .into_iter()
2476 .map(|dep| format!("{}", dep))
2477 .collect::<Vec<_>>()
2478 .join(", ")
2479 );
2480 Err(ExecutionError::new_with_source(
2481 ExecutionErrorKind::PublishUpgradeMissingDependency,
2482 msg,
2483 ))
2484 }
2485 Ok(Ok(pkgs)) => Ok(pkgs),
2486 }
2487}
2488
2489pub fn check_compatibility(
2490 protocol_config: &ProtocolConfig,
2491 existing_package: &MovePackage,
2492 upgrading_modules: &[CompiledModule],
2493 policy: u8,
2494) -> Result<(), ExecutionError> {
2495 let Ok(policy) = UpgradePolicy::try_from(policy) else {
2497 return Err(ExecutionError::from_kind(
2498 ExecutionErrorKind::PackageUpgradeError {
2499 upgrade_error: PackageUpgradeError::UnknownUpgradePolicy { policy },
2500 },
2501 ));
2502 };
2503
2504 let pool = &mut normalized::RcPool::new();
2505 let binary_config = protocol_config.binary_config(None);
2506 let Ok(current_normalized) =
2507 existing_package.normalize(pool, &binary_config, true)
2508 else {
2509 invariant_violation!("Tried to normalize modules in existing package but failed")
2510 };
2511
2512 let existing_modules_len = current_normalized.len();
2513 let upgrading_modules_len = upgrading_modules.len();
2514 let disallow_new_modules = policy as u8 == UpgradePolicy::DEP_ONLY;
2515
2516 if disallow_new_modules && existing_modules_len != upgrading_modules_len {
2517 return Err(ExecutionError::new_with_source(
2518 ExecutionErrorKind::PackageUpgradeError {
2519 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2520 },
2521 format!(
2522 "Existing package has {existing_modules_len} modules, but new package has \
2523 {upgrading_modules_len}. Adding or removing a module to a deps only package is not allowed."
2524 ),
2525 ));
2526 }
2527
2528 let mut new_normalized = normalize_deserialized_modules(
2529 pool,
2530 upgrading_modules.iter(),
2531 true,
2532 );
2533 for (name, cur_module) in current_normalized {
2534 let Some(new_module) = new_normalized.remove(&name) else {
2535 return Err(ExecutionError::new_with_source(
2536 ExecutionErrorKind::PackageUpgradeError {
2537 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2538 },
2539 format!("Existing module {name} not found in next version of package"),
2540 ));
2541 };
2542
2543 check_module_compatibility(&policy, &cur_module, &new_module)?;
2544 }
2545
2546 debug_assert!(!disallow_new_modules || new_normalized.is_empty());
2548
2549 Ok(())
2550}
2551
2552fn check_module_compatibility(
2553 policy: &UpgradePolicy,
2554 cur_module: &move_binary_format::compatibility::Module,
2555 new_module: &move_binary_format::compatibility::Module,
2556) -> Result<(), ExecutionError> {
2557 match policy {
2558 UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module),
2559 UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module),
2560 UpgradePolicy::Compatible => {
2561 let compatibility = Compatibility::upgrade_check();
2562
2563 compatibility.check(cur_module, new_module)
2564 }
2565 }
2566 .map_err(|e| {
2567 ExecutionError::new_with_source(
2568 ExecutionErrorKind::PackageUpgradeError {
2569 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2570 },
2571 e,
2572 )
2573 })
2574}
2575
2576fn assert_expected_move_object_type(
2579 actual: &Type,
2580 expected: &MoveObjectType,
2581) -> Result<(), ExecutionError> {
2582 let Type::Datatype(actual) = actual else {
2583 invariant_violation!("Expected a datatype for a Move object");
2584 };
2585 let (a, m, n) = actual.qualified_ident();
2586 assert_invariant!(
2587 a == &expected.address(),
2588 "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
2589 );
2590 assert_invariant!(
2591 m == expected.module(),
2592 "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
2593 );
2594 assert_invariant!(
2595 n == expected.name(),
2596 "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
2597 );
2598 let actual_type_arguments = &actual.type_arguments;
2599 let expected_type_arguments = expected.type_params();
2600 assert_invariant!(
2601 actual_type_arguments.len() == expected_type_arguments.len(),
2602 "Actual type arg length does not match expected. \
2603 actual: {actual:?} vs expected: {expected:?}",
2604 );
2605 for (actual_ty, expected_ty) in actual_type_arguments
2606 .iter()
2607 .zip_debug_eq(&expected_type_arguments)
2608 {
2609 assert_expected_type(actual_ty, expected_ty)?;
2610 }
2611 Ok(())
2612}
2613
2614fn assert_expected_type(actual: &Type, expected: &TypeTag) -> Result<(), ExecutionError> {
2617 match (actual, expected) {
2618 (Type::Bool, TypeTag::Bool)
2619 | (Type::U8, TypeTag::U8)
2620 | (Type::U16, TypeTag::U16)
2621 | (Type::U32, TypeTag::U32)
2622 | (Type::U64, TypeTag::U64)
2623 | (Type::U128, TypeTag::U128)
2624 | (Type::U256, TypeTag::U256)
2625 | (Type::Address, TypeTag::Address)
2626 | (Type::Signer, TypeTag::Signer) => Ok(()),
2627 (Type::Vector(inner_actual), TypeTag::Vector(inner_expected)) => {
2628 assert_expected_type(&inner_actual.element_type, inner_expected)
2629 }
2630 (Type::Datatype(actual_dt), TypeTag::Struct(expected_st)) => {
2631 assert_expected_data_type(actual_dt, expected_st)
2632 }
2633 _ => invariant_violation!(
2634 "Type mismatch between actual: {actual:?} and expected: {expected:?}"
2635 ),
2636 }
2637}
2638fn assert_expected_data_type(
2641 actual: &Datatype,
2642 expected: &StructTag,
2643) -> Result<(), ExecutionError> {
2644 let (a, m, n) = actual.qualified_ident();
2645 assert_invariant!(
2646 a == &expected.address,
2647 "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
2648 );
2649 assert_invariant!(
2650 m == expected.module.as_ident_str(),
2651 "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
2652 );
2653 assert_invariant!(
2654 n == expected.name.as_ident_str(),
2655 "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
2656 );
2657 let actual_type_arguments = &actual.type_arguments;
2658 let expected_type_arguments = &expected.type_params;
2659 assert_invariant!(
2660 actual_type_arguments.len() == expected_type_arguments.len(),
2661 "Actual type arg length does not match expected. \
2662 actual: {actual:?} vs expected: {expected:?}",
2663 );
2664 for (actual_ty, expected_ty) in actual_type_arguments
2665 .iter()
2666 .zip_debug_eq(expected_type_arguments)
2667 {
2668 assert_expected_type(actual_ty, expected_ty)?;
2669 }
2670 Ok(())
2671}