1use crate::{
5 adapter,
6 data_store::linked_data_store::LinkedDataStore,
7 execution_mode::ExecutionMode,
8 gas_charger::GasCharger,
9 gas_meter::SuiGasMeter,
10 programmable_transactions::{self as legacy_ptb},
11 sp,
12 static_programmable_transactions::{
13 env::Env,
14 execution::{
15 trace_utils,
16 values::{Local, Locals, Value},
17 },
18 linkage::resolved_linkage::{ResolvedLinkage, RootedLinkage},
19 loading::ast::{Datatype, ObjectMutability},
20 typing::ast::{self as T, Type},
21 },
22};
23use indexmap::{IndexMap, IndexSet};
24use move_binary_format::{
25 CompiledModule,
26 errors::{Location, PartialVMError, VMResult},
27 file_format::FunctionDefinitionIndex,
28 file_format_common::VERSION_6,
29};
30use move_core_types::{
31 account_address::AccountAddress,
32 identifier::IdentStr,
33 language_storage::{ModuleId, StructTag},
34};
35use move_trace_format::format::MoveTraceBuilder;
36use move_vm_runtime::native_extensions::NativeContextExtensions;
37use move_vm_types::{
38 gas::{GasMeter, SimpleInstruction},
39 values::{VMValueCast, Value as VMValue},
40};
41use std::{
42 cell::RefCell,
43 collections::{BTreeMap, BTreeSet},
44 rc::Rc,
45 sync::Arc,
46};
47use sui_move_natives::object_runtime::{
48 self, LoadedRuntimeObject, ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
49};
50use sui_types::{
51 TypeTag,
52 base_types::{MoveObjectType, ObjectID, SequenceNumber, TxContext},
53 error::{ExecutionError, ExecutionErrorKind, SafeIndex},
54 execution::ExecutionResults,
55 metrics::LimitsMetrics,
56 move_package::{MovePackage, UpgradeCap, UpgradeReceipt, UpgradeTicket},
57 object::{MoveObject, Object, Owner},
58};
59use sui_verifier::INIT_FN_NAME;
60use tracing::instrument;
61
62macro_rules! unwrap {
63 ($e:expr, $($args:expr),* $(,)?) => {
64 match $e {
65 Some(v) => v,
66 None => {
67 invariant_violation!("Unexpected none: {}", format!($($args),*))
68 }
69 }
70
71 };
72}
73
74macro_rules! object_runtime_mut {
75 ($context:ident) => {{
76 $context
77 .native_extensions
78 .get_mut::<ObjectRuntime>()
79 .map_err(|e| $context.env.convert_vm_error(e.finish(Location::Undefined)))
80 }};
81}
82
83macro_rules! charge_gas_ {
84 ($gas_charger:expr, $env:expr, $call:ident($($args:expr),*)) => {{
85 SuiGasMeter($gas_charger.move_gas_status_mut())
86 .$call($($args),*)
87 .map_err(|e| $env.convert_vm_error(e.finish(Location::Undefined)))
88 }};
89 ($gas_charger:expr, $env:expr, $case:ident, $value_view:expr) => {
90 charge_gas_!($gas_charger, $env, $case($value_view))
91 };
92}
93
94macro_rules! charge_gas {
95 ($context:ident, $case:ident, $value_view:expr) => {{ charge_gas_!($context.gas_charger, $context.env, $case, $value_view) }};
96}
97
98#[derive(Debug)]
100pub struct CtxValue(Value);
101
102#[derive(Clone, Debug)]
103pub struct InputObjectMetadata {
104 pub id: ObjectID,
105 pub mutability: ObjectMutability,
106 pub owner: Owner,
107 pub version: SequenceNumber,
108 pub type_: Type,
109}
110
111#[derive(Copy, Clone)]
112enum UsageKind {
113 Move,
114 Copy,
115 Borrow,
116}
117
118struct Locations {
120 tx_context_value: Locals,
122 gas: Option<(InputObjectMetadata, Locals)>,
124 input_object_metadata: Vec<(T::InputIndex, InputObjectMetadata)>,
126 object_inputs: Locals,
127 input_withdrawal_metadata: Vec<T::WithdrawalInput>,
128 withdrawal_inputs: Locals,
129 pure_input_bytes: IndexSet<Vec<u8>>,
130 pure_input_metadata: Vec<T::PureInput>,
131 pure_inputs: Locals,
132 receiving_input_metadata: Vec<T::ReceivingInput>,
133 receiving_inputs: Locals,
134 results: Vec<Locals>,
138}
139
140enum ResolvedLocation<'a> {
141 Local(Local<'a>),
142 Pure {
143 bytes: &'a [u8],
144 metadata: &'a T::PureInput,
145 local: Local<'a>,
146 },
147 Receiving {
148 metadata: &'a T::ReceivingInput,
149 local: Local<'a>,
150 },
151}
152
153pub struct Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas> {
155 pub env: &'env Env<'pc, 'vm, 'state, 'linkage>,
156 pub metrics: Arc<LimitsMetrics>,
158 pub native_extensions: NativeContextExtensions<'env>,
159 pub tx_context: Rc<RefCell<TxContext>>,
162 pub gas_charger: &'gas mut GasCharger,
164 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
166 locations: Locations,
168}
169
170impl Locations {
171 fn resolve(&mut self, location: T::Location) -> Result<ResolvedLocation<'_>, ExecutionError> {
174 Ok(match location {
175 T::Location::TxContext => ResolvedLocation::Local(self.tx_context_value.local(0)?),
176 T::Location::GasCoin => {
177 let (_, gas_locals) = unwrap!(self.gas.as_mut(), "Gas coin not provided");
178 ResolvedLocation::Local(gas_locals.local(0)?)
179 }
180 T::Location::ObjectInput(i) => ResolvedLocation::Local(self.object_inputs.local(i)?),
181 T::Location::WithdrawalInput(i) => {
182 ResolvedLocation::Local(self.withdrawal_inputs.local(i)?)
183 }
184 T::Location::Result(i, j) => {
185 let result = unwrap!(self.results.get_mut(i as usize), "bounds already verified");
186 ResolvedLocation::Local(result.local(j)?)
187 }
188 T::Location::PureInput(i) => {
189 let local = self.pure_inputs.local(i)?;
190 let metadata = &self.pure_input_metadata.safe_get(i as usize)?;
191 let bytes = self
192 .pure_input_bytes
193 .get_index(metadata.byte_index)
194 .ok_or_else(|| {
195 make_invariant_violation!(
196 "Pure input {} bytes out of bounds at index {}",
197 metadata.original_input_index.0,
198 metadata.byte_index,
199 )
200 })?;
201 ResolvedLocation::Pure {
202 bytes,
203 metadata,
204 local,
205 }
206 }
207 T::Location::ReceivingInput(i) => ResolvedLocation::Receiving {
208 metadata: self.receiving_input_metadata.safe_get(i as usize)?,
209 local: self.receiving_inputs.local(i)?,
210 },
211 })
212 }
213}
214
215impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas> {
216 #[instrument(name = "Context::new", level = "trace", skip_all)]
217 pub fn new(
218 env: &'env Env<'pc, 'vm, 'state, 'linkage>,
219 metrics: Arc<LimitsMetrics>,
220 tx_context: Rc<RefCell<TxContext>>,
221 gas_charger: &'gas mut GasCharger,
222 gas_coin: Option<ObjectID>,
223 pure_input_bytes: IndexSet<Vec<u8>>,
224 object_inputs: Vec<T::ObjectInput>,
225 input_withdrawal_metadata: Vec<T::WithdrawalInput>,
226 pure_input_metadata: Vec<T::PureInput>,
227 receiving_input_metadata: Vec<T::ReceivingInput>,
228 ) -> Result<Self, ExecutionError>
229 where
230 'pc: 'state,
231 {
232 let mut input_object_map = BTreeMap::new();
233 let mut input_object_metadata = Vec::with_capacity(object_inputs.len());
234 let mut object_values = Vec::with_capacity(object_inputs.len());
235 for object_input in object_inputs {
236 let (i, m, v) = load_object_arg(gas_charger, env, &mut input_object_map, object_input)?;
237 input_object_metadata.push((i, m));
238 object_values.push(Some(v));
239 }
240 let object_inputs = Locals::new(object_values)?;
241 let mut withdrawal_values = Vec::with_capacity(input_withdrawal_metadata.len());
242 for withdrawal_input in &input_withdrawal_metadata {
243 let v = load_withdrawal_arg(gas_charger, env, withdrawal_input)?;
244 withdrawal_values.push(Some(v));
245 }
246 let withdrawal_inputs = Locals::new(withdrawal_values)?;
247 let pure_inputs = Locals::new_invalid(pure_input_metadata.len())?;
248 let receiving_inputs = Locals::new_invalid(receiving_input_metadata.len())?;
249 let gas = match gas_coin {
250 Some(gas_coin) => {
251 let ty = env.gas_coin_type()?;
252 let (gas_metadata, gas_value) = load_object_arg_impl(
253 gas_charger,
254 env,
255 &mut input_object_map,
256 gas_coin,
257 ObjectMutability::Mutable,
258 ty,
259 )?;
260 let mut gas_locals = Locals::new([Some(gas_value)])?;
261 let gas_local = gas_locals.local(0)?;
262 let gas_ref = gas_local.borrow()?;
263 let max_gas_in_balance = gas_charger.gas_budget();
265 gas_ref.coin_ref_subtract_balance(max_gas_in_balance)?;
266 Some((gas_metadata, gas_locals))
267 }
268 None => None,
269 };
270 let native_extensions = adapter::new_native_extensions(
271 env.state_view.as_child_resolver(),
272 input_object_map,
273 !gas_charger.is_unmetered(),
274 env.protocol_config,
275 metrics.clone(),
276 tx_context.clone(),
277 );
278
279 debug_assert_eq!(gas_charger.move_gas_status().stack_height_current(), 0);
280 let tx_context_value = Locals::new(vec![Some(Value::new_tx_context(
281 tx_context.borrow().digest(),
282 )?)])?;
283 Ok(Self {
284 env,
285 metrics,
286 native_extensions,
287 tx_context,
288 gas_charger,
289 user_events: vec![],
290 locations: Locations {
291 tx_context_value,
292 gas,
293 input_object_metadata,
294 object_inputs,
295 input_withdrawal_metadata,
296 withdrawal_inputs,
297 pure_input_bytes,
298 pure_input_metadata,
299 pure_inputs,
300 receiving_input_metadata,
301 receiving_inputs,
302 results: vec![],
303 },
304 })
305 }
306
307 pub fn finish<Mode: ExecutionMode>(mut self) -> Result<ExecutionResults, ExecutionError> {
308 assert_invariant!(
309 !self.locations.tx_context_value.local(0)?.is_invalid()?,
310 "tx context value should be present"
311 );
312 let gas = std::mem::take(&mut self.locations.gas);
313 let object_input_metadata = std::mem::take(&mut self.locations.input_object_metadata);
314 let mut object_inputs =
315 std::mem::replace(&mut self.locations.object_inputs, Locals::new_invalid(0)?);
316 let mut loaded_runtime_objects = BTreeMap::new();
317 let mut by_value_shared_objects = BTreeSet::new();
318 let mut consensus_owner_objects = BTreeMap::new();
319 let gas = gas
320 .map(|(m, mut g)| Result::<_, ExecutionError>::Ok((m, g.local(0)?.move_if_valid()?)))
321 .transpose()?;
322 let gas_id_opt = gas.as_ref().map(|(m, _)| m.id);
323 let object_inputs = object_input_metadata
324 .into_iter()
325 .enumerate()
326 .map(|(i, (_, m))| {
327 let v_opt = object_inputs.local(checked_as!(i, u16)?)?.move_if_valid()?;
328 Ok((m, v_opt))
329 })
330 .collect::<Result<Vec<_>, ExecutionError>>()?;
331 for (metadata, value_opt) in object_inputs.into_iter().chain(gas) {
332 let InputObjectMetadata {
333 id,
334 mutability,
335 owner,
336 version,
337 type_,
338 } = metadata;
339 match mutability {
340 ObjectMutability::Immutable => continue,
341 ObjectMutability::NonExclusiveWrite | ObjectMutability::Mutable => (),
345 }
346 loaded_runtime_objects.insert(
347 id,
348 LoadedRuntimeObject {
349 version,
350 is_modified: true,
351 },
352 );
353 if let Some(object) = value_opt {
354 self.transfer_object_(
355 owner,
356 type_,
357 CtxValue(object),
358 true,
359 )?;
360 } else if owner.is_shared() {
361 by_value_shared_objects.insert(id);
362 } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
363 consensus_owner_objects.insert(id, owner.clone());
364 }
365 }
366
367 let Self {
368 env,
369 mut native_extensions,
370 tx_context,
371 gas_charger,
372 user_events,
373 ..
374 } = self;
375 let ref_context: &RefCell<TxContext> = &tx_context;
376 let tx_context: &TxContext = &ref_context.borrow();
377 let tx_digest = ref_context.borrow().digest();
378
379 let object_runtime: ObjectRuntime = native_extensions
380 .remove()
381 .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
382
383 let RuntimeResults {
384 mut writes,
385 user_events: remaining_events,
386 loaded_child_objects,
387 mut created_object_ids,
388 deleted_object_ids,
389 accumulator_events,
390 settlement_input_sui,
391 settlement_output_sui,
392 } = object_runtime.finish()?;
393 assert_invariant!(
394 remaining_events.is_empty(),
395 "Events should be taken after every Move call"
396 );
397 if let Some(gas_id) = gas_id_opt {
399 refund_max_gas_budget(&mut writes, gas_charger, gas_id)?;
400 }
401
402 loaded_runtime_objects.extend(loaded_child_objects);
403
404 let mut written_objects = BTreeMap::new();
405
406 for (id, (recipient, ty, value)) in writes {
407 let (ty, layout) = env.load_type_and_layout_from_struct(&ty.clone().into())?;
408 let abilities = ty.abilities();
409 let has_public_transfer = abilities.has_store();
410 let Some(bytes) = value.typed_serialize(&layout) else {
411 invariant_violation!("Failed to serialize already deserialized Move value");
412 };
413 let move_object = unsafe {
415 create_written_object::<Mode>(
416 env,
417 &loaded_runtime_objects,
418 id,
419 ty,
420 has_public_transfer,
421 bytes,
422 )?
423 };
424 let object = Object::new_move(move_object, recipient, tx_digest);
425 written_objects.insert(id, object);
426 }
427
428 for package in self.env.linkable_store.to_new_packages().into_iter() {
429 let package_obj = Object::new_from_package(package, tx_digest);
430 let id = package_obj.id();
431 created_object_ids.insert(id);
432 written_objects.insert(id, package_obj);
433 }
434
435 legacy_ptb::context::finish(
436 env.protocol_config,
437 env.state_view,
438 gas_charger,
439 tx_context,
440 &by_value_shared_objects,
441 &consensus_owner_objects,
442 loaded_runtime_objects,
443 written_objects,
444 created_object_ids,
445 deleted_object_ids,
446 user_events,
447 accumulator_events,
448 settlement_input_sui,
449 settlement_output_sui,
450 )
451 }
452
453 pub fn object_runtime(&self) -> Result<&ObjectRuntime<'_>, ExecutionError> {
454 self.native_extensions
455 .get::<ObjectRuntime>()
456 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))
457 }
458
459 pub fn take_user_events(
460 &mut self,
461 storage_id: ModuleId,
462 function_def_idx: FunctionDefinitionIndex,
463 instr_length: u16,
464 linkage: &RootedLinkage,
465 ) -> Result<(), ExecutionError> {
466 let events = object_runtime_mut!(self)?.take_user_events();
467 let Some(num_events) = self.user_events.len().checked_add(events.len()) else {
468 invariant_violation!("usize overflow, too many events emitted")
469 };
470 let max_events = self.env.protocol_config.max_num_event_emit();
471 if num_events as u64 > max_events {
472 let err = max_event_error(max_events)
473 .at_code_offset(function_def_idx, instr_length)
474 .finish(Location::Module(storage_id.clone()));
475 return Err(self.env.convert_linked_vm_error(err, linkage));
476 }
477 let new_events = events
478 .into_iter()
479 .map(|(tag, value)| {
480 let layout = self.env.type_layout_for_struct(&tag)?;
481 let Some(bytes) = value.typed_serialize(&layout) else {
482 invariant_violation!("Failed to serialize Move event");
483 };
484 Ok((storage_id.clone(), tag, bytes))
485 })
486 .collect::<Result<Vec<_>, ExecutionError>>()?;
487 self.user_events.extend(new_events);
488 Ok(())
489 }
490
491 fn location(
496 &mut self,
497 usage: UsageKind,
498 location: T::Location,
499 ) -> Result<Value, ExecutionError> {
500 let resolved = self.locations.resolve(location)?;
501 let mut local = match resolved {
502 ResolvedLocation::Local(l) => l,
503 ResolvedLocation::Pure {
504 bytes,
505 metadata,
506 mut local,
507 } => {
508 if local.is_invalid()? {
509 let v = load_pure_value(self.gas_charger, self.env, bytes, metadata)?;
510 local.store(v)?;
511 }
512 local
513 }
514 ResolvedLocation::Receiving {
515 metadata,
516 mut local,
517 } => {
518 if local.is_invalid()? {
519 let v = load_receiving_value(self.gas_charger, self.env, metadata)?;
520 local.store(v)?;
521 }
522 local
523 }
524 };
525 Ok(match usage {
526 UsageKind::Move => {
527 let value = local.move_()?;
528 charge_gas_!(self.gas_charger, self.env, charge_move_loc, &value)?;
529 value
530 }
531 UsageKind::Copy => {
532 let value = local.copy()?;
533 charge_gas_!(self.gas_charger, self.env, charge_copy_loc, &value)?;
534 value
535 }
536 UsageKind::Borrow => {
537 charge_gas_!(
538 self.gas_charger,
539 self.env,
540 charge_simple_instr(SimpleInstruction::MutBorrowLoc)
541 )?;
542 local.borrow()?
543 }
544 })
545 }
546
547 fn location_usage(&mut self, usage: T::Usage) -> Result<Value, ExecutionError> {
548 match usage {
549 T::Usage::Move(location) => self.location(UsageKind::Move, location),
550 T::Usage::Copy { location, .. } => self.location(UsageKind::Copy, location),
551 }
552 }
553
554 fn argument_value(&mut self, sp!(_, (arg_, _)): T::Argument) -> Result<Value, ExecutionError> {
555 match arg_ {
556 T::Argument__::Use(usage) => self.location_usage(usage),
557 T::Argument__::Freeze(usage) => self.location_usage(usage),
559 T::Argument__::Borrow(_, location) => self.location(UsageKind::Borrow, location),
560 T::Argument__::Read(usage) => {
561 let reference = self.location_usage(usage)?;
562 charge_gas!(self, charge_read_ref, &reference)?;
563 reference.read_ref()
564 }
565 }
566 }
567
568 pub fn argument<V>(&mut self, arg: T::Argument) -> Result<V, ExecutionError>
569 where
570 VMValue: VMValueCast<V>,
571 {
572 let before_height = self.gas_charger.move_gas_status().stack_height_current();
573 let value = self.argument_value(arg)?;
574 let after_height = self.gas_charger.move_gas_status().stack_height_current();
575 debug_assert_eq!(before_height.saturating_add(1), after_height);
576 let value: V = value.cast()?;
577 Ok(value)
578 }
579
580 pub fn arguments<V>(&mut self, args: Vec<T::Argument>) -> Result<Vec<V>, ExecutionError>
581 where
582 VMValue: VMValueCast<V>,
583 {
584 args.into_iter().map(|arg| self.argument(arg)).collect()
585 }
586
587 pub fn result(&mut self, result: Vec<Option<CtxValue>>) -> Result<(), ExecutionError> {
588 self.locations
589 .results
590 .push(Locals::new(result.into_iter().map(|v| v.map(|v| v.0)))?);
591 Ok(())
592 }
593
594 pub fn charge_command(
595 &mut self,
596 is_move_call: bool,
597 num_args: usize,
598 num_return: usize,
599 ) -> Result<(), ExecutionError> {
600 let move_gas_status = self.gas_charger.move_gas_status_mut();
601 let before_size = move_gas_status.stack_size_current();
602 let num_popped = if is_move_call {
606 num_args.checked_add(num_return).ok_or_else(|| {
607 make_invariant_violation!("usize overflow when charging gas for command",)
608 })?
609 } else {
610 num_args
611 };
612 move_gas_status
613 .charge(1, 0, num_popped as u64, 0, 1)
614 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
615 let after_size = move_gas_status.stack_size_current();
616 assert_invariant!(
617 before_size == after_size,
618 "We assume currently that the stack size is not decremented. \
619 If this changes, we need to actually account for it here"
620 );
621 Ok(())
622 }
623
624 pub fn copy_value(&mut self, value: &CtxValue) -> Result<CtxValue, ExecutionError> {
625 Ok(CtxValue(copy_value(self.gas_charger, self.env, &value.0)?))
626 }
627
628 pub fn new_coin(&mut self, amount: u64) -> Result<CtxValue, ExecutionError> {
629 let id = self.tx_context.borrow_mut().fresh_id();
630 object_runtime_mut!(self)?
631 .new_id(id)
632 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
633 Ok(CtxValue(Value::coin(id, amount)))
634 }
635
636 pub fn destroy_coin(&mut self, coin: CtxValue) -> Result<u64, ExecutionError> {
637 let (id, amount) = coin.0.unpack_coin()?;
638 object_runtime_mut!(self)?
639 .delete_id(id)
640 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
641 Ok(amount)
642 }
643
644 pub fn new_upgrade_cap(&mut self, storage_id: ObjectID) -> Result<CtxValue, ExecutionError> {
645 let id = self.tx_context.borrow_mut().fresh_id();
646 object_runtime_mut!(self)?
647 .new_id(id)
648 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
649 let cap = UpgradeCap::new(id, storage_id);
650 Ok(CtxValue(Value::upgrade_cap(cap)))
651 }
652
653 pub fn upgrade_receipt(
654 &self,
655 upgrade_ticket: UpgradeTicket,
656 upgraded_package_id: ObjectID,
657 ) -> CtxValue {
658 let receipt = UpgradeReceipt::new(upgrade_ticket, upgraded_package_id);
659 CtxValue(Value::upgrade_receipt(receipt))
660 }
661
662 pub fn vm_move_call(
667 &mut self,
668 function: T::LoadedFunction,
669 args: Vec<CtxValue>,
670 trace_builder_opt: &mut Option<MoveTraceBuilder>,
671 ) -> Result<Vec<CtxValue>, ExecutionError> {
672 let result = self.execute_function_bypass_visibility(
673 &function.runtime_id,
674 &function.name,
675 &function.type_arguments,
676 args,
677 &function.linkage,
678 trace_builder_opt,
679 )?;
680 self.take_user_events(
681 function.storage_id,
682 function.definition_index,
683 function.instruction_length,
684 &function.linkage,
685 )?;
686 Ok(result)
687 }
688
689 pub fn execute_function_bypass_visibility(
690 &mut self,
691 runtime_id: &ModuleId,
692 function_name: &IdentStr,
693 ty_args: &[Type],
694 args: Vec<CtxValue>,
695 linkage: &RootedLinkage,
696 tracer: &mut Option<MoveTraceBuilder>,
697 ) -> Result<Vec<CtxValue>, ExecutionError> {
698 let ty_args = ty_args
699 .iter()
700 .enumerate()
701 .map(|(idx, ty)| self.env.load_vm_type_argument_from_adapter_type(idx, ty))
702 .collect::<Result<_, _>>()?;
703 let gas_status = self.gas_charger.move_gas_status_mut();
704 let mut data_store = LinkedDataStore::new(linkage, self.env.linkable_store);
705 let values = self
706 .env
707 .vm
708 .get_runtime()
709 .execute_function_with_values_bypass_visibility(
710 runtime_id,
711 function_name,
712 ty_args,
713 args.into_iter().map(|v| v.0.into()).collect(),
714 &mut data_store,
715 &mut SuiGasMeter(gas_status),
716 &mut self.native_extensions,
717 tracer.as_mut(),
718 )
719 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
720 Ok(values.into_iter().map(|v| CtxValue(v.into())).collect())
721 }
722
723 pub fn deserialize_modules(
729 &mut self,
730 module_bytes: &[Vec<u8>],
731 is_upgrade: bool,
732 ) -> Result<Vec<CompiledModule>, ExecutionError> {
733 assert_invariant!(
734 !module_bytes.is_empty(),
735 "empty package is checked in transaction input checker"
736 );
737 let total_bytes = module_bytes.iter().map(|v| v.len()).sum();
738 if is_upgrade {
739 self.gas_charger.charge_upgrade_package(total_bytes)?
740 } else {
741 self.gas_charger.charge_publish_package(total_bytes)?
742 }
743
744 let binary_config = self.env.protocol_config.binary_config(None);
745 let modules = module_bytes
746 .iter()
747 .map(|b| {
748 CompiledModule::deserialize_with_config(b, &binary_config)
749 .map_err(|e| e.finish(Location::Undefined))
750 })
751 .collect::<VMResult<Vec<CompiledModule>>>()
752 .map_err(|e| self.env.convert_vm_error(e))?;
753 Ok(modules)
754 }
755
756 fn fetch_package(
757 &mut self,
758 dependency_id: &ObjectID,
759 ) -> Result<Rc<MovePackage>, ExecutionError> {
760 let [fetched_package] = self.fetch_packages(&[*dependency_id])?.try_into().map_err(
761 |_| {
762 make_invariant_violation!(
763 "We should always fetch a single package for each object or return a dependency error."
764 )
765 },
766 )?;
767 Ok(fetched_package)
768 }
769
770 fn fetch_packages(
771 &mut self,
772 dependency_ids: &[ObjectID],
773 ) -> Result<Vec<Rc<MovePackage>>, ExecutionError> {
774 let mut fetched = vec![];
775 let mut missing = vec![];
776
777 let dependency_ids: BTreeSet<_> = dependency_ids.iter().collect();
779
780 for id in &dependency_ids {
781 match self.env.linkable_store.get_package(id) {
782 Err(e) => {
783 return Err(ExecutionError::new_with_source(
784 ExecutionErrorKind::PublishUpgradeMissingDependency,
785 e,
786 ));
787 }
788 Ok(Some(inner)) => {
789 fetched.push(inner);
790 }
791 Ok(None) => {
792 missing.push(*id);
793 }
794 }
795 }
796
797 if missing.is_empty() {
798 assert_invariant!(
799 fetched.len() == dependency_ids.len(),
800 "all dependencies should be fetched"
801 );
802 Ok(fetched)
803 } else {
804 let msg = format!(
805 "Missing dependencies: {}",
806 missing
807 .into_iter()
808 .map(|dep| format!("{}", dep))
809 .collect::<Vec<_>>()
810 .join(", ")
811 );
812 Err(ExecutionError::new_with_source(
813 ExecutionErrorKind::PublishUpgradeMissingDependency,
814 msg,
815 ))
816 }
817 }
818
819 fn publish_and_verify_modules(
820 &mut self,
821 package_id: ObjectID,
822 modules: &[CompiledModule],
823 linkage: &RootedLinkage,
824 ) -> Result<(), ExecutionError> {
825 let binary_version = self.env.protocol_config.move_binary_format_version();
827 let new_module_bytes: Vec<_> = modules
828 .iter()
829 .map(|m| {
830 let mut bytes = Vec::new();
831 let version = if binary_version > VERSION_6 {
832 m.version
833 } else {
834 VERSION_6
835 };
836 m.serialize_with_version(version, &mut bytes).unwrap();
837 bytes
838 })
839 .collect();
840 let mut data_store = LinkedDataStore::new(linkage, self.env.linkable_store);
841 self.env
842 .vm
843 .get_runtime()
844 .publish_module_bundle(
845 new_module_bytes,
846 AccountAddress::from(package_id),
847 &mut data_store,
848 &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
849 )
850 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
851
852 for module in modules {
854 sui_verifier::verifier::sui_verify_module_unmetered(
857 module,
858 &BTreeMap::new(),
859 &self
860 .env
861 .protocol_config
862 .verifier_config(None),
863 )?;
864 }
865
866 Ok(())
867 }
868
869 fn init_modules(
870 &mut self,
871 package_id: ObjectID,
872 modules: &[CompiledModule],
873 linkage: &RootedLinkage,
874 trace_builder_opt: &mut Option<MoveTraceBuilder>,
875 ) -> Result<(), ExecutionError> {
876 for module in modules {
877 let Some((fdef_idx, fdef)) = module.find_function_def_by_name(INIT_FN_NAME.as_str())
878 else {
879 continue;
880 };
881 let fhandle = module.function_handle_at(fdef.function);
882 let fparameters = module.signature_at(fhandle.parameters);
883 assert_invariant!(
884 fparameters.0.len() <= 2,
885 "init function should have at most 2 parameters"
886 );
887 let has_otw = fparameters.0.len() == 2;
888 let tx_context = self
889 .location(UsageKind::Borrow, T::Location::TxContext)
890 .map_err(|e| {
891 make_invariant_violation!("Failed to get tx context for init function: {}", e)
892 })?;
893 charge_gas!(self, charge_store_loc, &tx_context)?;
895
896 let args = if has_otw {
897 vec![CtxValue(Value::one_time_witness()?), CtxValue(tx_context)]
898 } else {
899 vec![CtxValue(tx_context)]
900 };
901 debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
902 trace_utils::trace_move_call_start(trace_builder_opt);
903 let return_values = self.execute_function_bypass_visibility(
904 &module.self_id(),
905 INIT_FN_NAME,
906 &[],
907 args,
908 linkage,
909 trace_builder_opt,
910 )?;
911 trace_utils::trace_move_call_end(trace_builder_opt);
912
913 let storage_id = ModuleId::new(package_id.into(), module.self_id().name().to_owned());
914 self.take_user_events(
915 storage_id,
916 fdef_idx,
917 fdef.code
918 .as_ref()
919 .map(|c| checked_as!(c.code.len(), u16))
920 .transpose()?
921 .unwrap_or(0),
922 linkage,
923 )?;
924 assert_invariant!(
925 return_values.is_empty(),
926 "init should not have return values"
927 );
928 debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
929 }
930
931 Ok(())
932 }
933
934 pub fn publish_and_init_package<Mode: ExecutionMode>(
935 &mut self,
936 mut modules: Vec<CompiledModule>,
937 dep_ids: &[ObjectID],
938 linkage: ResolvedLinkage,
939 trace_builder_opt: &mut Option<MoveTraceBuilder>,
940 ) -> Result<ObjectID, ExecutionError> {
941 let runtime_id = if <Mode>::packages_are_predefined() {
942 (*modules.safe_get(0)?.self_id().address()).into()
944 } else {
945 let id = self.tx_context.borrow_mut().fresh_id();
949 adapter::substitute_package_id(&mut modules, id)?;
950 id
951 };
952
953 let dependencies = self.fetch_packages(dep_ids)?;
954 let package = Rc::new(MovePackage::new_initial(
955 &modules,
956 self.env.protocol_config,
957 dependencies.iter().map(|p| p.as_ref()),
958 )?);
959 let package_id = package.id();
960
961 let linkage = RootedLinkage::new_for_publication(package_id, runtime_id, linkage);
967
968 self.env.linkable_store.push_package(package_id, package)?;
969 let res = self
970 .publish_and_verify_modules(runtime_id, &modules, &linkage)
971 .and_then(|_| self.init_modules(package_id, &modules, &linkage, trace_builder_opt));
972 match res {
973 Ok(()) => Ok(runtime_id),
974 Err(e) => {
975 self.env.linkable_store.pop_package(package_id)?;
976 Err(e)
977 }
978 }
979 }
980
981 pub fn upgrade(
982 &mut self,
983 mut modules: Vec<CompiledModule>,
984 dep_ids: &[ObjectID],
985 current_package_id: ObjectID,
986 upgrade_ticket_policy: u8,
987 linkage: ResolvedLinkage,
988 ) -> Result<ObjectID, ExecutionError> {
989 let current_move_package = self.fetch_package(¤t_package_id)?;
991
992 let runtime_id = current_move_package.original_package_id();
993 adapter::substitute_package_id(&mut modules, runtime_id)?;
994
995 let storage_id = self.tx_context.borrow_mut().fresh_id();
1000
1001 let dependencies = self.fetch_packages(dep_ids)?;
1002 let package = current_move_package.new_upgraded(
1003 storage_id,
1004 &modules,
1005 self.env.protocol_config,
1006 dependencies.iter().map(|p| p.as_ref()),
1007 )?;
1008
1009 let linkage = RootedLinkage::new_for_publication(storage_id, runtime_id, linkage);
1010 self.publish_and_verify_modules(runtime_id, &modules, &linkage)?;
1011
1012 legacy_ptb::execution::check_compatibility(
1013 self.env.protocol_config,
1014 current_move_package.as_ref(),
1015 &modules,
1016 upgrade_ticket_policy,
1017 )?;
1018
1019 if self.env.protocol_config.check_for_init_during_upgrade() {
1020 let current_module_names: BTreeSet<&str> = current_move_package
1023 .serialized_module_map()
1024 .keys()
1025 .map(|s| s.as_str())
1026 .collect();
1027 let upgrade_module_names: BTreeSet<&str> = package
1028 .serialized_module_map()
1029 .keys()
1030 .map(|s| s.as_str())
1031 .collect();
1032 let new_module_names = upgrade_module_names
1033 .difference(¤t_module_names)
1034 .copied()
1035 .collect::<BTreeSet<&str>>();
1036 let new_modules = modules
1037 .iter()
1038 .filter(|m| {
1039 let name = m.identifier_at(m.self_handle().name).as_str();
1040 new_module_names.contains(name)
1041 })
1042 .collect::<Vec<&CompiledModule>>();
1043 let new_module_has_init = new_modules.iter().any(|module| {
1044 module.function_defs.iter().any(|fdef| {
1045 let fhandle = module.function_handle_at(fdef.function);
1046 let fname = module.identifier_at(fhandle.name);
1047 fname == INIT_FN_NAME
1048 })
1049 });
1050 if new_module_has_init {
1051 return Err(ExecutionError::new_with_source(
1053 ExecutionErrorKind::FeatureNotYetSupported,
1054 "`init` in new modules on upgrade is not yet supported",
1055 ));
1056 }
1057 }
1058
1059 self.env
1060 .linkable_store
1061 .push_package(storage_id, Rc::new(package))?;
1062 Ok(storage_id)
1063 }
1064
1065 pub fn transfer_object(
1070 &mut self,
1071 recipient: Owner,
1072 ty: Type,
1073 object: CtxValue,
1074 ) -> Result<(), ExecutionError> {
1075 self.transfer_object_(recipient, ty, object, false)
1076 }
1077
1078 fn transfer_object_(
1079 &mut self,
1080 recipient: Owner,
1081 ty: Type,
1082 object: CtxValue,
1083 end_of_transaction: bool,
1084 ) -> Result<(), ExecutionError> {
1085 let tag = TypeTag::try_from(ty)
1086 .map_err(|_| make_invariant_violation!("Unable to convert Type to TypeTag"))?;
1087 let TypeTag::Struct(tag) = tag else {
1088 invariant_violation!("Expected struct type tag");
1089 };
1090 let ty = MoveObjectType::from(*tag);
1091 object_runtime_mut!(self)?
1092 .transfer(recipient, ty, object.0.into(), end_of_transaction)
1093 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
1094 Ok(())
1095 }
1096
1097 pub fn argument_updates(
1102 &mut self,
1103 args: Vec<T::Argument>,
1104 ) -> Result<Vec<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, ExecutionError> {
1105 args.into_iter()
1106 .filter_map(|arg| self.argument_update(arg).transpose())
1107 .collect()
1108 }
1109
1110 fn argument_update(
1111 &mut self,
1112 sp!(_, (arg, ty)): T::Argument,
1113 ) -> Result<Option<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, ExecutionError> {
1114 use sui_types::transaction::Argument as TxArgument;
1115 let ty = match ty {
1116 Type::Reference(true, inner) => (*inner).clone(),
1117 ty => {
1118 debug_assert!(
1119 false,
1120 "Unexpected non reference type in location update: {ty:?}"
1121 );
1122 return Ok(None);
1123 }
1124 };
1125 let Ok(tag): Result<TypeTag, _> = ty.clone().try_into() else {
1126 invariant_violation!("unable to generate type tag from type")
1127 };
1128 let location = arg.location();
1129 let resolved = self.locations.resolve(location)?;
1130 let local = match resolved {
1131 ResolvedLocation::Local(local)
1132 | ResolvedLocation::Pure { local, .. }
1133 | ResolvedLocation::Receiving { local, .. } => local,
1134 };
1135 if local.is_invalid()? {
1136 return Ok(None);
1137 }
1138 let value = local.copy()?;
1140 let value = match arg {
1141 T::Argument__::Use(_) => {
1142 value.read_ref()?
1144 }
1145 T::Argument__::Borrow(_, _) => {
1146 value
1148 }
1149 T::Argument__::Freeze(_) => {
1150 invariant_violation!("freeze should not be used for a mutable reference")
1151 }
1152 T::Argument__::Read(_) => {
1153 invariant_violation!("read should not return a reference")
1154 }
1155 };
1156 let layout = self.env.runtime_layout(&ty)?;
1157 let Some(bytes) = value.typed_serialize(&layout) else {
1158 invariant_violation!("Failed to serialize Move value");
1159 };
1160 let arg = match location {
1161 T::Location::TxContext => return Ok(None),
1162 T::Location::GasCoin => TxArgument::GasCoin,
1163 T::Location::Result(i, j) => TxArgument::NestedResult(i, j),
1164 T::Location::ObjectInput(i) => TxArgument::Input(
1165 self.locations
1166 .input_object_metadata
1167 .safe_get(i as usize)?
1168 .0
1169 .0,
1170 ),
1171 T::Location::WithdrawalInput(i) => TxArgument::Input(
1172 self.locations
1173 .input_withdrawal_metadata
1174 .safe_get(i as usize)?
1175 .original_input_index
1176 .0,
1177 ),
1178 T::Location::PureInput(i) => TxArgument::Input(
1179 self.locations
1180 .pure_input_metadata
1181 .safe_get(i as usize)?
1182 .original_input_index
1183 .0,
1184 ),
1185 T::Location::ReceivingInput(i) => TxArgument::Input(
1186 self.locations
1187 .receiving_input_metadata
1188 .safe_get(i as usize)?
1189 .original_input_index
1190 .0,
1191 ),
1192 };
1193 Ok(Some((arg, bytes, tag)))
1194 }
1195
1196 pub fn tracked_results(
1197 &self,
1198 results: &[CtxValue],
1199 result_tys: &T::ResultType,
1200 ) -> Result<Vec<(Vec<u8>, TypeTag)>, ExecutionError> {
1201 assert_invariant!(
1202 results.len() == result_tys.len(),
1203 "results and result types should match"
1204 );
1205 results
1206 .iter()
1207 .zip(result_tys)
1208 .map(|(v, ty)| self.tracked_result(&v.0, ty.clone()))
1209 .collect()
1210 }
1211
1212 fn tracked_result(
1213 &self,
1214 result: &Value,
1215 ty: Type,
1216 ) -> Result<(Vec<u8>, TypeTag), ExecutionError> {
1217 let inner_value;
1218 let (v, ty) = match ty {
1219 Type::Reference(_, inner) => {
1220 inner_value = result.copy()?.read_ref()?;
1221 (&inner_value, (*inner).clone())
1222 }
1223 _ => (result, ty),
1224 };
1225 let layout = self.env.runtime_layout(&ty)?;
1226 let Some(bytes) = v.typed_serialize(&layout) else {
1227 invariant_violation!("Failed to serialize Move value");
1228 };
1229 let Ok(tag): Result<TypeTag, _> = ty.try_into() else {
1230 invariant_violation!("unable to generate type tag from type")
1231 };
1232 Ok((bytes, tag))
1233 }
1234}
1235
1236impl VMValueCast<CtxValue> for VMValue {
1237 fn cast(self) -> Result<CtxValue, PartialVMError> {
1238 Ok(CtxValue(self.into()))
1239 }
1240}
1241
1242impl CtxValue {
1243 pub fn vec_pack(ty: Type, values: Vec<CtxValue>) -> Result<CtxValue, ExecutionError> {
1244 Ok(CtxValue(Value::vec_pack(
1245 ty,
1246 values.into_iter().map(|v| v.0).collect(),
1247 )?))
1248 }
1249
1250 pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
1251 self.0.coin_ref_value()
1252 }
1253
1254 pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
1255 self.0.coin_ref_subtract_balance(amount)
1256 }
1257
1258 pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
1259 self.0.coin_ref_add_balance(amount)
1260 }
1261
1262 pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
1263 self.0.into_upgrade_ticket()
1264 }
1265
1266 pub(super) fn inner_for_tracing(&self) -> &Value {
1268 &self.0
1269 }
1270}
1271
1272fn load_object_arg(
1273 meter: &mut GasCharger,
1274 env: &Env,
1275 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1276 input: T::ObjectInput,
1277) -> Result<(T::InputIndex, InputObjectMetadata, Value), ExecutionError> {
1278 let id = input.arg.id();
1279 let mutability = input.arg.mutability();
1280 let (metadata, value) =
1281 load_object_arg_impl(meter, env, input_object_map, id, mutability, input.ty)?;
1282 Ok((input.original_input_index, metadata, value))
1283}
1284
1285fn load_object_arg_impl(
1286 meter: &mut GasCharger,
1287 env: &Env,
1288 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1289 id: ObjectID,
1290 mutability: ObjectMutability,
1291 ty: T::Type,
1292) -> Result<(InputObjectMetadata, Value), ExecutionError> {
1293 let obj = env.read_object(&id)?;
1294 let owner = obj.owner.clone();
1295 let version = obj.version();
1296 let object_metadata = InputObjectMetadata {
1297 id,
1298 mutability,
1299 owner: owner.clone(),
1300 version,
1301 type_: ty.clone(),
1302 };
1303 let sui_types::object::ObjectInner {
1304 data: sui_types::object::Data::Move(move_obj),
1305 ..
1306 } = obj.as_inner()
1307 else {
1308 invariant_violation!("Expected a Move object");
1309 };
1310 assert_expected_move_object_type(&object_metadata.type_, move_obj.type_())?;
1311 let contained_uids = {
1312 let fully_annotated_layout = env.fully_annotated_layout(&ty)?;
1313 get_all_uids(&fully_annotated_layout, move_obj.contents()).map_err(|e| {
1314 make_invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1315 })?
1316 };
1317 input_object_map.insert(
1318 id,
1319 object_runtime::InputObject {
1320 contained_uids,
1321 version,
1322 owner,
1323 },
1324 );
1325
1326 let v = Value::deserialize(env, move_obj.contents(), ty)?;
1327 charge_gas_!(meter, env, charge_copy_loc, &v)?;
1328 charge_gas_!(meter, env, charge_store_loc, &v)?;
1329 Ok((object_metadata, v))
1330}
1331
1332fn load_withdrawal_arg(
1333 meter: &mut GasCharger,
1334 env: &Env,
1335 withdrawal: &T::WithdrawalInput,
1336) -> Result<Value, ExecutionError> {
1337 let T::WithdrawalInput {
1338 original_input_index: _,
1339 ty: _,
1340 owner,
1341 amount,
1342 } = withdrawal;
1343 let loaded = Value::funds_accumulator_withdrawal(*owner, *amount);
1344 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1345 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1346 Ok(loaded)
1347}
1348
1349fn load_pure_value(
1350 meter: &mut GasCharger,
1351 env: &Env,
1352 bytes: &[u8],
1353 metadata: &T::PureInput,
1354) -> Result<Value, ExecutionError> {
1355 let loaded = Value::deserialize(env, bytes, metadata.ty.clone())?;
1356 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1358 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1359 Ok(loaded)
1360}
1361
1362fn load_receiving_value(
1363 meter: &mut GasCharger,
1364 env: &Env,
1365 metadata: &T::ReceivingInput,
1366) -> Result<Value, ExecutionError> {
1367 let (id, version, _) = metadata.object_ref;
1368 let loaded = Value::receiving(id, version);
1369 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1370 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1371 Ok(loaded)
1372}
1373
1374fn copy_value(meter: &mut GasCharger, env: &Env, value: &Value) -> Result<Value, ExecutionError> {
1375 charge_gas_!(meter, env, charge_copy_loc, value)?;
1376 charge_gas_!(meter, env, charge_pop, value)?;
1377 value.copy()
1378}
1379
1380fn refund_max_gas_budget<OType>(
1383 writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1384 gas_charger: &mut GasCharger,
1385 gas_id: ObjectID,
1386) -> Result<(), ExecutionError> {
1387 let Some((_, _, value_ref)) = writes.get_mut(&gas_id) else {
1388 invariant_violation!("Gas object cannot be wrapped or destroyed")
1389 };
1390 let value = std::mem::replace(value_ref, VMValue::u8(0));
1392 let mut locals = Locals::new([Some(value.into())])?;
1393 let mut local = locals.local(0)?;
1394 let coin_value = local.borrow()?.coin_ref_value()?;
1395 let additional = gas_charger.gas_budget();
1396 if coin_value.checked_add(additional).is_none() {
1397 return Err(ExecutionError::new_with_source(
1398 ExecutionErrorKind::CoinBalanceOverflow,
1399 "Gas coin too large after returning the max gas budget",
1400 ));
1401 };
1402 local.borrow()?.coin_ref_add_balance(additional)?;
1403 *value_ref = local.move_()?.into();
1405 Ok(())
1406}
1407
1408unsafe fn create_written_object<Mode: ExecutionMode>(
1414 env: &Env,
1415 objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1416 id: ObjectID,
1417 type_: Type,
1418 has_public_transfer: bool,
1419 contents: Vec<u8>,
1420) -> Result<MoveObject, ExecutionError> {
1421 debug_assert_eq!(
1422 id,
1423 MoveObject::id_opt(&contents).expect("object contents should start with an id")
1424 );
1425 let old_obj_ver = objects_modified_at
1426 .get(&id)
1427 .map(|obj: &LoadedRuntimeObject| obj.version);
1428
1429 let Ok(type_tag): Result<TypeTag, _> = type_.try_into() else {
1430 invariant_violation!("unable to generate type tag from type")
1431 };
1432
1433 let struct_tag = match type_tag {
1434 TypeTag::Struct(inner) => *inner,
1435 _ => invariant_violation!("Non struct type for object"),
1436 };
1437 unsafe {
1438 MoveObject::new_from_execution(
1439 struct_tag.into(),
1440 has_public_transfer,
1441 old_obj_ver.unwrap_or_default(),
1442 contents,
1443 env.protocol_config,
1444 Mode::packages_are_predefined(),
1445 )
1446 }
1447}
1448
1449fn assert_expected_move_object_type(
1452 actual: &Type,
1453 expected: &MoveObjectType,
1454) -> Result<(), ExecutionError> {
1455 let Type::Datatype(actual) = actual else {
1456 invariant_violation!("Expected a datatype for a Move object");
1457 };
1458 let (a, m, n) = actual.qualified_ident();
1459 assert_invariant!(
1460 a == &expected.address(),
1461 "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
1462 );
1463 assert_invariant!(
1464 m == expected.module(),
1465 "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
1466 );
1467 assert_invariant!(
1468 n == expected.name(),
1469 "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
1470 );
1471 let actual_type_arguments = &actual.type_arguments;
1472 let expected_type_arguments = expected.type_params();
1473 assert_invariant!(
1474 actual_type_arguments.len() == expected_type_arguments.len(),
1475 "Actual type arg length does not match expected. \
1476 actual: {actual:?} vs expected: {expected:?}",
1477 );
1478 for (actual_ty, expected_ty) in actual_type_arguments.iter().zip(&expected_type_arguments) {
1479 assert_expected_type(actual_ty, expected_ty)?;
1480 }
1481 Ok(())
1482}
1483
1484fn assert_expected_type(actual: &Type, expected: &TypeTag) -> Result<(), ExecutionError> {
1487 match (actual, expected) {
1488 (Type::Bool, TypeTag::Bool)
1489 | (Type::U8, TypeTag::U8)
1490 | (Type::U16, TypeTag::U16)
1491 | (Type::U32, TypeTag::U32)
1492 | (Type::U64, TypeTag::U64)
1493 | (Type::U128, TypeTag::U128)
1494 | (Type::U256, TypeTag::U256)
1495 | (Type::Address, TypeTag::Address)
1496 | (Type::Signer, TypeTag::Signer) => Ok(()),
1497 (Type::Vector(inner_actual), TypeTag::Vector(inner_expected)) => {
1498 assert_expected_type(&inner_actual.element_type, inner_expected)
1499 }
1500 (Type::Datatype(actual_dt), TypeTag::Struct(expected_st)) => {
1501 assert_expected_data_type(actual_dt, expected_st)
1502 }
1503 _ => invariant_violation!(
1504 "Type mismatch between actual: {actual:?} and expected: {expected:?}"
1505 ),
1506 }
1507}
1508fn assert_expected_data_type(
1511 actual: &Datatype,
1512 expected: &StructTag,
1513) -> Result<(), ExecutionError> {
1514 let (a, m, n) = actual.qualified_ident();
1515 assert_invariant!(
1516 a == &expected.address,
1517 "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
1518 );
1519 assert_invariant!(
1520 m == expected.module.as_ident_str(),
1521 "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
1522 );
1523 assert_invariant!(
1524 n == expected.name.as_ident_str(),
1525 "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
1526 );
1527 let actual_type_arguments = &actual.type_arguments;
1528 let expected_type_arguments = &expected.type_params;
1529 assert_invariant!(
1530 actual_type_arguments.len() == expected_type_arguments.len(),
1531 "Actual type arg length does not match expected. \
1532 actual: {actual:?} vs expected: {expected:?}",
1533 );
1534 for (actual_ty, expected_ty) in actual_type_arguments.iter().zip(expected_type_arguments) {
1535 assert_expected_type(actual_ty, expected_ty)?;
1536 }
1537 Ok(())
1538}