1pub use checked::*;
6
7#[iota_macros::with_checked_arithmetic]
8mod checked {
9 use std::{
10 borrow::Borrow,
11 cell::RefCell,
12 collections::{BTreeMap, BTreeSet, HashMap},
13 rc::Rc,
14 sync::Arc,
15 };
16
17 use indexmap::IndexSet;
18 use iota_move_natives::object_runtime::{
19 self, LoadedRuntimeObject, ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
20 };
21 use iota_protocol_config::ProtocolConfig;
22 use iota_sdk_types::{
23 Address, Argument, CommandArgumentError, Event, MovePackage, MoveStruct, ObjectData,
24 ObjectId, Owner, SharedObjectReference, StructTag, TypeTag,
25 };
26 use iota_types::{
27 balance::Balance,
28 base_types::TxContext,
29 coin::Coin,
30 error::{ExecutionError, ExecutionErrorKind, command_argument_error},
31 execution::{ExecutionResults, ExecutionResultsV1},
32 iota_sdk_types_conversions::{
33 identifier_core_to_sdk, identifier_sdk_to_core, struct_tag_core_to_sdk,
34 type_tag_core_to_sdk,
35 },
36 metrics::LimitsMetrics,
37 move_package::{MovePackageExt, derive_package_metadata_id},
38 object::{MoveStructExt, Object, ObjectInner},
39 storage::DenyListResult,
40 transaction::CallArg,
41 };
42 use move_binary_format::{
43 CompiledModule,
44 errors::{Location, PartialVMError, VMError, VMResult},
45 file_format::{AbilitySet, CodeOffset, FunctionDefinitionIndex, TypeParameterIndex},
46 };
47 use move_core_types::{
48 account_address::AccountAddress, identifier::IdentStr, language_storage::ModuleId,
49 vm_status::StatusCode,
50 };
51 use move_trace_format::format::MoveTraceBuilder;
52 use move_vm_runtime::{
53 move_vm::MoveVM,
54 native_extensions::NativeContextExtensions,
55 session::{LoadedFunctionInstantiation, SerializedReturnValues},
56 };
57 use move_vm_types::loaded_data::runtime_types::Type;
58 use tracing::instrument;
59
60 use crate::{
61 adapter::new_native_extensions,
62 data_store::{
63 PackageStore, cached_data_store::CachedPackageStore, iota_data_store::IotaDataStore,
64 linkage_view::LinkageView,
65 },
66 error::convert_vm_error,
67 execution_mode::ExecutionMode,
68 execution_value::{
69 CommandKind, ExecutionState, InputObjectMetadata, InputValue, ObjectContents,
70 ObjectValue, RawValueType, ResultValue, SizeBound, TryFromValue, UsageKind, Value,
71 },
72 gas_charger::GasCharger,
73 gas_meter::IotaGasMeter,
74 type_resolver::TypeTagResolver,
75 };
76
77 pub struct ExecutionContext<'vm, 'state, 'a> {
79 pub protocol_config: &'a ProtocolConfig,
81 pub metrics: Arc<LimitsMetrics>,
83 pub vm: &'vm MoveVM,
85 pub linkage_view: LinkageView<'state>,
87 pub native_extensions: NativeContextExtensions<'state>,
88 pub state_view: &'state dyn ExecutionState,
90 pub tx_context: Rc<RefCell<TxContext>>,
93 pub gas_charger: &'a mut GasCharger,
95 additional_transfers: Vec<(Owner, ObjectValue)>,
97 new_packages: Vec<MovePackage>,
99 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
101 gas: InputValue,
104 inputs: Vec<InputValue>,
107 results: Vec<Vec<ResultValue>>,
112 borrowed: HashMap<Arg, bool>,
116 }
117
118 struct AdditionalWrite {
121 recipient: Owner,
123 type_: Type,
125 bytes: Vec<u8>,
127 }
128
129 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
130 pub struct Arg(Arg_);
131
132 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
133 enum Arg_ {
134 V1(Argument),
135 V2(NormalizedArg),
136 }
137
138 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
139 enum NormalizedArg {
140 GasCoin,
141 Input(u16),
142 Result(u16, u16),
143 }
144
145 impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
146 #[instrument(name = "ExecutionContext::new", level = "trace", skip_all)]
153 pub fn new(
154 protocol_config: &'a ProtocolConfig,
155 metrics: Arc<LimitsMetrics>,
156 vm: &'vm MoveVM,
157 state_view: &'state dyn ExecutionState,
158 tx_context: Rc<RefCell<TxContext>>,
159 gas_charger: &'a mut GasCharger,
160 inputs: Vec<CallArg>,
161 ) -> Result<Self, ExecutionError>
162 where
163 'a: 'state,
164 {
165 let mut linkage_view = LinkageView::new(Box::new(CachedPackageStore::new(Box::new(
166 state_view.as_iota_resolver(),
167 ))));
168 let mut input_object_map = BTreeMap::new();
169 let inputs = inputs
170 .into_iter()
171 .map(|call_arg| {
172 load_call_arg(
173 vm,
174 state_view,
175 &mut linkage_view,
176 &[],
177 &mut input_object_map,
178 call_arg,
179 )
180 })
181 .collect::<Result<_, ExecutionError>>()?;
182 let gas = if let Some(gas_coin) = gas_charger.gas_coin() {
183 let mut gas = load_object(
184 vm,
185 state_view,
186 &mut linkage_view,
187 &[],
188 &mut input_object_map,
189 false,
191 gas_coin,
192 )?;
193 let Some(Value::Object(ObjectValue {
197 contents: ObjectContents::Coin(coin),
198 ..
199 })) = &mut gas.inner.value
200 else {
201 invariant_violation!("Gas object should be a populated coin")
202 };
203
204 let max_gas_in_balance = gas_charger.gas_budget();
205 let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else {
206 invariant_violation!(
207 "Transaction input checker should check that there is enough gas"
208 );
209 };
210 coin.balance = Balance::new(new_balance);
211 gas
212 } else {
213 InputValue {
214 object_metadata: None,
215 inner: ResultValue {
216 last_usage_kind: None,
217 value: None,
218 },
219 }
220 };
221 let native_extensions = new_native_extensions(
222 state_view.as_child_resolver(),
223 input_object_map,
224 !gas_charger.is_unmetered(),
225 protocol_config,
226 metrics.clone(),
227 tx_context.clone(),
228 state_view.read_auth_context(),
229 );
230
231 #[skip_checked_arithmetic]
233 move_vm_profiler::tracing_feature_enabled! {
234 use move_vm_profiler::GasProfiler;
235 use move_vm_types::gas::GasMeter;
236
237 let ref_context: &RefCell<TxContext> = tx_context.borrow();
238 let tx_digest = ref_context.borrow().digest();
239 let remaining_gas: u64 =
240 move_vm_types::gas::GasMeter::remaining_gas(&IotaGasMeter(gas_charger.move_gas_status_mut()))
241 .into();
242 IotaGasMeter(gas_charger.move_gas_status_mut())
243 .set_profiler(GasProfiler::init(
244 &vm.config().profiler_config,
245 format!("{tx_digest}"),
246 remaining_gas,
247 ));
248 }
249
250 Ok(Self {
251 protocol_config,
252 metrics,
253 vm,
254 linkage_view,
255 native_extensions,
256 state_view,
257 tx_context,
258 gas_charger,
259 gas,
260 inputs,
261 results: vec![],
262 additional_transfers: vec![],
263 new_packages: vec![],
264 user_events: vec![],
265 borrowed: HashMap::new(),
266 })
267 }
268
269 pub fn object_runtime(&self) -> Result<&ObjectRuntime<'_>, ExecutionError> {
270 self.native_extensions
271 .get::<ObjectRuntime>()
272 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
273 }
274
275 pub fn fresh_id(&mut self) -> Result<ObjectId, ExecutionError> {
277 let object_id = self.tx_context.borrow_mut().fresh_id();
278 self.record_new_uid(object_id)?;
279 Ok(object_id)
280 }
281
282 pub(crate) fn record_new_uid(&mut self, object_id: ObjectId) -> Result<(), ExecutionError> {
284 self.native_extensions
285 .get_mut()
286 .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.new_id(object_id))
287 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
288 Ok(())
289 }
290
291 pub(crate) fn package_derived_metadata_id(
293 &mut self,
294 package_storage_id: ObjectId,
295 ) -> Result<ObjectId, ExecutionError> {
296 let object_id = derive_package_metadata_id(package_storage_id);
297 self.record_new_uid(object_id)?;
298 Ok(object_id)
299 }
300
301 pub fn delete_id(&mut self, object_id: ObjectId) -> Result<(), ExecutionError> {
303 self.native_extensions
304 .get_mut()
305 .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.delete_id(object_id))
306 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
307 }
308
309 pub fn set_link_context(
313 &mut self,
314 package_id: ObjectId,
315 ) -> Result<AccountAddress, ExecutionError> {
316 if self.linkage_view.has_linkage(package_id)? {
317 return Ok(self
319 .linkage_view
320 .original_package_id()?
321 .unwrap_or(AccountAddress::new(package_id.into_bytes())));
322 }
323
324 let package = package_for_linkage(&self.linkage_view, package_id)
325 .map_err(|e| self.convert_vm_error(e))?;
326
327 self.linkage_view.set_linkage(&package)
328 }
329
330 pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
332 load_type(self.vm, &self.linkage_view, &self.new_packages, type_tag)
333 }
334
335 pub fn load_type_from_struct(&mut self, struct_tag: &StructTag) -> VMResult<Type> {
337 load_type_from_struct(self.vm, &self.linkage_view, &self.new_packages, struct_tag)
338 }
339
340 pub fn get_type_abilities(&self, t: &Type) -> Result<AbilitySet, ExecutionError> {
341 self.vm
342 .get_runtime()
343 .get_type_abilities(t)
344 .map_err(|e| self.convert_vm_error(e))
345 }
346
347 pub fn take_user_events(
350 &mut self,
351 module_id: &ModuleId,
352 function: FunctionDefinitionIndex,
353 last_offset: CodeOffset,
354 ) -> Result<(), ExecutionError> {
355 let events = self
356 .native_extensions
357 .get_mut()
358 .map(|object_runtime: &mut ObjectRuntime| object_runtime.take_user_events())
359 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
360 let num_events = self.user_events.len() + events.len();
361 let max_events = self.protocol_config.max_num_event_emit();
362 if num_events as u64 > max_events {
363 let err = max_event_error(max_events)
364 .at_code_offset(function, last_offset)
365 .finish(Location::Module(module_id.clone()));
366 return Err(self.convert_vm_error(err));
367 }
368 let new_events = events
369 .into_iter()
370 .map(|(ty, tag, value)| {
371 let layout = self
372 .vm
373 .get_runtime()
374 .type_to_type_layout(&ty)
375 .map_err(|e| self.convert_vm_error(e))?;
376 let Some(bytes) = value.simple_serialize(&layout) else {
377 invariant_violation!("Failed to deserialize already serialized Move value");
378 };
379 Ok((module_id.clone(), struct_tag_core_to_sdk(&tag), bytes))
380 })
381 .collect::<Result<Vec<_>, ExecutionError>>()?;
382 self.user_events.extend(new_events);
383 Ok(())
384 }
385
386 pub fn splat_args<Items: IntoIterator<Item = Argument>>(
392 &self,
393 start_idx: usize,
394 args: Items,
395 ) -> Result<Vec<Arg>, ExecutionError>
396 where
397 Items::IntoIter: ExactSizeIterator,
398 {
399 if !self.protocol_config.normalize_ptb_arguments() {
400 Ok(args.into_iter().map(|arg| Arg(Arg_::V1(arg))).collect())
401 } else {
402 let args = args.into_iter();
403 let _args_len = args.len();
404 let mut res = vec![];
405 for (arg_idx, arg) in args.enumerate() {
406 self.splat_arg(&mut res, arg)
407 .map_err(|e| e.into_execution_error(start_idx + arg_idx))?;
408 }
409 debug_assert_eq!(res.len(), _args_len);
410 Ok(res)
411 }
412 }
413
414 fn splat_arg(&self, res: &mut Vec<Arg>, arg: Argument) -> Result<(), EitherError> {
415 match arg {
416 Argument::Gas => res.push(Arg(Arg_::V2(NormalizedArg::GasCoin))),
417 Argument::Input(i) => {
418 if i as usize >= self.inputs.len() {
419 return Err(CommandArgumentError::IndexOutOfBounds { index: i }.into());
420 }
421 res.push(Arg(Arg_::V2(NormalizedArg::Input(i))))
422 }
423 Argument::NestedResult(i, j) => {
424 let Some(command_result) = self.results.get(i as usize) else {
425 return Err(CommandArgumentError::IndexOutOfBounds { index: i }.into());
426 };
427 if j as usize >= command_result.len() {
428 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
429 result: i,
430 subresult: j,
431 }
432 .into());
433 };
434 res.push(Arg(Arg_::V2(NormalizedArg::Result(i, j))))
435 }
436 Argument::Result(i) => {
437 let Some(result) = self.results.get(i as usize) else {
438 return Err(CommandArgumentError::IndexOutOfBounds { index: i }.into());
439 };
440 let Ok(len): Result<u16, _> = result.len().try_into() else {
441 invariant_violation!("Result of length greater than u16::MAX");
442 };
443 if len != 1 {
444 return Err(CommandArgumentError::InvalidResultArity { result: i }.into());
446 }
447 res.extend((0..len).map(|j| Arg(Arg_::V2(NormalizedArg::Result(i, j)))))
448 }
449 _ => {
450 unimplemented!("a new Argument enum variant was added and needs to be handled")
451 }
452 }
453 Ok(())
454 }
455
456 pub fn one_arg(
457 &self,
458 command_arg_idx: usize,
459 arg: Argument,
460 ) -> Result<Arg, ExecutionError> {
461 let args = self.splat_args(command_arg_idx, vec![arg])?;
462 let Ok([arg]): Result<[Arg; 1], _> = args.try_into() else {
463 return Err(command_argument_error(
464 CommandArgumentError::InvalidArgumentArity,
465 command_arg_idx,
466 ));
467 };
468 Ok(arg)
469 }
470
471 pub(crate) fn add_pure_input(
478 &mut self,
479 bytes: Vec<u8>,
480 ) -> Result<Argument, ExecutionError> {
481 let Ok(index) = u16::try_from(self.inputs.len()) else {
482 invariant_violation!("too many inputs to register an additional pure input");
483 };
484 self.inputs
485 .push(InputValue::new_raw(RawValueType::Any, bytes));
486 Ok(Argument::Input(index))
487 }
488
489 pub(crate) fn num_inputs(&self) -> usize {
493 self.inputs.len()
494 }
495
496 pub(crate) fn truncate_inputs(&mut self, len: usize) {
500 self.inputs.truncate(len);
501 }
502
503 pub fn by_value_arg<V: TryFromValue>(
509 &mut self,
510 command_kind: CommandKind,
511 arg_idx: usize,
512 arg: Arg,
513 ) -> Result<V, ExecutionError> {
514 self.by_value_arg_(command_kind, arg)
515 .map_err(|e| e.into_execution_error(arg_idx))
516 }
517 fn by_value_arg_<V: TryFromValue>(
518 &mut self,
519 command_kind: CommandKind,
520 arg: Arg,
521 ) -> Result<V, EitherError> {
522 let is_borrowed = self.arg_is_borrowed(&arg);
523 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::ByValue)?;
524 let is_copyable = if let Some(val) = val_opt {
525 val.is_copyable()
526 } else {
527 return Err(CommandArgumentError::InvalidValueUsage.into());
528 };
529 if !is_copyable && is_borrowed {
535 return Err(CommandArgumentError::InvalidValueUsage.into());
536 }
537 if arg.is_gas_coin() && !matches!(command_kind, CommandKind::TransferObjects) {
539 return Err(CommandArgumentError::InvalidGasCoinUsage.into());
540 }
541 if matches!(
543 input_metadata_opt,
544 Some(InputObjectMetadata::InputObject {
545 owner: Owner::Immutable,
546 ..
547 })
548 ) {
549 return Err(CommandArgumentError::InvalidObjectByValue.into());
550 }
551
552 if matches!(
554 input_metadata_opt,
555 Some(InputObjectMetadata::InputObject {
556 is_mutable_input: false,
557 ..
558 })
559 ) {
560 return Err(CommandArgumentError::InvalidObjectByValue.into());
561 }
562
563 let val = if is_copyable {
564 val_opt.as_ref().unwrap().clone()
565 } else {
566 val_opt.take().unwrap()
567 };
568 Ok(V::try_from_value(val)?)
569 }
570
571 pub fn borrow_arg_mut<V: TryFromValue>(
578 &mut self,
579 arg_idx: usize,
580 arg: Arg,
581 ) -> Result<V, ExecutionError> {
582 self.borrow_arg_mut_(arg)
583 .map_err(|e| e.into_execution_error(arg_idx))
584 }
585 fn borrow_arg_mut_<V: TryFromValue>(&mut self, arg: Arg) -> Result<V, EitherError> {
586 if self.arg_is_borrowed(&arg) {
588 return Err(CommandArgumentError::InvalidValueUsage.into());
589 }
590 self.borrowed.insert(arg, true);
591 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowMut)?;
592 let is_copyable = if let Some(val) = val_opt {
593 val.is_copyable()
594 } else {
595 return Err(CommandArgumentError::InvalidValueUsage.into());
597 };
598 if let Some(InputObjectMetadata::InputObject {
599 is_mutable_input: false,
600 ..
601 }) = input_metadata_opt
602 {
603 return Err(CommandArgumentError::InvalidObjectByMutRef.into());
604 }
605 let val = if is_copyable {
608 val_opt.as_ref().unwrap().clone()
609 } else {
610 val_opt.take().unwrap()
611 };
612 Ok(V::try_from_value(val)?)
613 }
614
615 pub fn borrow_arg<V: TryFromValue>(
620 &mut self,
621 arg_idx: usize,
622 arg: Arg,
623 type_: &Type,
624 ) -> Result<V, ExecutionError> {
625 self.borrow_arg_(arg, type_)
626 .map_err(|e| e.into_execution_error(arg_idx))
627 }
628 fn borrow_arg_<V: TryFromValue>(
629 &mut self,
630 arg: Arg,
631 arg_type: &Type,
632 ) -> Result<V, EitherError> {
633 if self.arg_is_mut_borrowed(&arg) {
637 return Err(CommandArgumentError::InvalidValueUsage.into());
638 }
639 self.borrowed.insert(arg, false);
640 let (_input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowImm)?;
641 if val_opt.is_none() {
642 return Err(CommandArgumentError::InvalidValueUsage.into());
643 }
644
645 if let &mut Some(Value::Receiving(_, _, ref mut recv_arg_type @ None)) = val_opt {
647 let Type::Reference(inner) = arg_type else {
648 return Err(CommandArgumentError::InvalidValueUsage.into());
649 };
650 *recv_arg_type = Some(*(*inner).clone());
651 }
652
653 Ok(V::try_from_value(val_opt.as_ref().unwrap().clone())?)
654 }
655
656 pub fn restore_arg<Mode: ExecutionMode>(
658 &mut self,
659 updates: &mut Mode::ArgumentUpdates,
660 arg: Arg,
661 value: Value,
662 ) -> Result<(), ExecutionError> {
663 Mode::add_argument_update(self, updates, arg.into(), &value)?;
664 let was_mut_opt = self.borrowed.remove(&arg);
665 assert_invariant!(
666 was_mut_opt.is_some() && was_mut_opt.unwrap(),
667 "Should never restore a non-mut borrowed value. \
668 The take+restore is an implementation detail of mutable references"
669 );
670 let Ok((_, value_opt)) = self.borrow_mut_impl(arg, None) else {
672 invariant_violation!("Should be able to borrow argument to restore it")
673 };
674
675 let old_value = value_opt.replace(value);
676 assert_invariant!(
677 old_value.is_none() || old_value.unwrap().is_copyable(),
678 "Should never restore a non-taken value, unless it is copyable. \
679 The take+restore is an implementation detail of mutable references"
680 );
681
682 Ok(())
683 }
684
685 pub fn transfer_object(
687 &mut self,
688 obj: ObjectValue,
689 addr: Address,
690 ) -> Result<(), ExecutionError> {
691 self.additional_transfers.push((Owner::Address(addr), obj));
692 Ok(())
693 }
694
695 pub fn freeze_object(&mut self, obj: ObjectValue) -> Result<(), ExecutionError> {
697 self.additional_transfers.push((Owner::Immutable, obj));
698 Ok(())
699 }
700
701 pub fn new_package<'p>(
703 &self,
704 modules: &[CompiledModule],
705 dependencies: impl IntoIterator<Item = &'p MovePackage>,
706 ) -> Result<MovePackage, ExecutionError> {
707 MovePackage::new_initial(modules, self.protocol_config, dependencies)
708 }
709
710 pub fn upgrade_package<'p>(
713 &self,
714 storage_id: ObjectId,
715 previous_package: &MovePackage,
716 new_modules: &[CompiledModule],
717 dependencies: impl IntoIterator<Item = &'p MovePackage>,
718 ) -> Result<MovePackage, ExecutionError> {
719 previous_package.new_upgraded(
720 storage_id,
721 new_modules,
722 self.protocol_config,
723 dependencies,
724 )
725 }
726
727 pub fn write_package(&mut self, package: MovePackage) {
729 self.new_packages.push(package);
730 }
731
732 pub fn pop_package(&mut self) -> Option<MovePackage> {
738 self.new_packages.pop()
739 }
740
741 pub fn push_command_results(&mut self, results: Vec<Value>) -> Result<(), ExecutionError> {
744 assert_invariant!(
745 self.borrowed.values().all(|is_mut| !is_mut),
746 "all mut borrows should be restored"
747 );
748 self.borrowed = HashMap::new();
750 self.results
751 .push(results.into_iter().map(ResultValue::new).collect());
752 Ok(())
753 }
754
755 pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
757 let Self {
758 protocol_config,
759 vm,
760 linkage_view,
761 mut native_extensions,
762 tx_context,
763 gas_charger,
764 additional_transfers,
765 new_packages,
766 gas,
767 inputs,
768 results,
769 user_events,
770 state_view,
771 ..
772 } = self;
773 let ref_context: &RefCell<TxContext> = tx_context.borrow();
774 let tx_digest = ref_context.borrow().digest();
775
776 let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
777 let mut loaded_runtime_objects = BTreeMap::new();
778 let mut additional_writes = BTreeMap::new();
779 let mut by_value_shared_objects = BTreeSet::new();
780 for input in inputs.into_iter().chain(std::iter::once(gas)) {
781 let InputValue {
782 object_metadata:
783 Some(InputObjectMetadata::InputObject {
784 is_mutable_input: true,
786 id,
787 version,
788 owner,
789 }),
790 inner: ResultValue { value, .. },
791 } = input
792 else {
793 continue;
794 };
795 loaded_runtime_objects.insert(
796 id,
797 LoadedRuntimeObject {
798 version,
799 is_modified: true,
800 },
801 );
802 if let Some(Value::Object(object_value)) = value {
803 add_additional_write(&mut additional_writes, owner, object_value)?;
804 } else if owner.is_shared() {
805 by_value_shared_objects.insert(id);
806 }
807 }
808 if !Mode::allow_arbitrary_values() {
811 for (i, command_result) in results.iter().enumerate() {
812 for (j, result_value) in command_result.iter().enumerate() {
813 let ResultValue {
814 last_usage_kind,
815 value,
816 } = result_value;
817 match value {
818 None => (),
819 Some(Value::Object(_)) => {
820 return Err(ExecutionErrorKind::UnusedValueWithoutDrop {
821 result: i as u16,
822 subresult: j as u16,
823 }
824 .into());
825 }
826 Some(Value::Raw(RawValueType::Any, _)) => (),
827 Some(Value::Raw(RawValueType::Loaded { abilities, .. }, _)) => {
828 if abilities.has_drop()
834 || (abilities.has_copy()
835 && matches!(last_usage_kind, Some(UsageKind::ByValue)))
836 {
837 } else {
838 let msg = if abilities.has_copy() {
839 "The value has copy, but not drop. \
840 Its last usage must be by-value so it can be taken."
841 } else {
842 "Unused value without drop"
843 };
844 return Err(ExecutionError::new_with_source(
845 ExecutionErrorKind::UnusedValueWithoutDrop {
846 result: i as u16,
847 subresult: j as u16,
848 },
849 msg,
850 ));
851 }
852 }
853 Some(Value::Receiving(_, _, _)) => (),
855 }
856 }
857 }
858 }
859 for (owner, object_value) in additional_transfers {
861 add_additional_write(&mut additional_writes, owner, object_value)?;
862 }
863 if let Some(gas_id) = gas_id_opt {
865 refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
866 }
867
868 let object_runtime: ObjectRuntime = native_extensions
869 .remove()
870 .map_err(|e| convert_vm_error(e.finish(Location::Undefined), vm, &linkage_view))?;
871
872 let RuntimeResults {
873 writes,
874 user_events: remaining_events,
875 loaded_child_objects,
876 mut created_object_ids,
877 deleted_object_ids,
878 } = object_runtime.finish()?;
879 assert_invariant!(
880 remaining_events.is_empty(),
881 "Events should be taken after every Move call"
882 );
883
884 loaded_runtime_objects.extend(loaded_child_objects);
885
886 let mut written_objects = BTreeMap::new();
887 for package in new_packages {
888 let package_obj = Object::new_from_package(package, tx_digest);
889 let id = package_obj.id();
890 created_object_ids.insert(id);
891 written_objects.insert(id, package_obj);
892 }
893 for (id, additional_write) in additional_writes {
894 let AdditionalWrite {
895 recipient,
896 type_,
897 bytes,
898 } = additional_write;
899
900 let move_object = {
901 create_written_object::<Mode>(
902 vm,
903 &linkage_view,
904 protocol_config,
905 &loaded_runtime_objects,
906 id,
907 type_,
908 bytes,
909 )?
910 };
911 let object = Object::new_move(move_object, recipient, tx_digest);
912 written_objects.insert(id, object);
913 if let Some(loaded) = loaded_runtime_objects.get_mut(&id) {
914 loaded.is_modified = true;
915 }
916 }
917
918 for (id, (recipient, ty, value)) in writes {
919 let layout = vm
920 .get_runtime()
921 .type_to_type_layout(&ty)
922 .map_err(|e| convert_vm_error(e, vm, &linkage_view))?;
923 let Some(bytes) = value.simple_serialize(&layout) else {
924 invariant_violation!("Failed to deserialize already serialized Move value");
925 };
926 let move_object = {
927 create_written_object::<Mode>(
928 vm,
929 &linkage_view,
930 protocol_config,
931 &loaded_runtime_objects,
932 id,
933 ty,
934 bytes,
935 )?
936 };
937 let object = Object::new_move(move_object, recipient, tx_digest);
938 written_objects.insert(id, object);
939 }
940
941 let results = finish(
942 protocol_config,
943 state_view,
944 gas_charger,
945 &ref_context.borrow(),
946 &by_value_shared_objects,
947 loaded_runtime_objects,
948 written_objects,
949 created_object_ids,
950 deleted_object_ids,
951 user_events,
952 );
953 results
954 }
955
956 pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
958 crate::error::convert_vm_error(error, self.vm, &self.linkage_view)
959 }
960
961 pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
963 use iota_sdk_types::TypeArgumentError;
964 use move_core_types::vm_status::StatusCode;
965 match error.major_status() {
966 StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
967 ExecutionErrorKind::TypeArityMismatch.into()
968 }
969 StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
970 type_argument: idx as TypeParameterIndex,
971 kind: TypeArgumentError::TypeNotFound,
972 }
973 .into(),
974 StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
975 type_argument: idx as TypeParameterIndex,
976 kind: TypeArgumentError::ConstraintNotSatisfied,
977 }
978 .into(),
979 _ => self.convert_vm_error(error),
980 }
981 }
982
983 fn arg_is_borrowed(&self, arg: &Arg) -> bool {
986 self.borrowed.contains_key(arg)
987 }
988
989 fn arg_is_mut_borrowed(&self, arg: &Arg) -> bool {
992 matches!(self.borrowed.get(arg), Some(true))
993 }
994
995 fn borrow_mut(
998 &mut self,
999 arg: Arg,
1000 usage: UsageKind,
1001 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), EitherError> {
1002 self.borrow_mut_impl(arg, Some(usage))
1003 }
1004
1005 fn borrow_mut_impl(
1008 &mut self,
1009 arg: Arg,
1010 update_last_usage: Option<UsageKind>,
1011 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), EitherError> {
1012 match arg.0 {
1013 Arg_::V1(arg) => {
1014 assert_invariant!(
1015 !self.protocol_config.normalize_ptb_arguments(),
1016 "Should not be using v1 args with normalized args"
1017 );
1018 Ok(self.borrow_mut_impl_v1(arg, update_last_usage)?)
1019 }
1020 Arg_::V2(arg) => {
1021 assert_invariant!(
1022 self.protocol_config.normalize_ptb_arguments(),
1023 "Should be using only v2 args with normalized args"
1024 );
1025 Ok(self.borrow_mut_impl_v2(arg, update_last_usage)?)
1026 }
1027 }
1028 }
1029
1030 fn borrow_mut_impl_v1(
1032 &mut self,
1033 arg: Argument,
1034 update_last_usage: Option<UsageKind>,
1035 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
1036 {
1037 let (metadata, result_value) = match arg {
1038 Argument::Gas => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1039 Argument::Input(i) => {
1040 let Some(input_value) = self.inputs.get_mut(i as usize) else {
1041 return Err(CommandArgumentError::IndexOutOfBounds { index: i });
1042 };
1043 (input_value.object_metadata.as_ref(), &mut input_value.inner)
1044 }
1045 Argument::Result(i) => {
1046 let Some(command_result) = self.results.get_mut(i as usize) else {
1047 return Err(CommandArgumentError::IndexOutOfBounds { index: i });
1048 };
1049 if command_result.len() != 1 {
1050 return Err(CommandArgumentError::InvalidResultArity { result: i });
1051 }
1052 (None, &mut command_result[0])
1053 }
1054 Argument::NestedResult(i, j) => {
1055 let Some(command_result) = self.results.get_mut(i as usize) else {
1056 return Err(CommandArgumentError::IndexOutOfBounds { index: i });
1057 };
1058 let Some(result_value) = command_result.get_mut(j as usize) else {
1059 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
1060 result: i,
1061 subresult: j,
1062 });
1063 };
1064 (None, result_value)
1065 }
1066 _ => {
1067 unimplemented!("a new Argument enum variant was added and needs to be handled")
1068 }
1069 };
1070 if let Some(usage) = update_last_usage {
1071 result_value.last_usage_kind = Some(usage);
1072 }
1073 Ok((metadata, &mut result_value.value))
1074 }
1075
1076 fn borrow_mut_impl_v2(
1078 &mut self,
1079 arg: NormalizedArg,
1080 update_last_usage: Option<UsageKind>,
1081 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), ExecutionError> {
1082 let (metadata, result_value) = match arg {
1083 NormalizedArg::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1084 NormalizedArg::Input(i) => {
1085 let input_value = self
1086 .inputs
1087 .get_mut(i as usize)
1088 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1089 (input_value.object_metadata.as_ref(), &mut input_value.inner)
1090 }
1091 NormalizedArg::Result(i, j) => {
1092 let result_value = self
1093 .results
1094 .get_mut(i as usize)
1095 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?
1096 .get_mut(j as usize)
1097 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1098 (None, result_value)
1099 }
1100 };
1101 if let Some(usage) = update_last_usage {
1102 result_value.last_usage_kind = Some(usage);
1103 }
1104 Ok((metadata, &mut result_value.value))
1105 }
1106
1107 pub(crate) fn execute_function_bypass_visibility(
1112 &mut self,
1113 module: &ModuleId,
1114 function_name: &IdentStr,
1115 ty_args: Vec<Type>,
1116 args: Vec<impl Borrow<[u8]>>,
1117 tracer: &mut Option<MoveTraceBuilder>,
1118 ) -> VMResult<SerializedReturnValues> {
1119 let gas_status = self.gas_charger.move_gas_status_mut();
1120 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1121 self.vm.get_runtime().execute_function_bypass_visibility(
1122 module,
1123 function_name,
1124 ty_args,
1125 args,
1126 &mut data_store,
1127 &mut IotaGasMeter(gas_status),
1128 &mut self.native_extensions,
1129 tracer.as_mut(),
1130 )
1131 }
1132
1133 pub(crate) fn load_function(
1138 &mut self,
1139 module_id: &ModuleId,
1140 function_name: &IdentStr,
1141 type_arguments: &[Type],
1142 ) -> VMResult<LoadedFunctionInstantiation> {
1143 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1144 self.vm.get_runtime().load_function(
1145 module_id,
1146 function_name,
1147 type_arguments,
1148 &mut data_store,
1149 )
1150 }
1151
1152 pub(crate) fn make_object_value(
1157 &mut self,
1158 type_: StructTag,
1159 used_in_non_entry_move_call: bool,
1160 contents: &[u8],
1161 ) -> Result<ObjectValue, ExecutionError> {
1162 make_object_value(
1163 self.vm,
1164 &mut self.linkage_view,
1165 &self.new_packages,
1166 type_,
1167 used_in_non_entry_move_call,
1168 contents,
1169 )
1170 }
1171
1172 pub fn publish_module_bundle(
1177 &mut self,
1178 modules: Vec<Vec<u8>>,
1179 sender: AccountAddress,
1180 ) -> VMResult<()> {
1181 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1184 self.vm.get_runtime().publish_module_bundle(
1185 modules,
1186 sender,
1187 &mut data_store,
1188 &mut IotaGasMeter(self.gas_charger.move_gas_status_mut()),
1189 )
1190 }
1191
1192 pub fn size_bound_raw(&self, bound: u64) -> SizeBound {
1193 if self.protocol_config.max_ptb_value_size_v2() {
1194 SizeBound::Raw(bound)
1195 } else {
1196 SizeBound::Object(bound)
1197 }
1198 }
1199
1200 pub fn size_bound_vector_elem(&self, bound: u64) -> SizeBound {
1201 if self.protocol_config.max_ptb_value_size_v2() {
1202 SizeBound::VectorElem(bound)
1203 } else {
1204 SizeBound::Object(bound)
1205 }
1206 }
1207 }
1208
1209 impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
1210 fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
1213 self.vm
1214 .get_runtime()
1215 .get_type_tag(type_)
1216 .map_err(|e| self.convert_vm_error(e))
1217 .map(|tt| type_tag_core_to_sdk(&tt))
1218 }
1219 }
1220
1221 fn package_for_linkage(
1225 package_store: &dyn PackageStore,
1226 package_id: ObjectId,
1227 ) -> VMResult<Rc<MovePackage>> {
1228 use move_binary_format::errors::PartialVMError;
1229 use move_core_types::vm_status::StatusCode;
1230
1231 match package_store.get_package(&package_id) {
1232 Ok(Some(package)) => Ok(package),
1233 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1234 .with_message(format!("Cannot find link context {package_id} in store"))
1235 .finish(Location::Undefined)),
1236 Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1237 .with_message(format!(
1238 "Error loading link context {package_id} from store: {err}"
1239 ))
1240 .finish(Location::Undefined)),
1241 }
1242 }
1243
1244 pub fn finish(
1250 protocol_config: &ProtocolConfig,
1251 state_view: &dyn ExecutionState,
1252 gas_charger: &mut GasCharger,
1253 tx_context: &TxContext,
1254 by_value_shared_objects: &BTreeSet<ObjectId>,
1255 loaded_runtime_objects: BTreeMap<ObjectId, LoadedRuntimeObject>,
1256 written_objects: BTreeMap<ObjectId, Object>,
1257 created_object_ids: IndexSet<ObjectId>,
1258 deleted_object_ids: IndexSet<ObjectId>,
1259 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
1260 ) -> Result<ExecutionResults, ExecutionError> {
1261 for id in by_value_shared_objects {
1268 if let Some(obj) = written_objects.get(id) {
1271 if !obj.is_shared() {
1272 return Err(ExecutionError::new(
1273 ExecutionErrorKind::SharedObjectOperationNotAllowed,
1274 Some(
1275 format!(
1276 "Shared object operation on {id} not allowed: \
1277 cannot be frozen, transferred, or wrapped"
1278 )
1279 .into(),
1280 ),
1281 ));
1282 }
1283 } else {
1284 if !deleted_object_ids.contains(id) {
1287 return Err(ExecutionError::new(
1288 ExecutionErrorKind::SharedObjectOperationNotAllowed,
1289 Some(
1290 format!(
1291 "Shared object operation on {id} not allowed: \
1292 shared objects used by value must be re-shared if not deleted"
1293 )
1294 .into(),
1295 ),
1296 ));
1297 }
1298 }
1299 }
1300
1301 let DenyListResult {
1302 result,
1303 num_non_gas_coin_owners,
1304 } = state_view.check_coin_deny_list(&written_objects);
1305 gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
1306 result?;
1307
1308 let user_events = user_events
1309 .into_iter()
1310 .map(|(module_id, tag, contents)| {
1311 let package_id = ObjectId::new(module_id.address().into_bytes());
1312 let module = identifier_core_to_sdk(module_id.name());
1313 let sender = tx_context.sender();
1314 Event {
1315 package_id,
1316 module,
1317 sender,
1318 struct_tag: tag,
1319 contents,
1320 }
1321 })
1322 .collect();
1323
1324 Ok(ExecutionResults::V1(ExecutionResultsV1 {
1325 written_objects,
1326 modified_objects: loaded_runtime_objects
1327 .into_iter()
1328 .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
1329 .collect(),
1330 created_object_ids: created_object_ids.into_iter().collect(),
1331 deleted_object_ids: deleted_object_ids.into_iter().collect(),
1332 user_events,
1333 }))
1334 }
1335
1336 pub fn load_type_from_struct(
1337 vm: &MoveVM,
1338 linkage_view: &LinkageView,
1339 new_packages: &[MovePackage],
1340 struct_tag: &StructTag,
1341 ) -> VMResult<Type> {
1342 fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1343 Err(PartialVMError::new(code).finish(Location::Undefined))
1344 }
1345
1346 let defining_id = struct_tag.address().into();
1348 let package = package_for_linkage(linkage_view, defining_id)?;
1349
1350 let original_address = linkage_view.set_linkage(&package).map_err(|e| {
1353 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1354 .with_message(e.to_string())
1355 .finish(Location::Undefined)
1356 })?;
1357
1358 let runtime_id = ModuleId::new(
1359 original_address,
1360 identifier_sdk_to_core(struct_tag.module()),
1361 );
1362 let data_store = IotaDataStore::new(linkage_view, new_packages);
1363 let res = vm.get_runtime().load_type(
1364 &runtime_id,
1365 IdentStr::new(struct_tag.name().as_str()).unwrap(),
1366 &data_store,
1367 );
1368 linkage_view.reset_linkage().map_err(|e| {
1369 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1370 .with_message(e.to_string())
1371 .finish(Location::Undefined)
1372 })?;
1373 let (idx, struct_type) = res?;
1374
1375 let type_param_constraints = struct_type.type_param_constraints();
1377 if type_param_constraints.len() != struct_tag.type_params().len() {
1378 return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1379 }
1380
1381 if struct_tag.type_params().is_empty() {
1382 Ok(Type::Datatype(idx))
1383 } else {
1384 let loaded_type_params = struct_tag
1385 .type_params()
1386 .iter()
1387 .map(|type_param| load_type(vm, linkage_view, new_packages, type_param))
1388 .collect::<VMResult<Vec<_>>>()?;
1389
1390 for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1392 let abilities = vm.get_runtime().get_type_abilities(param)?;
1393 if !constraint.is_subset(abilities) {
1394 return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1395 }
1396 }
1397
1398 Ok(Type::DatatypeInstantiation(Box::new((
1399 idx,
1400 loaded_type_params,
1401 ))))
1402 }
1403 }
1404
1405 pub fn load_type(
1409 vm: &MoveVM,
1410 linkage_view: &LinkageView,
1411 new_packages: &[MovePackage],
1412 type_tag: &TypeTag,
1413 ) -> VMResult<Type> {
1414 Ok(match type_tag {
1415 TypeTag::Bool => Type::Bool,
1416 TypeTag::U8 => Type::U8,
1417 TypeTag::U16 => Type::U16,
1418 TypeTag::U32 => Type::U32,
1419 TypeTag::U64 => Type::U64,
1420 TypeTag::U128 => Type::U128,
1421 TypeTag::U256 => Type::U256,
1422 TypeTag::Address => Type::Address,
1423 TypeTag::Signer => Type::Signer,
1424
1425 TypeTag::Vector(inner) => {
1426 Type::Vector(Box::new(load_type(vm, linkage_view, new_packages, inner)?))
1427 }
1428 TypeTag::Struct(struct_tag) => {
1429 return load_type_from_struct(vm, linkage_view, new_packages, struct_tag);
1430 }
1431 })
1432 }
1433
1434 pub(crate) fn make_object_value(
1441 vm: &MoveVM,
1442 linkage_view: &mut LinkageView,
1443 new_packages: &[MovePackage],
1444 type_: StructTag,
1445 used_in_non_entry_move_call: bool,
1446 contents: &[u8],
1447 ) -> Result<ObjectValue, ExecutionError> {
1448 let contents = if type_.is_coin() {
1449 let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1450 invariant_violation!("Could not deserialize a coin")
1451 };
1452 ObjectContents::Coin(coin)
1453 } else {
1454 ObjectContents::Raw(contents.to_vec())
1455 };
1456
1457 let type_ = load_type_from_struct(vm, linkage_view, new_packages, &type_)
1458 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1459 let abilities = vm
1460 .get_runtime()
1461 .get_type_abilities(&type_)
1462 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1463 let has_public_transfer = abilities.has_store();
1464 Ok(ObjectValue {
1465 type_,
1466 has_public_transfer,
1467 used_in_non_entry_move_call,
1468 contents,
1469 })
1470 }
1471
1472 impl Arg {
1473 fn is_gas_coin(&self) -> bool {
1474 match self {
1476 Arg(Arg_::V1(a)) => matches!(a, Argument::Gas),
1477 Arg(Arg_::V2(n)) => matches!(n, NormalizedArg::GasCoin),
1478 }
1479 }
1480 }
1481
1482 impl From<Arg> for Argument {
1483 fn from(arg: Arg) -> Self {
1484 match arg.0 {
1485 Arg_::V1(a) => a,
1486 Arg_::V2(normalized) => match normalized {
1487 NormalizedArg::GasCoin => Argument::Gas,
1488 NormalizedArg::Input(i) => Argument::Input(i),
1489 NormalizedArg::Result(i, j) => Argument::NestedResult(i, j),
1490 },
1491 }
1492 }
1493 }
1494
1495 pub(crate) fn value_from_object(
1500 vm: &MoveVM,
1501 linkage_view: &mut LinkageView,
1502 new_packages: &[MovePackage],
1503 object: &Object,
1504 ) -> Result<ObjectValue, ExecutionError> {
1505 let ObjectInner {
1506 data: ObjectData::Struct(object),
1507 ..
1508 } = object.as_inner()
1509 else {
1510 invariant_violation!("Expected a Move object");
1511 };
1512
1513 let used_in_non_entry_move_call = false;
1514 make_object_value(
1515 vm,
1516 linkage_view,
1517 new_packages,
1518 object.struct_tag().clone(),
1519 used_in_non_entry_move_call,
1520 object.contents(),
1521 )
1522 }
1523
1524 fn load_object(
1526 vm: &MoveVM,
1527 state_view: &dyn ExecutionState,
1528 linkage_view: &mut LinkageView,
1529 new_packages: &[MovePackage],
1530 input_object_map: &mut BTreeMap<ObjectId, object_runtime::InputObject>,
1531 override_as_immutable: bool,
1532 id: ObjectId,
1533 ) -> Result<InputValue, ExecutionError> {
1534 let Some(obj) = state_view.read_object(&id) else {
1535 invariant_violation!("Object {} does not exist yet", id);
1537 };
1538 assert_invariant!(
1540 !override_as_immutable || matches!(obj.owner, Owner::Shared(_)),
1541 "override_as_immutable should only be set for shared objects"
1542 );
1543 let is_mutable_input = match obj.owner {
1544 Owner::Address(_) => true,
1545 Owner::Shared(_) => !override_as_immutable,
1546 Owner::Immutable => false,
1547 Owner::Object(_) => {
1548 invariant_violation!("Object-owned objects cannot be inputs")
1550 }
1551 _ => unimplemented!("a new Owner enum variant was added and needs to be handled"),
1552 };
1553 let owner = obj.owner;
1554 let version = obj.version();
1555 let object_metadata = InputObjectMetadata::InputObject {
1556 id,
1557 is_mutable_input,
1558 owner,
1559 version,
1560 };
1561 let obj_value = value_from_object(vm, linkage_view, new_packages, obj)?;
1562 let contained_uids = {
1563 let fully_annotated_layout = vm
1564 .get_runtime()
1565 .type_to_fully_annotated_layout(&obj_value.type_)
1566 .map_err(|e| convert_vm_error(e, vm, linkage_view))?;
1567 let mut bytes = vec![];
1568 obj_value.write_bcs_bytes(&mut bytes, None)?;
1569 match get_all_uids(&fully_annotated_layout, &bytes) {
1570 Err(e) => {
1571 invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1572 }
1573 Ok(uids) => uids,
1574 }
1575 };
1576 let runtime_input = object_runtime::InputObject {
1577 contained_uids,
1578 owner,
1579 version,
1580 };
1581 let prev = input_object_map.insert(id, runtime_input);
1582 assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1584 Ok(InputValue::new_object(object_metadata, obj_value))
1585 }
1586
1587 fn load_call_arg(
1589 vm: &MoveVM,
1590 state_view: &dyn ExecutionState,
1591 linkage_view: &mut LinkageView,
1592 new_packages: &[MovePackage],
1593 input_object_map: &mut BTreeMap<ObjectId, object_runtime::InputObject>,
1594 call_arg: CallArg,
1595 ) -> Result<InputValue, ExecutionError> {
1596 Ok(match call_arg {
1597 CallArg::Pure(value) => InputValue::new_raw(RawValueType::Any, value),
1598 other => load_object_arg(
1599 vm,
1600 state_view,
1601 linkage_view,
1602 new_packages,
1603 input_object_map,
1604 other,
1605 )?,
1606 })
1607 }
1608
1609 fn load_object_arg(
1612 vm: &MoveVM,
1613 state_view: &dyn ExecutionState,
1614 linkage_view: &mut LinkageView,
1615 new_packages: &[MovePackage],
1616 input_object_map: &mut BTreeMap<ObjectId, object_runtime::InputObject>,
1617 obj_arg: CallArg,
1618 ) -> Result<InputValue, ExecutionError> {
1619 match obj_arg {
1620 CallArg::ImmutableOrOwned(object_ref) => load_object(
1621 vm,
1622 state_view,
1623 linkage_view,
1624 new_packages,
1625 input_object_map,
1626 false,
1628 object_ref.object_id,
1629 ),
1630 CallArg::Shared(SharedObjectReference {
1631 object_id: id,
1632 mutable,
1633 ..
1634 }) => load_object(
1635 vm,
1636 state_view,
1637 linkage_view,
1638 new_packages,
1639 input_object_map,
1640 !mutable,
1642 id,
1643 ),
1644 CallArg::Receiving(object_ref) => Ok(InputValue::new_receiving_object(
1645 object_ref.object_id,
1646 object_ref.version,
1647 )),
1648 CallArg::Pure(_) => Err(ExecutionError::invariant_violation(
1649 "unexpected pure CallArg in load_object_arg",
1650 )),
1651 _ => Err(ExecutionError::invariant_violation(
1652 "a new CallArg enum variant was added and needs to be handled",
1653 )),
1654 }
1655 }
1656
1657 fn add_additional_write(
1659 additional_writes: &mut BTreeMap<ObjectId, AdditionalWrite>,
1660 owner: Owner,
1661 object_value: ObjectValue,
1662 ) -> Result<(), ExecutionError> {
1663 let ObjectValue {
1664 type_, contents, ..
1665 } = object_value;
1666 let bytes = match contents {
1667 ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
1668 ObjectContents::Raw(bytes) => bytes,
1669 };
1670 let object_id =
1671 ObjectId::from_bytes(bytes.get(..ObjectId::LENGTH).ok_or_else(|| {
1672 ExecutionError::invariant_violation("No id for Raw object bytes")
1673 })?)
1674 .expect("ObjectId::LENGTH bytes is always a valid ObjectId");
1675 let additional_write = AdditionalWrite {
1676 recipient: owner,
1677 type_,
1678 bytes,
1679 };
1680 additional_writes.insert(object_id, additional_write);
1681 Ok(())
1682 }
1683
1684 fn refund_max_gas_budget(
1688 additional_writes: &mut BTreeMap<ObjectId, AdditionalWrite>,
1689 gas_charger: &mut GasCharger,
1690 gas_id: ObjectId,
1691 ) -> Result<(), ExecutionError> {
1692 let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
1693 invariant_violation!("Gas object cannot be wrapped or destroyed")
1694 };
1695 let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
1696 invariant_violation!("Gas object must be a coin")
1697 };
1698 let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
1699 return Err(ExecutionError::new_with_source(
1700 ExecutionErrorKind::CoinBalanceOverflow,
1701 "Gas coin too large after returning the max gas budget",
1702 ));
1703 };
1704 coin.balance = Balance::new(new_balance);
1705 *bytes = coin.to_bcs_bytes();
1706 Ok(())
1707 }
1708
1709 fn create_written_object<Mode: ExecutionMode>(
1711 vm: &MoveVM,
1712 linkage_view: &LinkageView,
1713 protocol_config: &ProtocolConfig,
1714 objects_modified_at: &BTreeMap<ObjectId, LoadedRuntimeObject>,
1715 id: ObjectId,
1716 type_: Type,
1717 contents: Vec<u8>,
1718 ) -> Result<MoveStruct, ExecutionError> {
1719 debug_assert_eq!(
1720 id,
1721 ObjectId::from_bytes(&contents[..ObjectId::LENGTH])
1722 .expect("object contents should start with an id")
1723 );
1724 let old_obj_ver = objects_modified_at
1725 .get(&id)
1726 .map(|obj: &LoadedRuntimeObject| obj.version);
1727
1728 let type_tag = type_tag_core_to_sdk(
1729 &vm.get_runtime()
1730 .get_type_tag(&type_)
1731 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?,
1732 );
1733
1734 let struct_tag = match type_tag {
1735 TypeTag::Struct(inner) => *inner,
1736 _ => invariant_violation!("Non struct type for object"),
1737 };
1738 MoveStruct::new_from_execution(
1739 struct_tag,
1740 old_obj_ver.unwrap_or_default(),
1741 contents,
1742 protocol_config,
1743 Mode::packages_are_predefined(),
1744 )
1745 }
1746
1747 pub enum EitherError {
1748 CommandArgument(CommandArgumentError),
1749 Execution(ExecutionError),
1750 }
1751
1752 impl From<ExecutionError> for EitherError {
1753 fn from(e: ExecutionError) -> Self {
1754 EitherError::Execution(e)
1755 }
1756 }
1757
1758 impl From<CommandArgumentError> for EitherError {
1759 fn from(e: CommandArgumentError) -> Self {
1760 EitherError::CommandArgument(e)
1761 }
1762 }
1763
1764 impl EitherError {
1765 pub fn into_execution_error(self, command_index: usize) -> ExecutionError {
1766 match self {
1767 EitherError::CommandArgument(e) => command_argument_error(e, command_index),
1768 EitherError::Execution(e) => e,
1769 }
1770 }
1771 }
1772}