1pub use checked::*;
6
7#[iota_macros::with_checked_arithmetic]
8mod checked {
9 use std::{
10 borrow::Borrow,
11 collections::{BTreeMap, BTreeSet, HashMap},
12 sync::Arc,
13 };
14
15 use iota_move_natives::object_runtime::{
16 self, LoadedRuntimeObject, ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
17 };
18 use iota_protocol_config::ProtocolConfig;
19 use iota_types::{
20 balance::Balance,
21 base_types::{IotaAddress, MoveObjectType, ObjectID, TxContext},
22 coin::Coin,
23 error::{ExecutionError, ExecutionErrorKind, command_argument_error},
24 event::Event,
25 execution::{ExecutionResults, ExecutionResultsV1},
26 execution_status::CommandArgumentError,
27 metrics::LimitsMetrics,
28 move_package::MovePackage,
29 object::{Data, MoveObject, Object, ObjectInner, Owner},
30 storage::{BackingPackageStore, DenyListResult, PackageObject},
31 transaction::{Argument, CallArg, ObjectArg},
32 };
33 use move_binary_format::{
34 CompiledModule,
35 errors::{Location, PartialVMError, PartialVMResult, VMError, VMResult},
36 file_format::{CodeOffset, FunctionDefinitionIndex, TypeParameterIndex},
37 };
38 use move_core_types::{
39 account_address::AccountAddress,
40 identifier::IdentStr,
41 language_storage::{ModuleId, StructTag, TypeTag},
42 resolver::ModuleResolver,
43 vm_status::StatusCode,
44 };
45 use move_trace_format::format::MoveTraceBuilder;
46 use move_vm_runtime::{
47 move_vm::MoveVM,
48 native_extensions::NativeContextExtensions,
49 session::{LoadedFunctionInstantiation, SerializedReturnValues},
50 };
51 use move_vm_types::{data_store::DataStore, loaded_data::runtime_types::Type};
52 use tracing::instrument;
53
54 use crate::{
55 adapter::new_native_extensions,
56 error::convert_vm_error,
57 execution_mode::ExecutionMode,
58 execution_value::{
59 CommandKind, ExecutionState, InputObjectMetadata, InputValue, ObjectContents,
60 ObjectValue, RawValueType, ResultValue, TryFromValue, UsageKind, Value,
61 },
62 gas_charger::GasCharger,
63 gas_meter::IotaGasMeter,
64 programmable_transactions::linkage_view::LinkageView,
65 type_resolver::TypeTagResolver,
66 };
67
68 pub struct ExecutionContext<'vm, 'state, 'a> {
70 pub protocol_config: &'a ProtocolConfig,
72 pub metrics: Arc<LimitsMetrics>,
74 pub vm: &'vm MoveVM,
76 pub linkage_view: LinkageView<'state>,
78 pub native_extensions: NativeContextExtensions<'state>,
79 pub state_view: &'state dyn ExecutionState,
81 pub tx_context: &'a mut TxContext,
84 pub gas_charger: &'a mut GasCharger,
86 additional_transfers: Vec<(IotaAddress, ObjectValue)>,
88 new_packages: Vec<MovePackage>,
90 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
92 gas: InputValue,
95 inputs: Vec<InputValue>,
98 results: Vec<Vec<ResultValue>>,
103 borrowed: HashMap<Arg, bool>,
107 }
108
109 struct AdditionalWrite {
112 recipient: Owner,
114 type_: Type,
116 bytes: Vec<u8>,
118 }
119
120 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
121 pub struct Arg(Arg_);
122
123 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
124 enum Arg_ {
125 V1(Argument),
126 V2(NormalizedArg),
127 }
128
129 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
130 enum NormalizedArg {
131 GasCoin,
132 Input(u16),
133 Result(u16, u16),
134 }
135
136 impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
137 #[instrument(name = "ExecutionContext::new", level = "trace", skip_all)]
144 pub fn new(
145 protocol_config: &'a ProtocolConfig,
146 metrics: Arc<LimitsMetrics>,
147 vm: &'vm MoveVM,
148 state_view: &'state dyn ExecutionState,
149 tx_context: &'a mut TxContext,
150 gas_charger: &'a mut GasCharger,
151 inputs: Vec<CallArg>,
152 ) -> Result<Self, ExecutionError>
153 where
154 'a: 'state,
155 {
156 let mut linkage_view = LinkageView::new(Box::new(state_view.as_iota_resolver()));
157 let mut input_object_map = BTreeMap::new();
158 let inputs = inputs
159 .into_iter()
160 .map(|call_arg| {
161 load_call_arg(
162 vm,
163 state_view,
164 &mut linkage_view,
165 &[],
166 &mut input_object_map,
167 call_arg,
168 )
169 })
170 .collect::<Result<_, ExecutionError>>()?;
171 let gas = if let Some(gas_coin) = gas_charger.gas_coin() {
172 let mut gas = load_object(
173 vm,
174 state_view,
175 &mut linkage_view,
176 &[],
177 &mut input_object_map,
178 false,
180 gas_coin,
181 )?;
182 let Some(Value::Object(ObjectValue {
186 contents: ObjectContents::Coin(coin),
187 ..
188 })) = &mut gas.inner.value
189 else {
190 invariant_violation!("Gas object should be a populated coin")
191 };
192
193 let max_gas_in_balance = gas_charger.gas_budget();
194 let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else {
195 invariant_violation!(
196 "Transaction input checker should check that there is enough gas"
197 );
198 };
199 coin.balance = Balance::new(new_balance);
200 gas
201 } else {
202 InputValue {
203 object_metadata: None,
204 inner: ResultValue {
205 last_usage_kind: None,
206 value: None,
207 },
208 }
209 };
210 let native_extensions = new_native_extensions(
211 state_view.as_child_resolver(),
212 input_object_map,
213 !gas_charger.is_unmetered(),
214 protocol_config,
215 metrics.clone(),
216 tx_context.epoch(),
217 );
218
219 #[skip_checked_arithmetic]
221 move_vm_profiler::tracing_feature_enabled! {
222 use move_vm_profiler::GasProfiler;
223 use move_vm_types::gas::GasMeter;
224
225 let tx_digest = tx_context.digest();
226 let remaining_gas: u64 =
227 move_vm_types::gas::GasMeter::remaining_gas(&IotaGasMeter(gas_charger.move_gas_status_mut()))
228 .into();
229 IotaGasMeter(gas_charger.move_gas_status_mut())
230 .set_profiler(GasProfiler::init(
231 &vm.config().profiler_config,
232 format!("{tx_digest}"),
233 remaining_gas,
234 ));
235 }
236
237 Ok(Self {
238 protocol_config,
239 metrics,
240 vm,
241 linkage_view,
242 native_extensions,
243 state_view,
244 tx_context,
245 gas_charger,
246 gas,
247 inputs,
248 results: vec![],
249 additional_transfers: vec![],
250 new_packages: vec![],
251 user_events: vec![],
252 borrowed: HashMap::new(),
253 })
254 }
255
256 pub fn object_runtime(&self) -> Result<&ObjectRuntime, ExecutionError> {
257 self.native_extensions
258 .get::<ObjectRuntime>()
259 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
260 }
261
262 pub fn fresh_id(&mut self) -> Result<ObjectID, ExecutionError> {
264 let object_id = self.tx_context.fresh_id();
265 self.native_extensions
266 .get_mut()
267 .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.new_id(object_id))
268 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
269 Ok(object_id)
270 }
271
272 pub fn delete_id(&mut self, object_id: ObjectID) -> Result<(), ExecutionError> {
274 self.native_extensions
275 .get_mut()
276 .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.delete_id(object_id))
277 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
278 }
279
280 pub fn set_link_context(
284 &mut self,
285 package_id: ObjectID,
286 ) -> Result<AccountAddress, ExecutionError> {
287 if self.linkage_view.has_linkage(package_id) {
288 return Ok(self
290 .linkage_view
291 .original_package_id()
292 .unwrap_or(*package_id));
293 }
294
295 let package = package_for_linkage(&self.linkage_view, package_id)
296 .map_err(|e| self.convert_vm_error(e))?;
297
298 self.linkage_view.set_linkage(package.move_package())
299 }
300
301 pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
303 load_type(
304 self.vm,
305 &mut self.linkage_view,
306 &self.new_packages,
307 type_tag,
308 )
309 }
310
311 pub fn load_type_from_struct(&mut self, struct_tag: &StructTag) -> VMResult<Type> {
313 load_type_from_struct(
314 self.vm,
315 &mut self.linkage_view,
316 &self.new_packages,
317 struct_tag,
318 )
319 }
320
321 pub fn take_user_events(
324 &mut self,
325 module_id: &ModuleId,
326 function: FunctionDefinitionIndex,
327 last_offset: CodeOffset,
328 ) -> Result<(), ExecutionError> {
329 let events = self
330 .native_extensions
331 .get_mut()
332 .map(|object_runtime: &mut ObjectRuntime| object_runtime.take_user_events())
333 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
334 let num_events = self.user_events.len() + events.len();
335 let max_events = self.protocol_config.max_num_event_emit();
336 if num_events as u64 > max_events {
337 let err = max_event_error(max_events)
338 .at_code_offset(function, last_offset)
339 .finish(Location::Module(module_id.clone()));
340 return Err(self.convert_vm_error(err));
341 }
342 let new_events = events
343 .into_iter()
344 .map(|(ty, tag, value)| {
345 let layout = self
346 .vm
347 .get_runtime()
348 .type_to_type_layout(&ty)
349 .map_err(|e| self.convert_vm_error(e))?;
350 let Some(bytes) = value.simple_serialize(&layout) else {
351 invariant_violation!("Failed to deserialize already serialized Move value");
352 };
353 Ok((module_id.clone(), tag, bytes))
354 })
355 .collect::<Result<Vec<_>, ExecutionError>>()?;
356 self.user_events.extend(new_events);
357 Ok(())
358 }
359
360 pub fn splat_args<Items: IntoIterator<Item = Argument>>(
366 &self,
367 start_idx: usize,
368 args: Items,
369 ) -> Result<Vec<Arg>, ExecutionError>
370 where
371 Items::IntoIter: ExactSizeIterator,
372 {
373 if !self.protocol_config.normalize_ptb_arguments() {
374 Ok(args.into_iter().map(|arg| Arg(Arg_::V1(arg))).collect())
375 } else {
376 let args = args.into_iter();
377 let _args_len = args.len();
378 let mut res = vec![];
379 for (arg_idx, arg) in args.enumerate() {
380 self.splat_arg(&mut res, arg)
381 .map_err(|e| e.into_execution_error(start_idx + arg_idx))?;
382 }
383 debug_assert_eq!(res.len(), _args_len);
384 Ok(res)
385 }
386 }
387
388 fn splat_arg(&self, res: &mut Vec<Arg>, arg: Argument) -> Result<(), EitherError> {
389 match arg {
390 Argument::GasCoin => res.push(Arg(Arg_::V2(NormalizedArg::GasCoin))),
391 Argument::Input(i) => {
392 if i as usize >= self.inputs.len() {
393 return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
394 }
395 res.push(Arg(Arg_::V2(NormalizedArg::Input(i))))
396 }
397 Argument::NestedResult(i, j) => {
398 let Some(command_result) = self.results.get(i as usize) else {
399 return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
400 };
401 if j as usize >= command_result.len() {
402 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
403 result_idx: i,
404 secondary_idx: j,
405 }
406 .into());
407 };
408 res.push(Arg(Arg_::V2(NormalizedArg::Result(i, j))))
409 }
410 Argument::Result(i) => {
411 let Some(result) = self.results.get(i as usize) else {
412 return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
413 };
414 let Ok(len): Result<u16, _> = result.len().try_into() else {
415 invariant_violation!("Result of length greater than u16::MAX");
416 };
417 if len != 1 {
418 return Err(
420 CommandArgumentError::InvalidResultArity { result_idx: i }.into()
421 );
422 }
423 res.extend((0..len).map(|j| Arg(Arg_::V2(NormalizedArg::Result(i, j)))))
424 }
425 }
426 Ok(())
427 }
428
429 pub fn one_arg(
430 &self,
431 command_arg_idx: usize,
432 arg: Argument,
433 ) -> Result<Arg, ExecutionError> {
434 let args = self.splat_args(command_arg_idx, vec![arg])?;
435 let Ok([arg]): Result<[Arg; 1], _> = args.try_into() else {
436 return Err(command_argument_error(
437 CommandArgumentError::InvalidArgumentArity,
438 command_arg_idx,
439 ));
440 };
441 Ok(arg)
442 }
443
444 pub fn by_value_arg<V: TryFromValue>(
450 &mut self,
451 command_kind: CommandKind<'_>,
452 arg_idx: usize,
453 arg: Arg,
454 ) -> Result<V, ExecutionError> {
455 self.by_value_arg_(command_kind, arg)
456 .map_err(|e| e.into_execution_error(arg_idx))
457 }
458 fn by_value_arg_<V: TryFromValue>(
459 &mut self,
460 command_kind: CommandKind<'_>,
461 arg: Arg,
462 ) -> Result<V, EitherError> {
463 let is_borrowed = self.arg_is_borrowed(&arg);
464 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::ByValue)?;
465 let is_copyable = if let Some(val) = val_opt {
466 val.is_copyable()
467 } else {
468 return Err(CommandArgumentError::InvalidValueUsage.into());
469 };
470 if !is_copyable && is_borrowed {
476 return Err(CommandArgumentError::InvalidValueUsage.into());
477 }
478 if arg.is_gas_coin() && !matches!(command_kind, CommandKind::TransferObjects) {
480 return Err(CommandArgumentError::InvalidGasCoinUsage.into());
481 }
482 if matches!(
484 input_metadata_opt,
485 Some(InputObjectMetadata::InputObject {
486 owner: Owner::Immutable,
487 ..
488 })
489 ) {
490 return Err(CommandArgumentError::InvalidObjectByValue.into());
491 }
492
493 if matches!(
495 input_metadata_opt,
496 Some(InputObjectMetadata::InputObject {
497 is_mutable_input: false,
498 ..
499 })
500 ) {
501 return Err(CommandArgumentError::InvalidObjectByValue.into());
502 }
503
504 let val = if is_copyable {
505 val_opt.as_ref().unwrap().clone()
506 } else {
507 val_opt.take().unwrap()
508 };
509 Ok(V::try_from_value(val)?)
510 }
511
512 pub fn borrow_arg_mut<V: TryFromValue>(
519 &mut self,
520 arg_idx: usize,
521 arg: Arg,
522 ) -> Result<V, ExecutionError> {
523 self.borrow_arg_mut_(arg)
524 .map_err(|e| e.into_execution_error(arg_idx))
525 }
526 fn borrow_arg_mut_<V: TryFromValue>(&mut self, arg: Arg) -> Result<V, EitherError> {
527 if self.arg_is_borrowed(&arg) {
529 return Err(CommandArgumentError::InvalidValueUsage.into());
530 }
531 self.borrowed.insert(arg, true);
532 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowMut)?;
533 let is_copyable = if let Some(val) = val_opt {
534 val.is_copyable()
535 } else {
536 return Err(CommandArgumentError::InvalidValueUsage.into());
538 };
539 if let Some(InputObjectMetadata::InputObject {
540 is_mutable_input: false,
541 ..
542 }) = input_metadata_opt
543 {
544 return Err(CommandArgumentError::InvalidObjectByMutRef.into());
545 }
546 let val = if is_copyable {
549 val_opt.as_ref().unwrap().clone()
550 } else {
551 val_opt.take().unwrap()
552 };
553 Ok(V::try_from_value(val)?)
554 }
555
556 pub fn borrow_arg<V: TryFromValue>(
561 &mut self,
562 arg_idx: usize,
563 arg: Arg,
564 type_: &Type,
565 ) -> Result<V, ExecutionError> {
566 self.borrow_arg_(arg, type_)
567 .map_err(|e| e.into_execution_error(arg_idx))
568 }
569 fn borrow_arg_<V: TryFromValue>(
570 &mut self,
571 arg: Arg,
572 arg_type: &Type,
573 ) -> Result<V, EitherError> {
574 if self.arg_is_mut_borrowed(&arg) {
578 return Err(CommandArgumentError::InvalidValueUsage.into());
579 }
580 self.borrowed.insert(arg, false);
581 let (_input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowImm)?;
582 if val_opt.is_none() {
583 return Err(CommandArgumentError::InvalidValueUsage.into());
584 }
585
586 if let &mut Some(Value::Receiving(_, _, ref mut recv_arg_type @ None)) = val_opt {
588 let Type::Reference(inner) = arg_type else {
589 return Err(CommandArgumentError::InvalidValueUsage.into());
590 };
591 *recv_arg_type = Some(*(*inner).clone());
592 }
593
594 Ok(V::try_from_value(val_opt.as_ref().unwrap().clone())?)
595 }
596
597 pub fn restore_arg<Mode: ExecutionMode>(
599 &mut self,
600 updates: &mut Mode::ArgumentUpdates,
601 arg: Arg,
602 value: Value,
603 ) -> Result<(), ExecutionError> {
604 Mode::add_argument_update(self, updates, arg.into(), &value)?;
605 let was_mut_opt = self.borrowed.remove(&arg);
606 assert_invariant!(
607 was_mut_opt.is_some() && was_mut_opt.unwrap(),
608 "Should never restore a non-mut borrowed value. \
609 The take+restore is an implementation detail of mutable references"
610 );
611 let Ok((_, value_opt)) = self.borrow_mut_impl(arg, None) else {
613 invariant_violation!("Should be able to borrow argument to restore it")
614 };
615
616 let old_value = value_opt.replace(value);
617 assert_invariant!(
618 old_value.is_none() || old_value.unwrap().is_copyable(),
619 "Should never restore a non-taken value, unless it is copyable. \
620 The take+restore is an implementation detail of mutable references"
621 );
622
623 Ok(())
624 }
625
626 pub fn transfer_object(
628 &mut self,
629 obj: ObjectValue,
630 addr: IotaAddress,
631 ) -> Result<(), ExecutionError> {
632 self.additional_transfers.push((addr, obj));
633 Ok(())
634 }
635
636 pub fn new_package<'p>(
638 &self,
639 modules: &[CompiledModule],
640 dependencies: impl IntoIterator<Item = &'p MovePackage>,
641 ) -> Result<MovePackage, ExecutionError> {
642 MovePackage::new_initial(modules, self.protocol_config, dependencies)
643 }
644
645 pub fn upgrade_package<'p>(
648 &self,
649 storage_id: ObjectID,
650 previous_package: &MovePackage,
651 new_modules: &[CompiledModule],
652 dependencies: impl IntoIterator<Item = &'p MovePackage>,
653 ) -> Result<MovePackage, ExecutionError> {
654 previous_package.new_upgraded(
655 storage_id,
656 new_modules,
657 self.protocol_config,
658 dependencies,
659 )
660 }
661
662 pub fn write_package(&mut self, package: MovePackage) {
664 self.new_packages.push(package);
665 }
666
667 pub fn pop_package(&mut self) -> Option<MovePackage> {
673 self.new_packages.pop()
674 }
675
676 pub fn push_command_results(&mut self, results: Vec<Value>) -> Result<(), ExecutionError> {
679 assert_invariant!(
680 self.borrowed.values().all(|is_mut| !is_mut),
681 "all mut borrows should be restored"
682 );
683 self.borrowed = HashMap::new();
685 self.results
686 .push(results.into_iter().map(ResultValue::new).collect());
687 Ok(())
688 }
689
690 pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
692 let Self {
693 protocol_config,
694 vm,
695 linkage_view,
696 mut native_extensions,
697 tx_context,
698 gas_charger,
699 additional_transfers,
700 new_packages,
701 gas,
702 inputs,
703 results,
704 user_events,
705 state_view,
706 ..
707 } = self;
708 let tx_digest = tx_context.digest();
709 let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
710 let mut loaded_runtime_objects = BTreeMap::new();
711 let mut additional_writes = BTreeMap::new();
712 let mut by_value_shared_objects = BTreeSet::new();
713 for input in inputs.into_iter().chain(std::iter::once(gas)) {
714 let InputValue {
715 object_metadata:
716 Some(InputObjectMetadata::InputObject {
717 is_mutable_input: true,
719 id,
720 version,
721 owner,
722 }),
723 inner: ResultValue { value, .. },
724 } = input
725 else {
726 continue;
727 };
728 loaded_runtime_objects.insert(
729 id,
730 LoadedRuntimeObject {
731 version,
732 is_modified: true,
733 },
734 );
735 if let Some(Value::Object(object_value)) = value {
736 add_additional_write(&mut additional_writes, owner, object_value)?;
737 } else if owner.is_shared() {
738 by_value_shared_objects.insert(id);
739 }
740 }
741 if !Mode::allow_arbitrary_values() {
744 for (i, command_result) in results.iter().enumerate() {
745 for (j, result_value) in command_result.iter().enumerate() {
746 let ResultValue {
747 last_usage_kind,
748 value,
749 } = result_value;
750 match value {
751 None => (),
752 Some(Value::Object(_)) => {
753 return Err(ExecutionErrorKind::UnusedValueWithoutDrop {
754 result_idx: i as u16,
755 secondary_idx: j as u16,
756 }
757 .into());
758 }
759 Some(Value::Raw(RawValueType::Any, _)) => (),
760 Some(Value::Raw(RawValueType::Loaded { abilities, .. }, _)) => {
761 if abilities.has_drop()
767 || (abilities.has_copy()
768 && matches!(last_usage_kind, Some(UsageKind::ByValue)))
769 {
770 } else {
771 let msg = if abilities.has_copy() {
772 "The value has copy, but not drop. \
773 Its last usage must be by-value so it can be taken."
774 } else {
775 "Unused value without drop"
776 };
777 return Err(ExecutionError::new_with_source(
778 ExecutionErrorKind::UnusedValueWithoutDrop {
779 result_idx: i as u16,
780 secondary_idx: j as u16,
781 },
782 msg,
783 ));
784 }
785 }
786 Some(Value::Receiving(_, _, _)) => (),
788 }
789 }
790 }
791 }
792 for (recipient, object_value) in additional_transfers {
794 let owner = Owner::AddressOwner(recipient);
795 add_additional_write(&mut additional_writes, owner, object_value)?;
796 }
797 if let Some(gas_id) = gas_id_opt {
799 refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
800 }
801
802 let object_runtime: ObjectRuntime = native_extensions
803 .remove()
804 .map_err(|e| convert_vm_error(e.finish(Location::Undefined), vm, &linkage_view))?;
805
806 let RuntimeResults {
807 writes,
808 user_events: remaining_events,
809 loaded_child_objects,
810 mut created_object_ids,
811 deleted_object_ids,
812 } = object_runtime.finish()?;
813 assert_invariant!(
814 remaining_events.is_empty(),
815 "Events should be taken after every Move call"
816 );
817
818 loaded_runtime_objects.extend(loaded_child_objects);
819
820 let mut written_objects = BTreeMap::new();
821 for package in new_packages {
822 let package_obj = Object::new_from_package(package, tx_digest);
823 let id = package_obj.id();
824 created_object_ids.insert(id);
825 written_objects.insert(id, package_obj);
826 }
827 for (id, additional_write) in additional_writes {
828 let AdditionalWrite {
829 recipient,
830 type_,
831 bytes,
832 } = additional_write;
833
834 let move_object = {
835 create_written_object(
836 vm,
837 &linkage_view,
838 protocol_config,
839 &loaded_runtime_objects,
840 id,
841 type_,
842 bytes,
843 )?
844 };
845 let object = Object::new_move(move_object, recipient, tx_digest);
846 written_objects.insert(id, object);
847 if let Some(loaded) = loaded_runtime_objects.get_mut(&id) {
848 loaded.is_modified = true;
849 }
850 }
851
852 for (id, (recipient, ty, value)) in writes {
853 let layout = vm
854 .get_runtime()
855 .type_to_type_layout(&ty)
856 .map_err(|e| convert_vm_error(e, vm, &linkage_view))?;
857 let Some(bytes) = value.simple_serialize(&layout) else {
858 invariant_violation!("Failed to deserialize already serialized Move value");
859 };
860 let move_object = {
861 create_written_object(
862 vm,
863 &linkage_view,
864 protocol_config,
865 &loaded_runtime_objects,
866 id,
867 ty,
868 bytes,
869 )?
870 };
871 let object = Object::new_move(move_object, recipient, tx_digest);
872 written_objects.insert(id, object);
873 }
874
875 for id in &by_value_shared_objects {
882 if let Some(obj) = written_objects.get(id) {
885 if !obj.is_shared() {
886 return Err(ExecutionError::new(
887 ExecutionErrorKind::SharedObjectOperationNotAllowed,
888 Some(
889 format!(
890 "Shared object operation on {id} not allowed: \
891 cannot be frozen, transferred, or wrapped"
892 )
893 .into(),
894 ),
895 ));
896 }
897 } else {
898 if !deleted_object_ids.contains(id) {
901 return Err(ExecutionError::new(
902 ExecutionErrorKind::SharedObjectOperationNotAllowed,
903 Some(
904 format!("Shared object operation on {id} not allowed: \
905 shared objects used by value must be re-shared if not deleted").into(),
906 ),
907 ));
908 }
909 }
910 }
911
912 let DenyListResult {
913 result,
914 num_non_gas_coin_owners,
915 } = state_view.check_coin_deny_list(&written_objects);
916 gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
917 result?;
918
919 let user_events = user_events
920 .into_iter()
921 .map(|(module_id, tag, contents)| {
922 Event::new(
923 module_id.address(),
924 module_id.name(),
925 tx_context.sender(),
926 tag,
927 contents,
928 )
929 })
930 .collect();
931
932 Ok(ExecutionResults::V1(ExecutionResultsV1 {
933 written_objects,
934 modified_objects: loaded_runtime_objects
935 .into_iter()
936 .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
937 .collect(),
938 created_object_ids: created_object_ids.into_iter().collect(),
939 deleted_object_ids: deleted_object_ids.into_iter().collect(),
940 user_events,
941 }))
942 }
943
944 pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
946 crate::error::convert_vm_error(error, self.vm, &self.linkage_view)
947 }
948
949 pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
951 use iota_types::execution_status::TypeArgumentError;
952 use move_core_types::vm_status::StatusCode;
953 match error.major_status() {
954 StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
955 ExecutionErrorKind::TypeArityMismatch.into()
956 }
957 StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
958 argument_idx: idx as TypeParameterIndex,
959 kind: TypeArgumentError::TypeNotFound,
960 }
961 .into(),
962 StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
963 argument_idx: idx as TypeParameterIndex,
964 kind: TypeArgumentError::ConstraintNotSatisfied,
965 }
966 .into(),
967 _ => self.convert_vm_error(error),
968 }
969 }
970
971 fn arg_is_borrowed(&self, arg: &Arg) -> bool {
974 self.borrowed.contains_key(arg)
975 }
976
977 fn arg_is_mut_borrowed(&self, arg: &Arg) -> bool {
980 matches!(self.borrowed.get(arg), Some(true))
981 }
982
983 fn borrow_mut(
986 &mut self,
987 arg: Arg,
988 usage: UsageKind,
989 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), EitherError> {
990 self.borrow_mut_impl(arg, Some(usage))
991 }
992
993 fn borrow_mut_impl(
996 &mut self,
997 arg: Arg,
998 update_last_usage: Option<UsageKind>,
999 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), EitherError> {
1000 match arg.0 {
1001 Arg_::V1(arg) => {
1002 assert_invariant!(
1003 !self.protocol_config.normalize_ptb_arguments(),
1004 "Should not be using v1 args with normalized args"
1005 );
1006 Ok(self.borrow_mut_impl_v1(arg, update_last_usage)?)
1007 }
1008 Arg_::V2(arg) => {
1009 assert_invariant!(
1010 self.protocol_config.normalize_ptb_arguments(),
1011 "Should be using only v2 args with normalized args"
1012 );
1013 Ok(self.borrow_mut_impl_v2(arg, update_last_usage)?)
1014 }
1015 }
1016 }
1017
1018 fn borrow_mut_impl_v1(
1020 &mut self,
1021 arg: Argument,
1022 update_last_usage: Option<UsageKind>,
1023 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
1024 {
1025 let (metadata, result_value) = match arg {
1026 Argument::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1027 Argument::Input(i) => {
1028 let Some(input_value) = self.inputs.get_mut(i as usize) else {
1029 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
1030 };
1031 (input_value.object_metadata.as_ref(), &mut input_value.inner)
1032 }
1033 Argument::Result(i) => {
1034 let Some(command_result) = self.results.get_mut(i as usize) else {
1035 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
1036 };
1037 if command_result.len() != 1 {
1038 return Err(CommandArgumentError::InvalidResultArity { result_idx: i });
1039 }
1040 (None, &mut command_result[0])
1041 }
1042 Argument::NestedResult(i, j) => {
1043 let Some(command_result) = self.results.get_mut(i as usize) else {
1044 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
1045 };
1046 let Some(result_value) = command_result.get_mut(j as usize) else {
1047 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
1048 result_idx: i,
1049 secondary_idx: j,
1050 });
1051 };
1052 (None, result_value)
1053 }
1054 };
1055 if let Some(usage) = update_last_usage {
1056 result_value.last_usage_kind = Some(usage);
1057 }
1058 Ok((metadata, &mut result_value.value))
1059 }
1060
1061 fn borrow_mut_impl_v2(
1063 &mut self,
1064 arg: NormalizedArg,
1065 update_last_usage: Option<UsageKind>,
1066 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), ExecutionError> {
1067 let (metadata, result_value) = match arg {
1068 NormalizedArg::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1069 NormalizedArg::Input(i) => {
1070 let input_value = self
1071 .inputs
1072 .get_mut(i as usize)
1073 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1074 (input_value.object_metadata.as_ref(), &mut input_value.inner)
1075 }
1076 NormalizedArg::Result(i, j) => {
1077 let result_value = self
1078 .results
1079 .get_mut(i as usize)
1080 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?
1081 .get_mut(j as usize)
1082 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1083 (None, result_value)
1084 }
1085 };
1086 if let Some(usage) = update_last_usage {
1087 result_value.last_usage_kind = Some(usage);
1088 }
1089 Ok((metadata, &mut result_value.value))
1090 }
1091
1092 pub(crate) fn execute_function_bypass_visibility(
1097 &mut self,
1098 module: &ModuleId,
1099 function_name: &IdentStr,
1100 ty_args: Vec<Type>,
1101 args: Vec<impl Borrow<[u8]>>,
1102 tracer: &mut Option<MoveTraceBuilder>,
1103 ) -> VMResult<SerializedReturnValues> {
1104 let gas_status = self.gas_charger.move_gas_status_mut();
1105 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1106 self.vm.get_runtime().execute_function_bypass_visibility(
1107 module,
1108 function_name,
1109 ty_args,
1110 args,
1111 &mut data_store,
1112 &mut IotaGasMeter(gas_status),
1113 &mut self.native_extensions,
1114 tracer.as_mut(),
1115 )
1116 }
1117
1118 pub(crate) fn load_function(
1123 &mut self,
1124 module_id: &ModuleId,
1125 function_name: &IdentStr,
1126 type_arguments: &[Type],
1127 ) -> VMResult<LoadedFunctionInstantiation> {
1128 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1129 self.vm.get_runtime().load_function(
1130 module_id,
1131 function_name,
1132 type_arguments,
1133 &mut data_store,
1134 )
1135 }
1136
1137 pub(crate) fn make_object_value(
1142 &mut self,
1143 type_: MoveObjectType,
1144 used_in_non_entry_move_call: bool,
1145 contents: &[u8],
1146 ) -> Result<ObjectValue, ExecutionError> {
1147 make_object_value(
1148 self.vm,
1149 &mut self.linkage_view,
1150 &self.new_packages,
1151 type_,
1152 used_in_non_entry_move_call,
1153 contents,
1154 )
1155 }
1156
1157 pub fn publish_module_bundle(
1162 &mut self,
1163 modules: Vec<Vec<u8>>,
1164 sender: AccountAddress,
1165 ) -> VMResult<()> {
1166 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1169 self.vm.get_runtime().publish_module_bundle(
1170 modules,
1171 sender,
1172 &mut data_store,
1173 &mut IotaGasMeter(self.gas_charger.move_gas_status_mut()),
1174 )
1175 }
1176 }
1177
1178 impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
1179 fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
1182 self.vm
1183 .get_runtime()
1184 .get_type_tag(type_)
1185 .map_err(|e| self.convert_vm_error(e))
1186 }
1187 }
1188
1189 fn package_for_linkage(
1193 linkage_view: &LinkageView,
1194 package_id: ObjectID,
1195 ) -> VMResult<PackageObject> {
1196 use move_binary_format::errors::PartialVMError;
1197 use move_core_types::vm_status::StatusCode;
1198
1199 match linkage_view.get_package_object(&package_id) {
1200 Ok(Some(package)) => Ok(package),
1201 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1202 .with_message(format!("Cannot find link context {package_id} in store"))
1203 .finish(Location::Undefined)),
1204 Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1205 .with_message(format!(
1206 "Error loading link context {package_id} from store: {err}"
1207 ))
1208 .finish(Location::Undefined)),
1209 }
1210 }
1211
1212 pub fn load_type_from_struct(
1218 vm: &MoveVM,
1219 linkage_view: &mut LinkageView,
1220 new_packages: &[MovePackage],
1221 struct_tag: &StructTag,
1222 ) -> VMResult<Type> {
1223 fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1224 Err(PartialVMError::new(code).finish(Location::Undefined))
1225 }
1226
1227 let StructTag {
1228 address,
1229 module,
1230 name,
1231 type_params,
1232 } = struct_tag;
1233
1234 let defining_id = ObjectID::from_address(*address);
1236 let package = package_for_linkage(linkage_view, defining_id)?;
1237
1238 let original_address = linkage_view
1241 .set_linkage(package.move_package())
1242 .map_err(|e| {
1243 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1244 .with_message(e.to_string())
1245 .finish(Location::Undefined)
1246 })?;
1247
1248 let runtime_id = ModuleId::new(original_address, module.clone());
1249 let data_store = IotaDataStore::new(linkage_view, new_packages);
1250 let res = vm.get_runtime().load_type(&runtime_id, name, &data_store);
1251 linkage_view.reset_linkage();
1252 let (idx, struct_type) = res?;
1253
1254 let type_param_constraints = struct_type.type_param_constraints();
1256 if type_param_constraints.len() != type_params.len() {
1257 return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1258 }
1259
1260 if type_params.is_empty() {
1261 Ok(Type::Datatype(idx))
1262 } else {
1263 let loaded_type_params = type_params
1264 .iter()
1265 .map(|type_param| load_type(vm, linkage_view, new_packages, type_param))
1266 .collect::<VMResult<Vec<_>>>()?;
1267
1268 for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1270 let abilities = vm.get_runtime().get_type_abilities(param)?;
1271 if !constraint.is_subset(abilities) {
1272 return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1273 }
1274 }
1275
1276 Ok(Type::DatatypeInstantiation(Box::new((
1277 idx,
1278 loaded_type_params,
1279 ))))
1280 }
1281 }
1282
1283 pub fn load_type(
1287 vm: &MoveVM,
1288 linkage_view: &mut LinkageView,
1289 new_packages: &[MovePackage],
1290 type_tag: &TypeTag,
1291 ) -> VMResult<Type> {
1292 Ok(match type_tag {
1293 TypeTag::Bool => Type::Bool,
1294 TypeTag::U8 => Type::U8,
1295 TypeTag::U16 => Type::U16,
1296 TypeTag::U32 => Type::U32,
1297 TypeTag::U64 => Type::U64,
1298 TypeTag::U128 => Type::U128,
1299 TypeTag::U256 => Type::U256,
1300 TypeTag::Address => Type::Address,
1301 TypeTag::Signer => Type::Signer,
1302
1303 TypeTag::Vector(inner) => {
1304 Type::Vector(Box::new(load_type(vm, linkage_view, new_packages, inner)?))
1305 }
1306 TypeTag::Struct(struct_tag) => {
1307 return load_type_from_struct(vm, linkage_view, new_packages, struct_tag);
1308 }
1309 })
1310 }
1311
1312 pub(crate) fn make_object_value(
1319 vm: &MoveVM,
1320 linkage_view: &mut LinkageView,
1321 new_packages: &[MovePackage],
1322 type_: MoveObjectType,
1323 used_in_non_entry_move_call: bool,
1324 contents: &[u8],
1325 ) -> Result<ObjectValue, ExecutionError> {
1326 let contents = if type_.is_coin() {
1327 let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1328 invariant_violation!("Could not deserialize a coin")
1329 };
1330 ObjectContents::Coin(coin)
1331 } else {
1332 ObjectContents::Raw(contents.to_vec())
1333 };
1334
1335 let tag: StructTag = type_.into();
1336 let type_ = load_type_from_struct(vm, linkage_view, new_packages, &tag)
1337 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1338 let abilities = vm
1339 .get_runtime()
1340 .get_type_abilities(&type_)
1341 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1342 let has_public_transfer = abilities.has_store();
1343 Ok(ObjectValue {
1344 type_,
1345 has_public_transfer,
1346 used_in_non_entry_move_call,
1347 contents,
1348 })
1349 }
1350
1351 impl Arg {
1352 fn is_gas_coin(&self) -> bool {
1353 match self {
1355 Arg(Arg_::V1(a)) => matches!(a, Argument::GasCoin),
1356 Arg(Arg_::V2(n)) => matches!(n, NormalizedArg::GasCoin),
1357 }
1358 }
1359 }
1360
1361 impl From<Arg> for Argument {
1362 fn from(arg: Arg) -> Self {
1363 match arg.0 {
1364 Arg_::V1(a) => a,
1365 Arg_::V2(normalized) => match normalized {
1366 NormalizedArg::GasCoin => Argument::GasCoin,
1367 NormalizedArg::Input(i) => Argument::Input(i),
1368 NormalizedArg::Result(i, j) => Argument::NestedResult(i, j),
1369 },
1370 }
1371 }
1372 }
1373
1374 pub(crate) fn value_from_object(
1379 vm: &MoveVM,
1380 linkage_view: &mut LinkageView,
1381 new_packages: &[MovePackage],
1382 object: &Object,
1383 ) -> Result<ObjectValue, ExecutionError> {
1384 let ObjectInner {
1385 data: Data::Move(object),
1386 ..
1387 } = object.as_inner()
1388 else {
1389 invariant_violation!("Expected a Move object");
1390 };
1391
1392 let used_in_non_entry_move_call = false;
1393 make_object_value(
1394 vm,
1395 linkage_view,
1396 new_packages,
1397 object.type_().clone(),
1398 used_in_non_entry_move_call,
1399 object.contents(),
1400 )
1401 }
1402
1403 fn load_object(
1405 vm: &MoveVM,
1406 state_view: &dyn ExecutionState,
1407 linkage_view: &mut LinkageView,
1408 new_packages: &[MovePackage],
1409 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1410 override_as_immutable: bool,
1411 id: ObjectID,
1412 ) -> Result<InputValue, ExecutionError> {
1413 let Some(obj) = state_view.read_object(&id) else {
1414 invariant_violation!("Object {} does not exist yet", id);
1416 };
1417 assert_invariant!(
1419 !override_as_immutable || matches!(obj.owner, Owner::Shared { .. }),
1420 "override_as_immutable should only be set for shared objects"
1421 );
1422 let is_mutable_input = match obj.owner {
1423 Owner::AddressOwner(_) => true,
1424 Owner::Shared { .. } => !override_as_immutable,
1425 Owner::Immutable => false,
1426 Owner::ObjectOwner(_) => {
1427 invariant_violation!("ObjectOwner objects cannot be input")
1429 }
1430 };
1431 let owner = obj.owner;
1432 let version = obj.version();
1433 let object_metadata = InputObjectMetadata::InputObject {
1434 id,
1435 is_mutable_input,
1436 owner,
1437 version,
1438 };
1439 let obj_value = value_from_object(vm, linkage_view, new_packages, obj)?;
1440 let contained_uids = {
1441 let fully_annotated_layout = vm
1442 .get_runtime()
1443 .type_to_fully_annotated_layout(&obj_value.type_)
1444 .map_err(|e| convert_vm_error(e, vm, linkage_view))?;
1445 let mut bytes = vec![];
1446 obj_value.write_bcs_bytes(&mut bytes, None)?;
1447 match get_all_uids(&fully_annotated_layout, &bytes) {
1448 Err(e) => {
1449 invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1450 }
1451 Ok(uids) => uids,
1452 }
1453 };
1454 let runtime_input = object_runtime::InputObject {
1455 contained_uids,
1456 owner,
1457 version,
1458 };
1459 let prev = input_object_map.insert(id, runtime_input);
1460 assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1462 Ok(InputValue::new_object(object_metadata, obj_value))
1463 }
1464
1465 fn load_call_arg(
1467 vm: &MoveVM,
1468 state_view: &dyn ExecutionState,
1469 linkage_view: &mut LinkageView,
1470 new_packages: &[MovePackage],
1471 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1472 call_arg: CallArg,
1473 ) -> Result<InputValue, ExecutionError> {
1474 Ok(match call_arg {
1475 CallArg::Pure(bytes) => InputValue::new_raw(RawValueType::Any, bytes),
1476 CallArg::Object(obj_arg) => load_object_arg(
1477 vm,
1478 state_view,
1479 linkage_view,
1480 new_packages,
1481 input_object_map,
1482 obj_arg,
1483 )?,
1484 })
1485 }
1486
1487 fn load_object_arg(
1490 vm: &MoveVM,
1491 state_view: &dyn ExecutionState,
1492 linkage_view: &mut LinkageView,
1493 new_packages: &[MovePackage],
1494 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1495 obj_arg: ObjectArg,
1496 ) -> Result<InputValue, ExecutionError> {
1497 match obj_arg {
1498 ObjectArg::ImmOrOwnedObject((id, _, _)) => load_object(
1499 vm,
1500 state_view,
1501 linkage_view,
1502 new_packages,
1503 input_object_map,
1504 false,
1506 id,
1507 ),
1508 ObjectArg::SharedObject { id, mutable, .. } => load_object(
1509 vm,
1510 state_view,
1511 linkage_view,
1512 new_packages,
1513 input_object_map,
1514 !mutable,
1516 id,
1517 ),
1518 ObjectArg::Receiving((id, version, _)) => {
1519 Ok(InputValue::new_receiving_object(id, version))
1520 }
1521 }
1522 }
1523
1524 fn add_additional_write(
1526 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1527 owner: Owner,
1528 object_value: ObjectValue,
1529 ) -> Result<(), ExecutionError> {
1530 let ObjectValue {
1531 type_, contents, ..
1532 } = object_value;
1533 let bytes = match contents {
1534 ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
1535 ObjectContents::Raw(bytes) => bytes,
1536 };
1537 let object_id = MoveObject::id_opt(&bytes).map_err(|e| {
1538 ExecutionError::invariant_violation(format!("No id for Raw object bytes. {e}"))
1539 })?;
1540 let additional_write = AdditionalWrite {
1541 recipient: owner,
1542 type_,
1543 bytes,
1544 };
1545 additional_writes.insert(object_id, additional_write);
1546 Ok(())
1547 }
1548
1549 fn refund_max_gas_budget(
1553 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1554 gas_charger: &mut GasCharger,
1555 gas_id: ObjectID,
1556 ) -> Result<(), ExecutionError> {
1557 let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
1558 invariant_violation!("Gas object cannot be wrapped or destroyed")
1559 };
1560 let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
1561 invariant_violation!("Gas object must be a coin")
1562 };
1563 let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
1564 return Err(ExecutionError::new_with_source(
1565 ExecutionErrorKind::CoinBalanceOverflow,
1566 "Gas coin too large after returning the max gas budget",
1567 ));
1568 };
1569 coin.balance = Balance::new(new_balance);
1570 *bytes = coin.to_bcs_bytes();
1571 Ok(())
1572 }
1573
1574 fn create_written_object(
1576 vm: &MoveVM,
1577 linkage_view: &LinkageView,
1578 protocol_config: &ProtocolConfig,
1579 objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1580 id: ObjectID,
1581 type_: Type,
1582 contents: Vec<u8>,
1583 ) -> Result<MoveObject, ExecutionError> {
1584 debug_assert_eq!(
1585 id,
1586 MoveObject::id_opt(&contents).expect("object contents should start with an id")
1587 );
1588 let old_obj_ver = objects_modified_at
1589 .get(&id)
1590 .map(|obj: &LoadedRuntimeObject| obj.version);
1591
1592 let type_tag = vm
1593 .get_runtime()
1594 .get_type_tag(&type_)
1595 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1596
1597 let struct_tag = match type_tag {
1598 TypeTag::Struct(inner) => *inner,
1599 _ => invariant_violation!("Non struct type for object"),
1600 };
1601 MoveObject::new_from_execution(
1602 struct_tag.into(),
1603 old_obj_ver.unwrap_or_default(),
1604 contents,
1605 protocol_config,
1606 )
1607 }
1608
1609 pub(crate) struct IotaDataStore<'state, 'a> {
1617 linkage_view: &'a LinkageView<'state>,
1618 new_packages: &'a [MovePackage],
1619 }
1620
1621 impl<'state, 'a> IotaDataStore<'state, 'a> {
1622 pub(crate) fn new(
1623 linkage_view: &'a LinkageView<'state>,
1624 new_packages: &'a [MovePackage],
1625 ) -> Self {
1626 Self {
1627 linkage_view,
1628 new_packages,
1629 }
1630 }
1631
1632 fn get_module(&self, module_id: &ModuleId) -> Option<&Vec<u8>> {
1633 for package in self.new_packages {
1634 let module = package.get_module(module_id);
1635 if module.is_some() {
1636 return module;
1637 }
1638 }
1639 None
1640 }
1641 }
1642
1643 impl DataStore for IotaDataStore<'_, '_> {
1647 fn link_context(&self) -> AccountAddress {
1648 self.linkage_view.link_context()
1649 }
1650
1651 fn relocate(&self, module_id: &ModuleId) -> PartialVMResult<ModuleId> {
1652 self.linkage_view.relocate(module_id).map_err(|err| {
1653 PartialVMError::new(StatusCode::LINKER_ERROR)
1654 .with_message(format!("Error relocating {module_id}: {err:?}"))
1655 })
1656 }
1657
1658 fn defining_module(
1659 &self,
1660 runtime_id: &ModuleId,
1661 struct_: &IdentStr,
1662 ) -> PartialVMResult<ModuleId> {
1663 self.linkage_view
1664 .defining_module(runtime_id, struct_)
1665 .map_err(|err| {
1666 PartialVMError::new(StatusCode::LINKER_ERROR).with_message(format!(
1667 "Error finding defining module for {runtime_id}::{struct_}: {err:?}"
1668 ))
1669 })
1670 }
1671
1672 fn load_module(&self, module_id: &ModuleId) -> VMResult<Vec<u8>> {
1673 if let Some(bytes) = self.get_module(module_id) {
1674 return Ok(bytes.clone());
1675 }
1676 match self.linkage_view.get_module(module_id) {
1677 Ok(Some(bytes)) => Ok(bytes),
1678 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1679 .with_message(format!("Cannot find {module_id:?} in data cache"))
1680 .finish(Location::Undefined)),
1681 Err(err) => {
1682 let msg = format!("Unexpected storage error: {err:?}");
1683 Err(
1684 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1685 .with_message(msg)
1686 .finish(Location::Undefined),
1687 )
1688 }
1689 }
1690 }
1691
1692 fn publish_module(&mut self, _module_id: &ModuleId, _blob: Vec<u8>) -> VMResult<()> {
1693 Ok(())
1696 }
1697 }
1698
1699 enum EitherError {
1700 CommandArgument(CommandArgumentError),
1701 Execution(ExecutionError),
1702 }
1703
1704 impl From<ExecutionError> for EitherError {
1705 fn from(e: ExecutionError) -> Self {
1706 EitherError::Execution(e)
1707 }
1708 }
1709
1710 impl From<CommandArgumentError> for EitherError {
1711 fn from(e: CommandArgumentError) -> Self {
1712 EitherError::CommandArgument(e)
1713 }
1714 }
1715
1716 impl EitherError {
1717 fn into_execution_error(self, command_index: usize) -> ExecutionError {
1718 match self {
1719 EitherError::CommandArgument(e) => command_argument_error(e, command_index),
1720 EitherError::Execution(e) => e,
1721 }
1722 }
1723 }
1724}