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 programmable_transactions::linkage_view::LinkageView,
64 type_resolver::TypeTagResolver,
65 };
66
67 pub struct ExecutionContext<'vm, 'state, 'a> {
69 pub protocol_config: &'a ProtocolConfig,
71 pub metrics: Arc<LimitsMetrics>,
73 pub vm: &'vm MoveVM,
75 pub linkage_view: LinkageView<'state>,
77 pub native_extensions: NativeContextExtensions<'state>,
78 pub state_view: &'state dyn ExecutionState,
80 pub tx_context: &'a mut TxContext,
83 pub gas_charger: &'a mut GasCharger,
85 additional_transfers: Vec<(IotaAddress, ObjectValue)>,
87 new_packages: Vec<MovePackage>,
89 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
91 gas: InputValue,
94 inputs: Vec<InputValue>,
97 results: Vec<Vec<ResultValue>>,
102 borrowed: HashMap<Argument, bool>,
106 }
107
108 struct AdditionalWrite {
111 recipient: Owner,
113 type_: Type,
115 bytes: Vec<u8>,
117 }
118
119 impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
120 #[instrument(name = "ExecutionContext::new", level = "trace", skip_all)]
127 pub fn new(
128 protocol_config: &'a ProtocolConfig,
129 metrics: Arc<LimitsMetrics>,
130 vm: &'vm MoveVM,
131 state_view: &'state dyn ExecutionState,
132 tx_context: &'a mut TxContext,
133 gas_charger: &'a mut GasCharger,
134 inputs: Vec<CallArg>,
135 ) -> Result<Self, ExecutionError>
136 where
137 'a: 'state,
138 {
139 let mut linkage_view = LinkageView::new(Box::new(state_view.as_iota_resolver()));
140 let mut input_object_map = BTreeMap::new();
141 let inputs = inputs
142 .into_iter()
143 .map(|call_arg| {
144 load_call_arg(
145 vm,
146 state_view,
147 &mut linkage_view,
148 &[],
149 &mut input_object_map,
150 call_arg,
151 )
152 })
153 .collect::<Result<_, ExecutionError>>()?;
154 let gas = if let Some(gas_coin) = gas_charger.gas_coin() {
155 let mut gas = load_object(
156 vm,
157 state_view,
158 &mut linkage_view,
159 &[],
160 &mut input_object_map,
161 false,
163 gas_coin,
164 )?;
165 let Some(Value::Object(ObjectValue {
169 contents: ObjectContents::Coin(coin),
170 ..
171 })) = &mut gas.inner.value
172 else {
173 invariant_violation!("Gas object should be a populated coin")
174 };
175
176 let max_gas_in_balance = gas_charger.gas_budget();
177 let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else {
178 invariant_violation!(
179 "Transaction input checker should check that there is enough gas"
180 );
181 };
182 coin.balance = Balance::new(new_balance);
183 gas
184 } else {
185 InputValue {
186 object_metadata: None,
187 inner: ResultValue {
188 last_usage_kind: None,
189 value: None,
190 },
191 }
192 };
193 let native_extensions = new_native_extensions(
194 state_view.as_child_resolver(),
195 input_object_map,
196 !gas_charger.is_unmetered(),
197 protocol_config,
198 metrics.clone(),
199 tx_context.epoch(),
200 );
201
202 #[skip_checked_arithmetic]
204 move_vm_profiler::tracing_feature_enabled! {
205 use move_vm_profiler::GasProfiler;
206 use move_vm_types::gas::GasMeter;
207
208 let tx_digest = tx_context.digest();
209 let remaining_gas: u64 =
210 move_vm_types::gas::GasMeter::remaining_gas(gas_charger.move_gas_status())
211 .into();
212 gas_charger
213 .move_gas_status_mut()
214 .set_profiler(GasProfiler::init(
215 &vm.config().profiler_config,
216 format!("{tx_digest}"),
217 remaining_gas,
218 ));
219 }
220
221 Ok(Self {
222 protocol_config,
223 metrics,
224 vm,
225 linkage_view,
226 native_extensions,
227 state_view,
228 tx_context,
229 gas_charger,
230 gas,
231 inputs,
232 results: vec![],
233 additional_transfers: vec![],
234 new_packages: vec![],
235 user_events: vec![],
236 borrowed: HashMap::new(),
237 })
238 }
239
240 pub fn object_runtime(&mut self) -> &ObjectRuntime {
241 self.native_extensions.get()
242 }
243
244 pub fn fresh_id(&mut self) -> Result<ObjectID, ExecutionError> {
246 let object_id = self.tx_context.fresh_id();
247 let object_runtime: &mut ObjectRuntime = self.native_extensions.get_mut();
248 object_runtime
249 .new_id(object_id)
250 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
251 Ok(object_id)
252 }
253
254 pub fn delete_id(&mut self, object_id: ObjectID) -> Result<(), ExecutionError> {
256 let object_runtime: &mut ObjectRuntime = self.native_extensions.get_mut();
257 object_runtime
258 .delete_id(object_id)
259 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
260 }
261
262 pub fn set_link_context(
266 &mut self,
267 package_id: ObjectID,
268 ) -> Result<AccountAddress, ExecutionError> {
269 if self.linkage_view.has_linkage(package_id) {
270 return Ok(self
272 .linkage_view
273 .original_package_id()
274 .unwrap_or(*package_id));
275 }
276
277 let package = package_for_linkage(&self.linkage_view, package_id)
278 .map_err(|e| self.convert_vm_error(e))?;
279
280 self.linkage_view.set_linkage(package.move_package())
281 }
282
283 pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
285 load_type(
286 self.vm,
287 &mut self.linkage_view,
288 &self.new_packages,
289 type_tag,
290 )
291 }
292
293 pub fn load_type_from_struct(&mut self, struct_tag: &StructTag) -> VMResult<Type> {
295 load_type_from_struct(
296 self.vm,
297 &mut self.linkage_view,
298 &self.new_packages,
299 struct_tag,
300 )
301 }
302
303 pub fn take_user_events(
306 &mut self,
307 module_id: &ModuleId,
308 function: FunctionDefinitionIndex,
309 last_offset: CodeOffset,
310 ) -> Result<(), ExecutionError> {
311 let object_runtime: &mut ObjectRuntime = self.native_extensions.get_mut();
312 let events = object_runtime.take_user_events();
313 let num_events = self.user_events.len() + events.len();
314 let max_events = self.protocol_config.max_num_event_emit();
315 if num_events as u64 > max_events {
316 let err = max_event_error(max_events)
317 .at_code_offset(function, last_offset)
318 .finish(Location::Module(module_id.clone()));
319 return Err(self.convert_vm_error(err));
320 }
321 let new_events = events
322 .into_iter()
323 .map(|(ty, tag, value)| {
324 let layout = self
325 .vm
326 .get_runtime()
327 .type_to_type_layout(&ty)
328 .map_err(|e| self.convert_vm_error(e))?;
329 let Some(bytes) = value.simple_serialize(&layout) else {
330 invariant_violation!("Failed to deserialize already serialized Move value");
331 };
332 Ok((module_id.clone(), tag, bytes))
333 })
334 .collect::<Result<Vec<_>, ExecutionError>>()?;
335 self.user_events.extend(new_events);
336 Ok(())
337 }
338
339 pub fn by_value_arg<V: TryFromValue>(
345 &mut self,
346 command_kind: CommandKind<'_>,
347 arg_idx: usize,
348 arg: Argument,
349 ) -> Result<V, ExecutionError> {
350 self.by_value_arg_(command_kind, arg)
351 .map_err(|e| command_argument_error(e, arg_idx))
352 }
353 fn by_value_arg_<V: TryFromValue>(
354 &mut self,
355 command_kind: CommandKind<'_>,
356 arg: Argument,
357 ) -> Result<V, CommandArgumentError> {
358 let is_borrowed = self.arg_is_borrowed(&arg);
359 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::ByValue)?;
360 let is_copyable = if let Some(val) = val_opt {
361 val.is_copyable()
362 } else {
363 return Err(CommandArgumentError::InvalidValueUsage);
364 };
365 if !is_copyable && is_borrowed {
371 return Err(CommandArgumentError::InvalidValueUsage);
372 }
373 if matches!(arg, Argument::GasCoin)
375 && !matches!(command_kind, CommandKind::TransferObjects)
376 {
377 return Err(CommandArgumentError::InvalidGasCoinUsage);
378 }
379 if matches!(
381 input_metadata_opt,
382 Some(InputObjectMetadata::InputObject {
383 owner: Owner::Immutable,
384 ..
385 })
386 ) {
387 return Err(CommandArgumentError::InvalidObjectByValue);
388 }
389
390 if matches!(
392 input_metadata_opt,
393 Some(InputObjectMetadata::InputObject {
394 is_mutable_input: false,
395 ..
396 })
397 ) {
398 return Err(CommandArgumentError::InvalidObjectByValue);
399 }
400
401 let val = if is_copyable {
402 val_opt.as_ref().unwrap().clone()
403 } else {
404 val_opt.take().unwrap()
405 };
406 V::try_from_value(val)
407 }
408
409 pub fn borrow_arg_mut<V: TryFromValue>(
416 &mut self,
417 arg_idx: usize,
418 arg: Argument,
419 ) -> Result<V, ExecutionError> {
420 self.borrow_arg_mut_(arg)
421 .map_err(|e| command_argument_error(e, arg_idx))
422 }
423 fn borrow_arg_mut_<V: TryFromValue>(
424 &mut self,
425 arg: Argument,
426 ) -> Result<V, CommandArgumentError> {
427 if self.arg_is_borrowed(&arg) {
429 return Err(CommandArgumentError::InvalidValueUsage);
430 }
431 self.borrowed.insert(arg, true);
432 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowMut)?;
433 let is_copyable = if let Some(val) = val_opt {
434 val.is_copyable()
435 } else {
436 return Err(CommandArgumentError::InvalidValueUsage);
438 };
439 if let Some(InputObjectMetadata::InputObject {
440 is_mutable_input: false,
441 ..
442 }) = input_metadata_opt
443 {
444 return Err(CommandArgumentError::InvalidObjectByMutRef);
445 }
446 let val = if is_copyable {
449 val_opt.as_ref().unwrap().clone()
450 } else {
451 val_opt.take().unwrap()
452 };
453 V::try_from_value(val)
454 }
455
456 pub fn borrow_arg<V: TryFromValue>(
461 &mut self,
462 arg_idx: usize,
463 arg: Argument,
464 type_: &Type,
465 ) -> Result<V, ExecutionError> {
466 self.borrow_arg_(arg, type_)
467 .map_err(|e| command_argument_error(e, arg_idx))
468 }
469 fn borrow_arg_<V: TryFromValue>(
470 &mut self,
471 arg: Argument,
472 arg_type: &Type,
473 ) -> Result<V, CommandArgumentError> {
474 if self.arg_is_mut_borrowed(&arg) {
478 return Err(CommandArgumentError::InvalidValueUsage);
479 }
480 self.borrowed.insert(arg, false);
481 let (_input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowImm)?;
482 if val_opt.is_none() {
483 return Err(CommandArgumentError::InvalidValueUsage);
484 }
485
486 if let &mut Some(Value::Receiving(_, _, ref mut recv_arg_type @ None)) = val_opt {
488 let Type::Reference(inner) = arg_type else {
489 return Err(CommandArgumentError::InvalidValueUsage);
490 };
491 *recv_arg_type = Some(*(*inner).clone());
492 }
493
494 V::try_from_value(val_opt.as_ref().unwrap().clone())
495 }
496
497 pub fn restore_arg<Mode: ExecutionMode>(
499 &mut self,
500 updates: &mut Mode::ArgumentUpdates,
501 arg: Argument,
502 value: Value,
503 ) -> Result<(), ExecutionError> {
504 Mode::add_argument_update(self, updates, arg, &value)?;
505 let was_mut_opt = self.borrowed.remove(&arg);
506 assert_invariant!(
507 was_mut_opt.is_some() && was_mut_opt.unwrap(),
508 "Should never restore a non-mut borrowed value. \
509 The take+restore is an implementation detail of mutable references"
510 );
511 let Ok((_, value_opt)) = self.borrow_mut_impl(arg, None) else {
513 invariant_violation!("Should be able to borrow argument to restore it")
514 };
515
516 let old_value = value_opt.replace(value);
517 assert_invariant!(
518 old_value.is_none() || old_value.unwrap().is_copyable(),
519 "Should never restore a non-taken value, unless it is copyable. \
520 The take+restore is an implementation detail of mutable references"
521 );
522
523 Ok(())
524 }
525
526 pub fn transfer_object(
528 &mut self,
529 obj: ObjectValue,
530 addr: IotaAddress,
531 ) -> Result<(), ExecutionError> {
532 self.additional_transfers.push((addr, obj));
533 Ok(())
534 }
535
536 pub fn new_package<'p>(
538 &self,
539 modules: &[CompiledModule],
540 dependencies: impl IntoIterator<Item = &'p MovePackage>,
541 ) -> Result<MovePackage, ExecutionError> {
542 MovePackage::new_initial(
543 modules,
544 self.protocol_config.max_move_package_size(),
545 self.protocol_config.move_binary_format_version(),
546 dependencies,
547 )
548 }
549
550 pub fn upgrade_package<'p>(
553 &self,
554 storage_id: ObjectID,
555 previous_package: &MovePackage,
556 new_modules: &[CompiledModule],
557 dependencies: impl IntoIterator<Item = &'p MovePackage>,
558 ) -> Result<MovePackage, ExecutionError> {
559 previous_package.new_upgraded(
560 storage_id,
561 new_modules,
562 self.protocol_config,
563 dependencies,
564 )
565 }
566
567 pub fn write_package(&mut self, package: MovePackage) {
569 self.new_packages.push(package);
570 }
571
572 pub fn pop_package(&mut self) -> Option<MovePackage> {
578 self.new_packages.pop()
579 }
580
581 pub fn push_command_results(&mut self, results: Vec<Value>) -> Result<(), ExecutionError> {
584 assert_invariant!(
585 self.borrowed.values().all(|is_mut| !is_mut),
586 "all mut borrows should be restored"
587 );
588 self.borrowed = HashMap::new();
590 self.results
591 .push(results.into_iter().map(ResultValue::new).collect());
592 Ok(())
593 }
594
595 pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
597 let Self {
598 protocol_config,
599 vm,
600 linkage_view,
601 mut native_extensions,
602 tx_context,
603 gas_charger,
604 additional_transfers,
605 new_packages,
606 gas,
607 inputs,
608 results,
609 user_events,
610 state_view,
611 ..
612 } = self;
613 let tx_digest = tx_context.digest();
614 let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
615 let mut loaded_runtime_objects = BTreeMap::new();
616 let mut additional_writes = BTreeMap::new();
617 let mut by_value_shared_objects = BTreeSet::new();
618 for input in inputs.into_iter().chain(std::iter::once(gas)) {
619 let InputValue {
620 object_metadata:
621 Some(InputObjectMetadata::InputObject {
622 is_mutable_input: true,
624 id,
625 version,
626 owner,
627 }),
628 inner: ResultValue { value, .. },
629 } = input
630 else {
631 continue;
632 };
633 loaded_runtime_objects.insert(
634 id,
635 LoadedRuntimeObject {
636 version,
637 is_modified: true,
638 },
639 );
640 if let Some(Value::Object(object_value)) = value {
641 add_additional_write(&mut additional_writes, owner, object_value)?;
642 } else if owner.is_shared() {
643 by_value_shared_objects.insert(id);
644 }
645 }
646 if !Mode::allow_arbitrary_values() {
649 for (i, command_result) in results.iter().enumerate() {
650 for (j, result_value) in command_result.iter().enumerate() {
651 let ResultValue {
652 last_usage_kind,
653 value,
654 } = result_value;
655 match value {
656 None => (),
657 Some(Value::Object(_)) => {
658 return Err(ExecutionErrorKind::UnusedValueWithoutDrop {
659 result_idx: i as u16,
660 secondary_idx: j as u16,
661 }
662 .into());
663 }
664 Some(Value::Raw(RawValueType::Any, _)) => (),
665 Some(Value::Raw(RawValueType::Loaded { abilities, .. }, _)) => {
666 if abilities.has_drop()
672 || (abilities.has_copy()
673 && matches!(last_usage_kind, Some(UsageKind::ByValue)))
674 {
675 } else {
676 let msg = if abilities.has_copy() {
677 "The value has copy, but not drop. \
678 Its last usage must be by-value so it can be taken."
679 } else {
680 "Unused value without drop"
681 };
682 return Err(ExecutionError::new_with_source(
683 ExecutionErrorKind::UnusedValueWithoutDrop {
684 result_idx: i as u16,
685 secondary_idx: j as u16,
686 },
687 msg,
688 ));
689 }
690 }
691 Some(Value::Receiving(_, _, _)) => (),
693 }
694 }
695 }
696 }
697 for (recipient, object_value) in additional_transfers {
699 let owner = Owner::AddressOwner(recipient);
700 add_additional_write(&mut additional_writes, owner, object_value)?;
701 }
702 if let Some(gas_id) = gas_id_opt {
704 refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
705 }
706
707 let object_runtime: ObjectRuntime = native_extensions.remove();
708
709 let RuntimeResults {
710 writes,
711 user_events: remaining_events,
712 loaded_child_objects,
713 mut created_object_ids,
714 deleted_object_ids,
715 } = object_runtime.finish()?;
716 assert_invariant!(
717 remaining_events.is_empty(),
718 "Events should be taken after every Move call"
719 );
720
721 loaded_runtime_objects.extend(loaded_child_objects);
722
723 let mut written_objects = BTreeMap::new();
724 for package in new_packages {
725 let package_obj = Object::new_from_package(package, tx_digest);
726 let id = package_obj.id();
727 created_object_ids.insert(id);
728 written_objects.insert(id, package_obj);
729 }
730 for (id, additional_write) in additional_writes {
731 let AdditionalWrite {
732 recipient,
733 type_,
734 bytes,
735 } = additional_write;
736
737 let move_object = {
738 create_written_object(
739 vm,
740 &linkage_view,
741 protocol_config,
742 &loaded_runtime_objects,
743 id,
744 type_,
745 bytes,
746 )?
747 };
748 let object = Object::new_move(move_object, recipient, tx_digest);
749 written_objects.insert(id, object);
750 if let Some(loaded) = loaded_runtime_objects.get_mut(&id) {
751 loaded.is_modified = true;
752 }
753 }
754
755 for (id, (recipient, ty, value)) in writes {
756 let layout = vm
757 .get_runtime()
758 .type_to_type_layout(&ty)
759 .map_err(|e| convert_vm_error(e, vm, &linkage_view))?;
760 let Some(bytes) = value.simple_serialize(&layout) else {
761 invariant_violation!("Failed to deserialize already serialized Move value");
762 };
763 let move_object = {
764 create_written_object(
765 vm,
766 &linkage_view,
767 protocol_config,
768 &loaded_runtime_objects,
769 id,
770 ty,
771 bytes,
772 )?
773 };
774 let object = Object::new_move(move_object, recipient, tx_digest);
775 written_objects.insert(id, object);
776 }
777
778 for id in &by_value_shared_objects {
785 if let Some(obj) = written_objects.get(id) {
788 if !obj.is_shared() {
789 return Err(ExecutionError::new(
790 ExecutionErrorKind::SharedObjectOperationNotAllowed,
791 Some(
792 format!(
793 "Shared object operation on {id} not allowed: \
794 cannot be frozen, transferred, or wrapped"
795 )
796 .into(),
797 ),
798 ));
799 }
800 } else {
801 if !deleted_object_ids.contains(id) {
804 return Err(ExecutionError::new(
805 ExecutionErrorKind::SharedObjectOperationNotAllowed,
806 Some(
807 format!("Shared object operation on {id} not allowed: \
808 shared objects used by value must be re-shared if not deleted").into(),
809 ),
810 ));
811 }
812 }
813 }
814
815 let DenyListResult {
816 result,
817 num_non_gas_coin_owners,
818 } = state_view.check_coin_deny_list(&written_objects);
819 gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
820 result?;
821
822 let user_events = user_events
823 .into_iter()
824 .map(|(module_id, tag, contents)| {
825 Event::new(
826 module_id.address(),
827 module_id.name(),
828 tx_context.sender(),
829 tag,
830 contents,
831 )
832 })
833 .collect();
834
835 Ok(ExecutionResults::V1(ExecutionResultsV1 {
836 written_objects,
837 modified_objects: loaded_runtime_objects
838 .into_iter()
839 .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
840 .collect(),
841 created_object_ids: created_object_ids.into_iter().collect(),
842 deleted_object_ids: deleted_object_ids.into_iter().collect(),
843 user_events,
844 }))
845 }
846
847 pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
849 crate::error::convert_vm_error(error, self.vm, &self.linkage_view)
850 }
851
852 pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
854 use iota_types::execution_status::TypeArgumentError;
855 use move_core_types::vm_status::StatusCode;
856 match error.major_status() {
857 StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
858 ExecutionErrorKind::TypeArityMismatch.into()
859 }
860 StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
861 argument_idx: idx as TypeParameterIndex,
862 kind: TypeArgumentError::TypeNotFound,
863 }
864 .into(),
865 StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
866 argument_idx: idx as TypeParameterIndex,
867 kind: TypeArgumentError::ConstraintNotSatisfied,
868 }
869 .into(),
870 _ => self.convert_vm_error(error),
871 }
872 }
873
874 fn arg_is_borrowed(&self, arg: &Argument) -> bool {
877 self.borrowed.contains_key(arg)
878 }
879
880 fn arg_is_mut_borrowed(&self, arg: &Argument) -> bool {
883 matches!(self.borrowed.get(arg), Some(true))
884 }
885
886 fn borrow_mut(
889 &mut self,
890 arg: Argument,
891 usage: UsageKind,
892 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
893 {
894 self.borrow_mut_impl(arg, Some(usage))
895 }
896
897 fn borrow_mut_impl(
900 &mut self,
901 arg: Argument,
902 update_last_usage: Option<UsageKind>,
903 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
904 {
905 let (metadata, result_value) = match arg {
906 Argument::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
907 Argument::Input(i) => {
908 let Some(input_value) = self.inputs.get_mut(i as usize) else {
909 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
910 };
911 (input_value.object_metadata.as_ref(), &mut input_value.inner)
912 }
913 Argument::Result(i) => {
914 let Some(command_result) = self.results.get_mut(i as usize) else {
915 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
916 };
917 if command_result.len() != 1 {
918 return Err(CommandArgumentError::InvalidResultArity { result_idx: i });
919 }
920 (None, &mut command_result[0])
921 }
922 Argument::NestedResult(i, j) => {
923 let Some(command_result) = self.results.get_mut(i as usize) else {
924 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
925 };
926 let Some(result_value) = command_result.get_mut(j as usize) else {
927 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
928 result_idx: i,
929 secondary_idx: j,
930 });
931 };
932 (None, result_value)
933 }
934 };
935 if let Some(usage) = update_last_usage {
936 result_value.last_usage_kind = Some(usage);
937 }
938 Ok((metadata, &mut result_value.value))
939 }
940
941 pub(crate) fn execute_function_bypass_visibility(
946 &mut self,
947 module: &ModuleId,
948 function_name: &IdentStr,
949 ty_args: Vec<Type>,
950 args: Vec<impl Borrow<[u8]>>,
951 tracer: &mut Option<MoveTraceBuilder>,
952 ) -> VMResult<SerializedReturnValues> {
953 let gas_status = self.gas_charger.move_gas_status_mut();
954 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
955 self.vm.get_runtime().execute_function_bypass_visibility(
956 module,
957 function_name,
958 ty_args,
959 args,
960 &mut data_store,
961 gas_status,
962 &mut self.native_extensions,
963 tracer.as_mut(),
964 )
965 }
966
967 pub(crate) fn load_function(
972 &mut self,
973 module_id: &ModuleId,
974 function_name: &IdentStr,
975 type_arguments: &[Type],
976 ) -> VMResult<LoadedFunctionInstantiation> {
977 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
978 self.vm.get_runtime().load_function(
979 module_id,
980 function_name,
981 type_arguments,
982 &mut data_store,
983 )
984 }
985
986 pub(crate) fn make_object_value(
991 &mut self,
992 type_: MoveObjectType,
993 used_in_non_entry_move_call: bool,
994 contents: &[u8],
995 ) -> Result<ObjectValue, ExecutionError> {
996 make_object_value(
997 self.vm,
998 &mut self.linkage_view,
999 &self.new_packages,
1000 type_,
1001 used_in_non_entry_move_call,
1002 contents,
1003 )
1004 }
1005
1006 pub fn publish_module_bundle(
1011 &mut self,
1012 modules: Vec<Vec<u8>>,
1013 sender: AccountAddress,
1014 ) -> VMResult<()> {
1015 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1018 self.vm.get_runtime().publish_module_bundle(
1019 modules,
1020 sender,
1021 &mut data_store,
1022 self.gas_charger.move_gas_status_mut(),
1023 )
1024 }
1025 }
1026
1027 impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
1028 fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
1031 self.vm
1032 .get_runtime()
1033 .get_type_tag(type_)
1034 .map_err(|e| self.convert_vm_error(e))
1035 }
1036 }
1037
1038 fn package_for_linkage(
1042 linkage_view: &LinkageView,
1043 package_id: ObjectID,
1044 ) -> VMResult<PackageObject> {
1045 use move_binary_format::errors::PartialVMError;
1046 use move_core_types::vm_status::StatusCode;
1047
1048 match linkage_view.get_package_object(&package_id) {
1049 Ok(Some(package)) => Ok(package),
1050 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1051 .with_message(format!("Cannot find link context {package_id} in store"))
1052 .finish(Location::Undefined)),
1053 Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1054 .with_message(format!(
1055 "Error loading link context {package_id} from store: {err}"
1056 ))
1057 .finish(Location::Undefined)),
1058 }
1059 }
1060
1061 pub fn load_type_from_struct(
1067 vm: &MoveVM,
1068 linkage_view: &mut LinkageView,
1069 new_packages: &[MovePackage],
1070 struct_tag: &StructTag,
1071 ) -> VMResult<Type> {
1072 fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1073 Err(PartialVMError::new(code).finish(Location::Undefined))
1074 }
1075
1076 let StructTag {
1077 address,
1078 module,
1079 name,
1080 type_params,
1081 } = struct_tag;
1082
1083 let defining_id = ObjectID::from_address(*address);
1085 let package = package_for_linkage(linkage_view, defining_id)?;
1086
1087 let original_address = linkage_view
1090 .set_linkage(package.move_package())
1091 .map_err(|e| {
1092 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1093 .with_message(e.to_string())
1094 .finish(Location::Undefined)
1095 })?;
1096
1097 let runtime_id = ModuleId::new(original_address, module.clone());
1098 let data_store = IotaDataStore::new(linkage_view, new_packages);
1099 let res = vm.get_runtime().load_type(&runtime_id, name, &data_store);
1100 linkage_view.reset_linkage();
1101 let (idx, struct_type) = res?;
1102
1103 let type_param_constraints = struct_type.type_param_constraints();
1105 if type_param_constraints.len() != type_params.len() {
1106 return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1107 }
1108
1109 if type_params.is_empty() {
1110 Ok(Type::Datatype(idx))
1111 } else {
1112 let loaded_type_params = type_params
1113 .iter()
1114 .map(|type_param| load_type(vm, linkage_view, new_packages, type_param))
1115 .collect::<VMResult<Vec<_>>>()?;
1116
1117 for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1119 let abilities = vm.get_runtime().get_type_abilities(param)?;
1120 if !constraint.is_subset(abilities) {
1121 return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1122 }
1123 }
1124
1125 Ok(Type::DatatypeInstantiation(Box::new((
1126 idx,
1127 loaded_type_params,
1128 ))))
1129 }
1130 }
1131
1132 pub fn load_type(
1136 vm: &MoveVM,
1137 linkage_view: &mut LinkageView,
1138 new_packages: &[MovePackage],
1139 type_tag: &TypeTag,
1140 ) -> VMResult<Type> {
1141 Ok(match type_tag {
1142 TypeTag::Bool => Type::Bool,
1143 TypeTag::U8 => Type::U8,
1144 TypeTag::U16 => Type::U16,
1145 TypeTag::U32 => Type::U32,
1146 TypeTag::U64 => Type::U64,
1147 TypeTag::U128 => Type::U128,
1148 TypeTag::U256 => Type::U256,
1149 TypeTag::Address => Type::Address,
1150 TypeTag::Signer => Type::Signer,
1151
1152 TypeTag::Vector(inner) => {
1153 Type::Vector(Box::new(load_type(vm, linkage_view, new_packages, inner)?))
1154 }
1155 TypeTag::Struct(struct_tag) => {
1156 return load_type_from_struct(vm, linkage_view, new_packages, struct_tag);
1157 }
1158 })
1159 }
1160
1161 pub(crate) fn make_object_value(
1168 vm: &MoveVM,
1169 linkage_view: &mut LinkageView,
1170 new_packages: &[MovePackage],
1171 type_: MoveObjectType,
1172 used_in_non_entry_move_call: bool,
1173 contents: &[u8],
1174 ) -> Result<ObjectValue, ExecutionError> {
1175 let contents = if type_.is_coin() {
1176 let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1177 invariant_violation!("Could not deserialize a coin")
1178 };
1179 ObjectContents::Coin(coin)
1180 } else {
1181 ObjectContents::Raw(contents.to_vec())
1182 };
1183
1184 let tag: StructTag = type_.into();
1185 let type_ = load_type_from_struct(vm, linkage_view, new_packages, &tag)
1186 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1187 let abilities = vm
1188 .get_runtime()
1189 .get_type_abilities(&type_)
1190 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1191 let has_public_transfer = abilities.has_store();
1192 Ok(ObjectValue {
1193 type_,
1194 has_public_transfer,
1195 used_in_non_entry_move_call,
1196 contents,
1197 })
1198 }
1199
1200 pub(crate) fn value_from_object(
1205 vm: &MoveVM,
1206 linkage_view: &mut LinkageView,
1207 new_packages: &[MovePackage],
1208 object: &Object,
1209 ) -> Result<ObjectValue, ExecutionError> {
1210 let ObjectInner {
1211 data: Data::Move(object),
1212 ..
1213 } = object.as_inner()
1214 else {
1215 invariant_violation!("Expected a Move object");
1216 };
1217
1218 let used_in_non_entry_move_call = false;
1219 make_object_value(
1220 vm,
1221 linkage_view,
1222 new_packages,
1223 object.type_().clone(),
1224 used_in_non_entry_move_call,
1225 object.contents(),
1226 )
1227 }
1228
1229 fn load_object(
1231 vm: &MoveVM,
1232 state_view: &dyn ExecutionState,
1233 linkage_view: &mut LinkageView,
1234 new_packages: &[MovePackage],
1235 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1236 override_as_immutable: bool,
1237 id: ObjectID,
1238 ) -> Result<InputValue, ExecutionError> {
1239 let Some(obj) = state_view.read_object(&id) else {
1240 invariant_violation!("Object {} does not exist yet", id);
1242 };
1243 assert_invariant!(
1245 !override_as_immutable || matches!(obj.owner, Owner::Shared { .. }),
1246 "override_as_immutable should only be set for shared objects"
1247 );
1248 let is_mutable_input = match obj.owner {
1249 Owner::AddressOwner(_) => true,
1250 Owner::Shared { .. } => !override_as_immutable,
1251 Owner::Immutable => false,
1252 Owner::ObjectOwner(_) => {
1253 invariant_violation!("ObjectOwner objects cannot be input")
1255 }
1256 };
1257 let owner = obj.owner;
1258 let version = obj.version();
1259 let object_metadata = InputObjectMetadata::InputObject {
1260 id,
1261 is_mutable_input,
1262 owner,
1263 version,
1264 };
1265 let obj_value = value_from_object(vm, linkage_view, new_packages, obj)?;
1266 let contained_uids = {
1267 let fully_annotated_layout = vm
1268 .get_runtime()
1269 .type_to_fully_annotated_layout(&obj_value.type_)
1270 .map_err(|e| convert_vm_error(e, vm, linkage_view))?;
1271 let mut bytes = vec![];
1272 obj_value.write_bcs_bytes(&mut bytes, None)?;
1273 match get_all_uids(&fully_annotated_layout, &bytes) {
1274 Err(e) => {
1275 invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1276 }
1277 Ok(uids) => uids,
1278 }
1279 };
1280 let runtime_input = object_runtime::InputObject {
1281 contained_uids,
1282 owner,
1283 version,
1284 };
1285 let prev = input_object_map.insert(id, runtime_input);
1286 assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1288 Ok(InputValue::new_object(object_metadata, obj_value))
1289 }
1290
1291 fn load_call_arg(
1293 vm: &MoveVM,
1294 state_view: &dyn ExecutionState,
1295 linkage_view: &mut LinkageView,
1296 new_packages: &[MovePackage],
1297 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1298 call_arg: CallArg,
1299 ) -> Result<InputValue, ExecutionError> {
1300 Ok(match call_arg {
1301 CallArg::Pure(bytes) => InputValue::new_raw(RawValueType::Any, bytes),
1302 CallArg::Object(obj_arg) => load_object_arg(
1303 vm,
1304 state_view,
1305 linkage_view,
1306 new_packages,
1307 input_object_map,
1308 obj_arg,
1309 )?,
1310 })
1311 }
1312
1313 fn load_object_arg(
1316 vm: &MoveVM,
1317 state_view: &dyn ExecutionState,
1318 linkage_view: &mut LinkageView,
1319 new_packages: &[MovePackage],
1320 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1321 obj_arg: ObjectArg,
1322 ) -> Result<InputValue, ExecutionError> {
1323 match obj_arg {
1324 ObjectArg::ImmOrOwnedObject((id, _, _)) => load_object(
1325 vm,
1326 state_view,
1327 linkage_view,
1328 new_packages,
1329 input_object_map,
1330 false,
1332 id,
1333 ),
1334 ObjectArg::SharedObject { id, mutable, .. } => load_object(
1335 vm,
1336 state_view,
1337 linkage_view,
1338 new_packages,
1339 input_object_map,
1340 !mutable,
1342 id,
1343 ),
1344 ObjectArg::Receiving((id, version, _)) => {
1345 Ok(InputValue::new_receiving_object(id, version))
1346 }
1347 }
1348 }
1349
1350 fn add_additional_write(
1352 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1353 owner: Owner,
1354 object_value: ObjectValue,
1355 ) -> Result<(), ExecutionError> {
1356 let ObjectValue {
1357 type_, contents, ..
1358 } = object_value;
1359 let bytes = match contents {
1360 ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
1361 ObjectContents::Raw(bytes) => bytes,
1362 };
1363 let object_id = MoveObject::id_opt(&bytes).map_err(|e| {
1364 ExecutionError::invariant_violation(format!("No id for Raw object bytes. {e}"))
1365 })?;
1366 let additional_write = AdditionalWrite {
1367 recipient: owner,
1368 type_,
1369 bytes,
1370 };
1371 additional_writes.insert(object_id, additional_write);
1372 Ok(())
1373 }
1374
1375 fn refund_max_gas_budget(
1379 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1380 gas_charger: &mut GasCharger,
1381 gas_id: ObjectID,
1382 ) -> Result<(), ExecutionError> {
1383 let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
1384 invariant_violation!("Gas object cannot be wrapped or destroyed")
1385 };
1386 let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
1387 invariant_violation!("Gas object must be a coin")
1388 };
1389 let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
1390 return Err(ExecutionError::new_with_source(
1391 ExecutionErrorKind::CoinBalanceOverflow,
1392 "Gas coin too large after returning the max gas budget",
1393 ));
1394 };
1395 coin.balance = Balance::new(new_balance);
1396 *bytes = coin.to_bcs_bytes();
1397 Ok(())
1398 }
1399
1400 fn create_written_object(
1402 vm: &MoveVM,
1403 linkage_view: &LinkageView,
1404 protocol_config: &ProtocolConfig,
1405 objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1406 id: ObjectID,
1407 type_: Type,
1408 contents: Vec<u8>,
1409 ) -> Result<MoveObject, ExecutionError> {
1410 debug_assert_eq!(
1411 id,
1412 MoveObject::id_opt(&contents).expect("object contents should start with an id")
1413 );
1414 let old_obj_ver = objects_modified_at
1415 .get(&id)
1416 .map(|obj: &LoadedRuntimeObject| obj.version);
1417
1418 let type_tag = vm
1419 .get_runtime()
1420 .get_type_tag(&type_)
1421 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1422
1423 let struct_tag = match type_tag {
1424 TypeTag::Struct(inner) => *inner,
1425 _ => invariant_violation!("Non struct type for object"),
1426 };
1427 MoveObject::new_from_execution(
1428 struct_tag.into(),
1429 old_obj_ver.unwrap_or_default(),
1430 contents,
1431 protocol_config,
1432 )
1433 }
1434
1435 pub(crate) struct IotaDataStore<'state, 'a> {
1443 linkage_view: &'a LinkageView<'state>,
1444 new_packages: &'a [MovePackage],
1445 }
1446
1447 impl<'state, 'a> IotaDataStore<'state, 'a> {
1448 pub(crate) fn new(
1449 linkage_view: &'a LinkageView<'state>,
1450 new_packages: &'a [MovePackage],
1451 ) -> Self {
1452 Self {
1453 linkage_view,
1454 new_packages,
1455 }
1456 }
1457
1458 fn get_module(&self, module_id: &ModuleId) -> Option<&Vec<u8>> {
1459 for package in self.new_packages {
1460 let module = package.get_module(module_id);
1461 if module.is_some() {
1462 return module;
1463 }
1464 }
1465 None
1466 }
1467 }
1468
1469 impl DataStore for IotaDataStore<'_, '_> {
1473 fn link_context(&self) -> AccountAddress {
1474 self.linkage_view.link_context()
1475 }
1476
1477 fn relocate(&self, module_id: &ModuleId) -> PartialVMResult<ModuleId> {
1478 self.linkage_view.relocate(module_id).map_err(|err| {
1479 PartialVMError::new(StatusCode::LINKER_ERROR)
1480 .with_message(format!("Error relocating {module_id}: {err:?}"))
1481 })
1482 }
1483
1484 fn defining_module(
1485 &self,
1486 runtime_id: &ModuleId,
1487 struct_: &IdentStr,
1488 ) -> PartialVMResult<ModuleId> {
1489 self.linkage_view
1490 .defining_module(runtime_id, struct_)
1491 .map_err(|err| {
1492 PartialVMError::new(StatusCode::LINKER_ERROR).with_message(format!(
1493 "Error finding defining module for {runtime_id}::{struct_}: {err:?}"
1494 ))
1495 })
1496 }
1497
1498 fn load_module(&self, module_id: &ModuleId) -> VMResult<Vec<u8>> {
1499 if let Some(bytes) = self.get_module(module_id) {
1500 return Ok(bytes.clone());
1501 }
1502 match self.linkage_view.get_module(module_id) {
1503 Ok(Some(bytes)) => Ok(bytes),
1504 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1505 .with_message(format!("Cannot find {module_id:?} in data cache"))
1506 .finish(Location::Undefined)),
1507 Err(err) => {
1508 let msg = format!("Unexpected storage error: {err:?}");
1509 Err(
1510 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1511 .with_message(msg)
1512 .finish(Location::Undefined),
1513 )
1514 }
1515 }
1516 }
1517
1518 fn publish_module(&mut self, _module_id: &ModuleId, _blob: Vec<u8>) -> VMResult<()> {
1519 Ok(())
1522 }
1523 }
1524}