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