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