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 programmable_transactions::argument_nodes::{
75 ArgumentNodeBudget, NodeCountError, count_nodes,
76 },
77 type_resolver::TypeTagResolver,
78 };
79
80 pub struct ExecutionContext<'vm, 'state, 'a> {
82 pub protocol_config: &'a ProtocolConfig,
84 pub metrics: Arc<LimitsMetrics>,
86 pub vm: &'vm MoveVM,
88 pub linkage_view: LinkageView<'state>,
90 pub native_extensions: NativeContextExtensions<'state>,
91 pub state_view: &'state dyn ExecutionState,
93 pub tx_context: Rc<RefCell<TxContext>>,
96 pub gas_charger: &'a mut GasCharger,
98 additional_transfers: Vec<(Owner, ObjectValue)>,
100 new_packages: Vec<MovePackage>,
102 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
104 gas: InputValue,
107 inputs: Vec<InputValue>,
110 results: Vec<Vec<ResultValue>>,
115 borrowed: HashMap<Arg, bool>,
119 argument_nodes: Option<ArgumentNodeBudget>,
123 }
124
125 struct AdditionalWrite {
128 recipient: Owner,
130 type_: Type,
132 bytes: Vec<u8>,
134 }
135
136 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
137 pub struct Arg(Arg_);
138
139 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
140 enum Arg_ {
141 V1(Argument),
142 V2(NormalizedArg),
143 }
144
145 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
146 enum NormalizedArg {
147 GasCoin,
148 Input(u16),
149 Result(u16, u16),
150 }
151
152 impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
153 #[instrument(name = "ExecutionContext::new", level = "trace", skip_all)]
160 pub fn new(
161 protocol_config: &'a ProtocolConfig,
162 metrics: Arc<LimitsMetrics>,
163 vm: &'vm MoveVM,
164 state_view: &'state dyn ExecutionState,
165 tx_context: Rc<RefCell<TxContext>>,
166 gas_charger: &'a mut GasCharger,
167 inputs: Vec<CallArg>,
168 ) -> Result<Self, ExecutionError>
169 where
170 'a: 'state,
171 {
172 let mut linkage_view = LinkageView::new(Box::new(CachedPackageStore::new(Box::new(
173 state_view.as_iota_resolver(),
174 ))));
175 let mut input_object_map = BTreeMap::new();
176 let inputs = inputs
177 .into_iter()
178 .map(|call_arg| {
179 load_call_arg(
180 vm,
181 state_view,
182 &mut linkage_view,
183 &[],
184 &mut input_object_map,
185 call_arg,
186 )
187 })
188 .collect::<Result<_, ExecutionError>>()?;
189 let gas = if let Some(gas_coin) = gas_charger.gas_coin() {
190 let mut gas = load_object(
191 vm,
192 state_view,
193 &mut linkage_view,
194 &[],
195 &mut input_object_map,
196 false,
198 gas_coin,
199 )?;
200 let Some(Value::Object(ObjectValue {
204 contents: ObjectContents::Coin(coin),
205 ..
206 })) = &mut gas.inner.value
207 else {
208 invariant_violation!("Gas object should be a populated coin")
209 };
210
211 let max_gas_in_balance = gas_charger.gas_budget();
212 let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else {
213 invariant_violation!(
214 "Transaction input checker should check that there is enough gas"
215 );
216 };
217 coin.balance = Balance::new(new_balance);
218 gas
219 } else {
220 InputValue {
221 object_metadata: None,
222 inner: ResultValue {
223 last_usage_kind: None,
224 value: None,
225 },
226 }
227 };
228 let native_extensions = new_native_extensions(
229 state_view.as_child_resolver(),
230 input_object_map,
231 !gas_charger.is_unmetered(),
232 protocol_config,
233 metrics.clone(),
234 tx_context.clone(),
235 state_view.read_auth_context(),
236 );
237
238 #[skip_checked_arithmetic]
240 move_vm_profiler::tracing_feature_enabled! {
241 use move_vm_profiler::GasProfiler;
242 use move_vm_types::gas::GasMeter;
243
244 let ref_context: &RefCell<TxContext> = tx_context.borrow();
245 let tx_digest = ref_context.borrow().digest();
246 let remaining_gas: u64 =
247 move_vm_types::gas::GasMeter::remaining_gas(&IotaGasMeter(gas_charger.move_gas_status_mut()))
248 .into();
249 IotaGasMeter(gas_charger.move_gas_status_mut())
250 .set_profiler(GasProfiler::init(
251 &vm.config().profiler_config,
252 format!("{tx_digest}"),
253 remaining_gas,
254 ));
255 }
256
257 let argument_nodes = ArgumentNodeBudget::new(
258 protocol_config.max_move_call_argument_nodes_total_as_option(),
259 gas_charger.is_unmetered(),
260 );
261
262 Ok(Self {
263 protocol_config,
264 metrics,
265 vm,
266 linkage_view,
267 native_extensions,
268 state_view,
269 tx_context,
270 gas_charger,
271 gas,
272 inputs,
273 results: vec![],
274 additional_transfers: vec![],
275 new_packages: vec![],
276 user_events: vec![],
277 borrowed: HashMap::new(),
278 argument_nodes,
279 })
280 }
281
282 pub fn new_call_argument_node_budget(&self) -> Option<ArgumentNodeBudget> {
286 ArgumentNodeBudget::new(
287 self.protocol_config
288 .max_move_call_argument_nodes_as_option(),
289 self.gas_charger.is_unmetered(),
290 )
291 }
292
293 pub fn count_argument_nodes(
298 &mut self,
299 call_budget: &mut Option<ArgumentNodeBudget>,
300 idx: usize,
301 param_ty: &Type,
302 bytes: &[u8],
303 ) -> Result<(), ExecutionError> {
304 let (limit_type, scope, budget) =
307 match (call_budget.as_ref(), self.argument_nodes.as_ref()) {
308 (None, None) => return Ok(()),
309 (Some(call), Some(tx)) if call.remaining() < tx.remaining() => {
310 ("call", "one call", call)
311 }
312 (Some(call), None) => ("call", "one call", call),
313 (_, Some(tx)) => ("transaction", "the transaction's calls", tx),
314 };
315 let (max_nodes, limit) = (budget.remaining(), budget.limit());
316 let layout = self
317 .vm
318 .get_runtime()
319 .type_to_fully_annotated_layout(param_ty)
320 .map_err(|e| self.convert_vm_error(e))?;
321 match count_nodes(bytes, &layout, max_nodes) {
322 Ok(nodes) => {
323 if let Some(call) = call_budget {
324 call.spend(nodes)?;
325 }
326 if let Some(tx) = &mut self.argument_nodes {
327 tx.spend(nodes)?;
328 }
329 Ok(())
330 }
331 Err(NodeCountError::Exceeded) => {
332 self.metrics
333 .excessive_move_call_argument_nodes
334 .with_label_values(&[limit_type])
335 .inc();
336 Err(ExecutionError::new_with_source(
337 ExecutionErrorKind::MovePrimitiveRuntimeError { location: None },
338 format!("arguments of {scope} hold more than {limit} values"),
339 ))
340 }
341 Err(NodeCountError::Malformed) => Err(command_argument_error(
342 CommandArgumentError::InvalidBcsBytes,
343 idx,
344 )),
345 }
346 }
347
348 pub fn record_call_argument_nodes(&self, call_budget: Option<ArgumentNodeBudget>) {
351 if let Some(budget) = call_budget {
352 self.metrics
353 .move_call_argument_nodes
354 .observe(budget.used() as f64);
355 }
356 }
357
358 pub fn object_runtime(&self) -> Result<&ObjectRuntime<'_>, ExecutionError> {
359 self.native_extensions
360 .get::<ObjectRuntime>()
361 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
362 }
363
364 pub fn fresh_id(&mut self) -> Result<ObjectId, ExecutionError> {
366 let object_id = self.tx_context.borrow_mut().fresh_id();
367 self.record_new_uid(object_id)?;
368 Ok(object_id)
369 }
370
371 pub(crate) fn record_new_uid(&mut self, object_id: ObjectId) -> Result<(), ExecutionError> {
373 self.native_extensions
374 .get_mut()
375 .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.new_id(object_id))
376 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
377 Ok(())
378 }
379
380 pub(crate) fn package_derived_metadata_id(
382 &mut self,
383 package_storage_id: ObjectId,
384 ) -> Result<ObjectId, ExecutionError> {
385 let object_id = derive_package_metadata_id(package_storage_id);
386 self.record_new_uid(object_id)?;
387 Ok(object_id)
388 }
389
390 pub fn delete_id(&mut self, object_id: ObjectId) -> Result<(), ExecutionError> {
392 self.native_extensions
393 .get_mut()
394 .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.delete_id(object_id))
395 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
396 }
397
398 pub fn set_link_context(
402 &mut self,
403 package_id: ObjectId,
404 ) -> Result<AccountAddress, ExecutionError> {
405 if self.linkage_view.has_linkage(package_id)? {
406 return Ok(self
408 .linkage_view
409 .original_package_id()?
410 .unwrap_or(AccountAddress::new(package_id.into_bytes())));
411 }
412
413 let package = package_for_linkage(&self.linkage_view, package_id)
414 .map_err(|e| self.convert_vm_error(e))?;
415
416 self.linkage_view.set_linkage(&package)
417 }
418
419 pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
421 load_type(self.vm, &self.linkage_view, &self.new_packages, type_tag)
422 }
423
424 pub fn load_type_from_struct(&mut self, struct_tag: &StructTag) -> VMResult<Type> {
426 load_type_from_struct(self.vm, &self.linkage_view, &self.new_packages, struct_tag)
427 }
428
429 pub fn get_type_abilities(&self, t: &Type) -> Result<AbilitySet, ExecutionError> {
430 self.vm
431 .get_runtime()
432 .get_type_abilities(t)
433 .map_err(|e| self.convert_vm_error(e))
434 }
435
436 pub fn take_user_events(
439 &mut self,
440 module_id: &ModuleId,
441 function: FunctionDefinitionIndex,
442 last_offset: CodeOffset,
443 ) -> Result<(), ExecutionError> {
444 let events = self
445 .native_extensions
446 .get_mut()
447 .map(|object_runtime: &mut ObjectRuntime| object_runtime.take_user_events())
448 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
449 let num_events = self.user_events.len() + events.len();
450 let max_events = self.protocol_config.max_num_event_emit();
451 if num_events as u64 > max_events {
452 let err = max_event_error(max_events)
453 .at_code_offset(function, last_offset)
454 .finish(Location::Module(module_id.clone()));
455 return Err(self.convert_vm_error(err));
456 }
457 let new_events = events
458 .into_iter()
459 .map(|(ty, tag, value)| {
460 let layout = self
461 .vm
462 .get_runtime()
463 .type_to_type_layout(&ty)
464 .map_err(|e| self.convert_vm_error(e))?;
465 let Some(bytes) = value.simple_serialize(&layout) else {
466 invariant_violation!("Failed to deserialize already serialized Move value");
467 };
468 Ok((module_id.clone(), struct_tag_core_to_sdk(&tag), bytes))
469 })
470 .collect::<Result<Vec<_>, ExecutionError>>()?;
471 self.user_events.extend(new_events);
472 Ok(())
473 }
474
475 pub fn splat_args<Items: IntoIterator<Item = Argument>>(
481 &self,
482 start_idx: usize,
483 args: Items,
484 ) -> Result<Vec<Arg>, ExecutionError>
485 where
486 Items::IntoIter: ExactSizeIterator,
487 {
488 if !self.protocol_config.normalize_ptb_arguments() {
489 Ok(args.into_iter().map(|arg| Arg(Arg_::V1(arg))).collect())
490 } else {
491 let args = args.into_iter();
492 let _args_len = args.len();
493 let mut res = vec![];
494 for (arg_idx, arg) in args.enumerate() {
495 self.splat_arg(&mut res, arg)
496 .map_err(|e| e.into_execution_error(start_idx + arg_idx))?;
497 }
498 debug_assert_eq!(res.len(), _args_len);
499 Ok(res)
500 }
501 }
502
503 fn splat_arg(&self, res: &mut Vec<Arg>, arg: Argument) -> Result<(), EitherError> {
504 match arg {
505 Argument::Gas => res.push(Arg(Arg_::V2(NormalizedArg::GasCoin))),
506 Argument::Input(i) => {
507 if i as usize >= self.inputs.len() {
508 return Err(CommandArgumentError::IndexOutOfBounds { index: i }.into());
509 }
510 res.push(Arg(Arg_::V2(NormalizedArg::Input(i))))
511 }
512 Argument::NestedResult(i, j) => {
513 let Some(command_result) = self.results.get(i as usize) else {
514 return Err(CommandArgumentError::IndexOutOfBounds { index: i }.into());
515 };
516 if j as usize >= command_result.len() {
517 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
518 result: i,
519 subresult: j,
520 }
521 .into());
522 };
523 res.push(Arg(Arg_::V2(NormalizedArg::Result(i, j))))
524 }
525 Argument::Result(i) => {
526 let Some(result) = self.results.get(i as usize) else {
527 return Err(CommandArgumentError::IndexOutOfBounds { index: i }.into());
528 };
529 let Ok(len): Result<u16, _> = result.len().try_into() else {
530 invariant_violation!("Result of length greater than u16::MAX");
531 };
532 if len != 1 {
533 return Err(CommandArgumentError::InvalidResultArity { result: i }.into());
535 }
536 res.extend((0..len).map(|j| Arg(Arg_::V2(NormalizedArg::Result(i, j)))))
537 }
538 _ => {
539 unimplemented!("a new Argument enum variant was added and needs to be handled")
540 }
541 }
542 Ok(())
543 }
544
545 pub fn one_arg(
546 &self,
547 command_arg_idx: usize,
548 arg: Argument,
549 ) -> Result<Arg, ExecutionError> {
550 let args = self.splat_args(command_arg_idx, vec![arg])?;
551 let Ok([arg]): Result<[Arg; 1], _> = args.try_into() else {
552 return Err(command_argument_error(
553 CommandArgumentError::InvalidArgumentArity,
554 command_arg_idx,
555 ));
556 };
557 Ok(arg)
558 }
559
560 pub(crate) fn add_pure_input(
567 &mut self,
568 bytes: Vec<u8>,
569 ) -> Result<Argument, ExecutionError> {
570 let Ok(index) = u16::try_from(self.inputs.len()) else {
571 invariant_violation!("too many inputs to register an additional pure input");
572 };
573 self.inputs
574 .push(InputValue::new_raw(RawValueType::Any, bytes));
575 Ok(Argument::Input(index))
576 }
577
578 pub(crate) fn num_inputs(&self) -> usize {
582 self.inputs.len()
583 }
584
585 pub(crate) fn truncate_inputs(&mut self, len: usize) {
589 self.inputs.truncate(len);
590 }
591
592 pub fn by_value_arg<V: TryFromValue>(
598 &mut self,
599 command_kind: CommandKind,
600 arg_idx: usize,
601 arg: Arg,
602 ) -> Result<V, ExecutionError> {
603 self.by_value_arg_(command_kind, arg)
604 .map_err(|e| e.into_execution_error(arg_idx))
605 }
606 fn by_value_arg_<V: TryFromValue>(
607 &mut self,
608 command_kind: CommandKind,
609 arg: Arg,
610 ) -> Result<V, EitherError> {
611 let is_borrowed = self.arg_is_borrowed(&arg);
612 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::ByValue)?;
613 let is_copyable = if let Some(val) = val_opt {
614 val.is_copyable()
615 } else {
616 return Err(CommandArgumentError::InvalidValueUsage.into());
617 };
618 if !is_copyable && is_borrowed {
624 return Err(CommandArgumentError::InvalidValueUsage.into());
625 }
626 if arg.is_gas_coin() && !matches!(command_kind, CommandKind::TransferObjects) {
628 return Err(CommandArgumentError::InvalidGasCoinUsage.into());
629 }
630 if matches!(
632 input_metadata_opt,
633 Some(InputObjectMetadata::InputObject {
634 owner: Owner::Immutable,
635 ..
636 })
637 ) {
638 return Err(CommandArgumentError::InvalidObjectByValue.into());
639 }
640
641 if matches!(
643 input_metadata_opt,
644 Some(InputObjectMetadata::InputObject {
645 is_mutable_input: false,
646 ..
647 })
648 ) {
649 return Err(CommandArgumentError::InvalidObjectByValue.into());
650 }
651
652 let val = if is_copyable {
653 val_opt.as_ref().unwrap().clone()
654 } else {
655 val_opt.take().unwrap()
656 };
657 Ok(V::try_from_value(val)?)
658 }
659
660 pub fn borrow_arg_mut<V: TryFromValue>(
667 &mut self,
668 arg_idx: usize,
669 arg: Arg,
670 ) -> Result<V, ExecutionError> {
671 self.borrow_arg_mut_(arg)
672 .map_err(|e| e.into_execution_error(arg_idx))
673 }
674 fn borrow_arg_mut_<V: TryFromValue>(&mut self, arg: Arg) -> Result<V, EitherError> {
675 if self.arg_is_borrowed(&arg) {
677 return Err(CommandArgumentError::InvalidValueUsage.into());
678 }
679 self.borrowed.insert(arg, true);
680 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowMut)?;
681 let is_copyable = if let Some(val) = val_opt {
682 val.is_copyable()
683 } else {
684 return Err(CommandArgumentError::InvalidValueUsage.into());
686 };
687 if let Some(InputObjectMetadata::InputObject {
688 is_mutable_input: false,
689 ..
690 }) = input_metadata_opt
691 {
692 return Err(CommandArgumentError::InvalidObjectByMutRef.into());
693 }
694 let val = if is_copyable {
697 val_opt.as_ref().unwrap().clone()
698 } else {
699 val_opt.take().unwrap()
700 };
701 Ok(V::try_from_value(val)?)
702 }
703
704 pub fn borrow_arg<V: TryFromValue>(
709 &mut self,
710 arg_idx: usize,
711 arg: Arg,
712 type_: &Type,
713 ) -> Result<V, ExecutionError> {
714 self.borrow_arg_(arg, type_)
715 .map_err(|e| e.into_execution_error(arg_idx))
716 }
717 fn borrow_arg_<V: TryFromValue>(
718 &mut self,
719 arg: Arg,
720 arg_type: &Type,
721 ) -> Result<V, EitherError> {
722 if self.arg_is_mut_borrowed(&arg) {
726 return Err(CommandArgumentError::InvalidValueUsage.into());
727 }
728 self.borrowed.insert(arg, false);
729 let (_input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowImm)?;
730 if val_opt.is_none() {
731 return Err(CommandArgumentError::InvalidValueUsage.into());
732 }
733
734 if let &mut Some(Value::Receiving(_, _, ref mut recv_arg_type @ None)) = val_opt {
736 let Type::Reference(inner) = arg_type else {
737 return Err(CommandArgumentError::InvalidValueUsage.into());
738 };
739 *recv_arg_type = Some(*(*inner).clone());
740 }
741
742 Ok(V::try_from_value(val_opt.as_ref().unwrap().clone())?)
743 }
744
745 pub fn restore_arg<Mode: ExecutionMode>(
747 &mut self,
748 updates: &mut Mode::ArgumentUpdates,
749 arg: Arg,
750 value: Value,
751 ) -> Result<(), ExecutionError> {
752 Mode::add_argument_update(self, updates, arg.into(), &value)?;
753 let was_mut_opt = self.borrowed.remove(&arg);
754 assert_invariant!(
755 was_mut_opt.is_some() && was_mut_opt.unwrap(),
756 "Should never restore a non-mut borrowed value. \
757 The take+restore is an implementation detail of mutable references"
758 );
759 let Ok((_, value_opt)) = self.borrow_mut_impl(arg, None) else {
761 invariant_violation!("Should be able to borrow argument to restore it")
762 };
763
764 let old_value = value_opt.replace(value);
765 assert_invariant!(
766 old_value.is_none() || old_value.unwrap().is_copyable(),
767 "Should never restore a non-taken value, unless it is copyable. \
768 The take+restore is an implementation detail of mutable references"
769 );
770
771 Ok(())
772 }
773
774 pub fn transfer_object(
776 &mut self,
777 obj: ObjectValue,
778 addr: Address,
779 ) -> Result<(), ExecutionError> {
780 self.additional_transfers.push((Owner::Address(addr), obj));
781 Ok(())
782 }
783
784 pub fn freeze_object(&mut self, obj: ObjectValue) -> Result<(), ExecutionError> {
786 self.additional_transfers.push((Owner::Immutable, obj));
787 Ok(())
788 }
789
790 pub fn new_package<'p>(
792 &self,
793 modules: &[CompiledModule],
794 dependencies: impl IntoIterator<Item = &'p MovePackage>,
795 ) -> Result<MovePackage, ExecutionError> {
796 MovePackage::new_initial(modules, self.protocol_config, dependencies)
797 }
798
799 pub fn upgrade_package<'p>(
802 &self,
803 storage_id: ObjectId,
804 previous_package: &MovePackage,
805 new_modules: &[CompiledModule],
806 dependencies: impl IntoIterator<Item = &'p MovePackage>,
807 ) -> Result<MovePackage, ExecutionError> {
808 previous_package.new_upgraded(
809 storage_id,
810 new_modules,
811 self.protocol_config,
812 dependencies,
813 )
814 }
815
816 pub fn write_package(&mut self, package: MovePackage) {
818 self.new_packages.push(package);
819 }
820
821 pub fn pop_package(&mut self) -> Option<MovePackage> {
827 self.new_packages.pop()
828 }
829
830 pub fn push_command_results(&mut self, results: Vec<Value>) -> Result<(), ExecutionError> {
833 assert_invariant!(
834 self.borrowed.values().all(|is_mut| !is_mut),
835 "all mut borrows should be restored"
836 );
837 self.borrowed = HashMap::new();
839 self.results
840 .push(results.into_iter().map(ResultValue::new).collect());
841 Ok(())
842 }
843
844 pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
846 let Self {
847 protocol_config,
848 vm,
849 linkage_view,
850 mut native_extensions,
851 tx_context,
852 gas_charger,
853 additional_transfers,
854 new_packages,
855 gas,
856 inputs,
857 results,
858 user_events,
859 state_view,
860 ..
861 } = self;
862 let ref_context: &RefCell<TxContext> = tx_context.borrow();
863 let tx_digest = ref_context.borrow().digest();
864
865 let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
866 let mut loaded_runtime_objects = BTreeMap::new();
867 let mut additional_writes = BTreeMap::new();
868 let mut by_value_shared_objects = BTreeSet::new();
869 for input in inputs.into_iter().chain(std::iter::once(gas)) {
870 let InputValue {
871 object_metadata:
872 Some(InputObjectMetadata::InputObject {
873 is_mutable_input: true,
875 id,
876 version,
877 owner,
878 }),
879 inner: ResultValue { value, .. },
880 } = input
881 else {
882 continue;
883 };
884 loaded_runtime_objects.insert(
885 id,
886 LoadedRuntimeObject {
887 version,
888 is_modified: true,
889 },
890 );
891 if let Some(Value::Object(object_value)) = value {
892 add_additional_write(&mut additional_writes, owner, object_value)?;
893 } else if owner.is_shared() {
894 by_value_shared_objects.insert(id);
895 }
896 }
897 if !Mode::allow_arbitrary_values() {
900 for (i, command_result) in results.iter().enumerate() {
901 for (j, result_value) in command_result.iter().enumerate() {
902 let ResultValue {
903 last_usage_kind,
904 value,
905 } = result_value;
906 match value {
907 None => (),
908 Some(Value::Object(_)) => {
909 return Err(ExecutionErrorKind::UnusedValueWithoutDrop {
910 result: i as u16,
911 subresult: j as u16,
912 }
913 .into());
914 }
915 Some(Value::Raw(RawValueType::Any, _)) => (),
916 Some(Value::Raw(RawValueType::Loaded { abilities, .. }, _)) => {
917 if abilities.has_drop()
923 || (abilities.has_copy()
924 && matches!(last_usage_kind, Some(UsageKind::ByValue)))
925 {
926 } else {
927 let msg = if abilities.has_copy() {
928 "The value has copy, but not drop. \
929 Its last usage must be by-value so it can be taken."
930 } else {
931 "Unused value without drop"
932 };
933 return Err(ExecutionError::new_with_source(
934 ExecutionErrorKind::UnusedValueWithoutDrop {
935 result: i as u16,
936 subresult: j as u16,
937 },
938 msg,
939 ));
940 }
941 }
942 Some(Value::Receiving(_, _, _)) => (),
944 }
945 }
946 }
947 }
948 for (owner, object_value) in additional_transfers {
950 add_additional_write(&mut additional_writes, owner, object_value)?;
951 }
952 if let Some(gas_id) = gas_id_opt {
954 refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
955 }
956
957 let object_runtime: ObjectRuntime = native_extensions
958 .remove()
959 .map_err(|e| convert_vm_error(e.finish(Location::Undefined), vm, &linkage_view))?;
960
961 let RuntimeResults {
962 writes,
963 user_events: remaining_events,
964 loaded_child_objects,
965 mut created_object_ids,
966 deleted_object_ids,
967 received_coin_types,
968 } = object_runtime.finish()?;
969 assert_invariant!(
970 remaining_events.is_empty(),
971 "Events should be taken after every Move call"
972 );
973
974 loaded_runtime_objects.extend(loaded_child_objects);
975
976 let mut written_objects = BTreeMap::new();
977 for package in new_packages {
978 let package_obj = Object::new_from_package(package, tx_digest);
979 let id = package_obj.id();
980 created_object_ids.insert(id);
981 written_objects.insert(id, package_obj);
982 }
983 for (id, additional_write) in additional_writes {
984 let AdditionalWrite {
985 recipient,
986 type_,
987 bytes,
988 } = additional_write;
989
990 let move_object = {
991 create_written_object::<Mode>(
992 vm,
993 &linkage_view,
994 protocol_config,
995 &loaded_runtime_objects,
996 id,
997 type_,
998 bytes,
999 )?
1000 };
1001 let object = Object::new_move(move_object, recipient, tx_digest);
1002 written_objects.insert(id, object);
1003 if let Some(loaded) = loaded_runtime_objects.get_mut(&id) {
1004 loaded.is_modified = true;
1005 }
1006 }
1007
1008 for (id, (recipient, ty, value)) in writes {
1009 let layout = vm
1010 .get_runtime()
1011 .type_to_type_layout(&ty)
1012 .map_err(|e| convert_vm_error(e, vm, &linkage_view))?;
1013 let Some(bytes) = value.simple_serialize(&layout) else {
1014 invariant_violation!("Failed to deserialize already serialized Move value");
1015 };
1016 let move_object = {
1017 create_written_object::<Mode>(
1018 vm,
1019 &linkage_view,
1020 protocol_config,
1021 &loaded_runtime_objects,
1022 id,
1023 ty,
1024 bytes,
1025 )?
1026 };
1027 let object = Object::new_move(move_object, recipient, tx_digest);
1028 written_objects.insert(id, object);
1029 }
1030
1031 let results = finish(
1032 protocol_config,
1033 state_view,
1034 gas_charger,
1035 &ref_context.borrow(),
1036 &by_value_shared_objects,
1037 loaded_runtime_objects,
1038 written_objects,
1039 created_object_ids,
1040 deleted_object_ids,
1041 user_events,
1042 received_coin_types,
1043 );
1044 results
1045 }
1046
1047 pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
1049 crate::error::convert_vm_error(error, self.vm, &self.linkage_view)
1050 }
1051
1052 pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
1054 use iota_sdk_types::TypeArgumentError;
1055 use move_core_types::vm_status::StatusCode;
1056 match error.major_status() {
1057 StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
1058 ExecutionErrorKind::TypeArityMismatch.into()
1059 }
1060 StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
1061 type_argument: idx as TypeParameterIndex,
1062 kind: TypeArgumentError::TypeNotFound,
1063 }
1064 .into(),
1065 StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
1066 type_argument: idx as TypeParameterIndex,
1067 kind: TypeArgumentError::ConstraintNotSatisfied,
1068 }
1069 .into(),
1070 _ => self.convert_vm_error(error),
1071 }
1072 }
1073
1074 fn arg_is_borrowed(&self, arg: &Arg) -> bool {
1077 self.borrowed.contains_key(arg)
1078 }
1079
1080 fn arg_is_mut_borrowed(&self, arg: &Arg) -> bool {
1083 matches!(self.borrowed.get(arg), Some(true))
1084 }
1085
1086 fn borrow_mut(
1089 &mut self,
1090 arg: Arg,
1091 usage: UsageKind,
1092 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), EitherError> {
1093 self.borrow_mut_impl(arg, Some(usage))
1094 }
1095
1096 fn borrow_mut_impl(
1099 &mut self,
1100 arg: Arg,
1101 update_last_usage: Option<UsageKind>,
1102 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), EitherError> {
1103 match arg.0 {
1104 Arg_::V1(arg) => {
1105 assert_invariant!(
1106 !self.protocol_config.normalize_ptb_arguments(),
1107 "Should not be using v1 args with normalized args"
1108 );
1109 Ok(self.borrow_mut_impl_v1(arg, update_last_usage)?)
1110 }
1111 Arg_::V2(arg) => {
1112 assert_invariant!(
1113 self.protocol_config.normalize_ptb_arguments(),
1114 "Should be using only v2 args with normalized args"
1115 );
1116 Ok(self.borrow_mut_impl_v2(arg, update_last_usage)?)
1117 }
1118 }
1119 }
1120
1121 fn borrow_mut_impl_v1(
1123 &mut self,
1124 arg: Argument,
1125 update_last_usage: Option<UsageKind>,
1126 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
1127 {
1128 let (metadata, result_value) = match arg {
1129 Argument::Gas => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1130 Argument::Input(i) => {
1131 let Some(input_value) = self.inputs.get_mut(i as usize) else {
1132 return Err(CommandArgumentError::IndexOutOfBounds { index: i });
1133 };
1134 (input_value.object_metadata.as_ref(), &mut input_value.inner)
1135 }
1136 Argument::Result(i) => {
1137 let Some(command_result) = self.results.get_mut(i as usize) else {
1138 return Err(CommandArgumentError::IndexOutOfBounds { index: i });
1139 };
1140 if command_result.len() != 1 {
1141 return Err(CommandArgumentError::InvalidResultArity { result: i });
1142 }
1143 (None, &mut command_result[0])
1144 }
1145 Argument::NestedResult(i, j) => {
1146 let Some(command_result) = self.results.get_mut(i as usize) else {
1147 return Err(CommandArgumentError::IndexOutOfBounds { index: i });
1148 };
1149 let Some(result_value) = command_result.get_mut(j as usize) else {
1150 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
1151 result: i,
1152 subresult: j,
1153 });
1154 };
1155 (None, result_value)
1156 }
1157 _ => {
1158 unimplemented!("a new Argument enum variant was added and needs to be handled")
1159 }
1160 };
1161 if let Some(usage) = update_last_usage {
1162 result_value.last_usage_kind = Some(usage);
1163 }
1164 Ok((metadata, &mut result_value.value))
1165 }
1166
1167 fn borrow_mut_impl_v2(
1169 &mut self,
1170 arg: NormalizedArg,
1171 update_last_usage: Option<UsageKind>,
1172 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), ExecutionError> {
1173 let (metadata, result_value) = match arg {
1174 NormalizedArg::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1175 NormalizedArg::Input(i) => {
1176 let input_value = self
1177 .inputs
1178 .get_mut(i as usize)
1179 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1180 (input_value.object_metadata.as_ref(), &mut input_value.inner)
1181 }
1182 NormalizedArg::Result(i, j) => {
1183 let result_value = self
1184 .results
1185 .get_mut(i as usize)
1186 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?
1187 .get_mut(j as usize)
1188 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1189 (None, result_value)
1190 }
1191 };
1192 if let Some(usage) = update_last_usage {
1193 result_value.last_usage_kind = Some(usage);
1194 }
1195 Ok((metadata, &mut result_value.value))
1196 }
1197
1198 pub(crate) fn execute_function_bypass_visibility(
1203 &mut self,
1204 module: &ModuleId,
1205 function_name: &IdentStr,
1206 ty_args: Vec<Type>,
1207 args: Vec<impl Borrow<[u8]>>,
1208 tracer: &mut Option<MoveTraceBuilder>,
1209 ) -> VMResult<SerializedReturnValues> {
1210 let gas_status = self.gas_charger.move_gas_status_mut();
1211 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1212 self.vm.get_runtime().execute_function_bypass_visibility(
1213 module,
1214 function_name,
1215 ty_args,
1216 args,
1217 &mut data_store,
1218 &mut IotaGasMeter(gas_status),
1219 &mut self.native_extensions,
1220 tracer.as_mut(),
1221 )
1222 }
1223
1224 pub(crate) fn load_function(
1229 &mut self,
1230 module_id: &ModuleId,
1231 function_name: &IdentStr,
1232 type_arguments: &[Type],
1233 ) -> VMResult<LoadedFunctionInstantiation> {
1234 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1235 self.vm.get_runtime().load_function(
1236 module_id,
1237 function_name,
1238 type_arguments,
1239 &mut data_store,
1240 )
1241 }
1242
1243 pub(crate) fn make_object_value(
1248 &mut self,
1249 type_: StructTag,
1250 used_in_non_entry_move_call: bool,
1251 contents: &[u8],
1252 ) -> Result<ObjectValue, ExecutionError> {
1253 make_object_value(
1254 self.vm,
1255 &mut self.linkage_view,
1256 &self.new_packages,
1257 type_,
1258 used_in_non_entry_move_call,
1259 contents,
1260 )
1261 }
1262
1263 pub fn publish_module_bundle(
1268 &mut self,
1269 modules: Vec<Vec<u8>>,
1270 sender: AccountAddress,
1271 ) -> VMResult<()> {
1272 let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1275 self.vm.get_runtime().publish_module_bundle(
1276 modules,
1277 sender,
1278 &mut data_store,
1279 &mut IotaGasMeter(self.gas_charger.move_gas_status_mut()),
1280 )
1281 }
1282
1283 pub fn size_bound_raw(&self, bound: u64) -> SizeBound {
1284 if self.protocol_config.max_ptb_value_size_v2() {
1285 SizeBound::Raw(bound)
1286 } else {
1287 SizeBound::Object(bound)
1288 }
1289 }
1290
1291 pub fn size_bound_vector_elem(&self, bound: u64) -> SizeBound {
1292 if self.protocol_config.max_ptb_value_size_v2() {
1293 SizeBound::VectorElem(bound)
1294 } else {
1295 SizeBound::Object(bound)
1296 }
1297 }
1298 }
1299
1300 impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
1301 fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
1304 self.vm
1305 .get_runtime()
1306 .get_type_tag(type_)
1307 .map_err(|e| self.convert_vm_error(e))
1308 .map(|tt| type_tag_core_to_sdk(&tt))
1309 }
1310 }
1311
1312 fn package_for_linkage(
1316 package_store: &dyn PackageStore,
1317 package_id: ObjectId,
1318 ) -> VMResult<Rc<MovePackage>> {
1319 use move_binary_format::errors::PartialVMError;
1320 use move_core_types::vm_status::StatusCode;
1321
1322 match package_store.get_package(&package_id) {
1323 Ok(Some(package)) => Ok(package),
1324 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1325 .with_message(format!("Cannot find link context {package_id} in store"))
1326 .finish(Location::Undefined)),
1327 Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1328 .with_message(format!(
1329 "Error loading link context {package_id} from store: {err}"
1330 ))
1331 .finish(Location::Undefined)),
1332 }
1333 }
1334
1335 pub fn finish(
1341 protocol_config: &ProtocolConfig,
1342 state_view: &dyn ExecutionState,
1343 gas_charger: &mut GasCharger,
1344 tx_context: &TxContext,
1345 by_value_shared_objects: &BTreeSet<ObjectId>,
1346 loaded_runtime_objects: BTreeMap<ObjectId, LoadedRuntimeObject>,
1347 written_objects: BTreeMap<ObjectId, Object>,
1348 created_object_ids: IndexSet<ObjectId>,
1349 deleted_object_ids: IndexSet<ObjectId>,
1350 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
1351 received_coin_types: BTreeSet<String>,
1352 ) -> Result<ExecutionResults, ExecutionError> {
1353 for id in by_value_shared_objects {
1360 if let Some(obj) = written_objects.get(id) {
1363 if !obj.is_shared() {
1364 return Err(ExecutionError::new(
1365 ExecutionErrorKind::SharedObjectOperationNotAllowed,
1366 Some(
1367 format!(
1368 "Shared object operation on {id} not allowed: \
1369 cannot be frozen, transferred, or wrapped"
1370 )
1371 .into(),
1372 ),
1373 ));
1374 }
1375 } else {
1376 if !deleted_object_ids.contains(id) {
1379 return Err(ExecutionError::new(
1380 ExecutionErrorKind::SharedObjectOperationNotAllowed,
1381 Some(
1382 format!(
1383 "Shared object operation on {id} not allowed: \
1384 shared objects used by value must be re-shared if not deleted"
1385 )
1386 .into(),
1387 ),
1388 ));
1389 }
1390 }
1391 }
1392
1393 let DenyListResult {
1394 result,
1395 num_non_gas_coin_owners,
1396 } = state_view.check_coin_deny_list(&written_objects);
1397 gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
1398 result?;
1399
1400 if protocol_config.enable_pcool_flow() {
1407 state_view.check_received_coin_deny_list(tx_context.sender(), received_coin_types)?;
1408 }
1409
1410 let user_events = user_events
1411 .into_iter()
1412 .map(|(module_id, tag, contents)| {
1413 let package_id = ObjectId::new(module_id.address().into_bytes());
1414 let module = identifier_core_to_sdk(module_id.name());
1415 let sender = tx_context.sender();
1416 Event {
1417 package_id,
1418 module,
1419 sender,
1420 struct_tag: tag,
1421 contents,
1422 }
1423 })
1424 .collect();
1425
1426 Ok(ExecutionResults::V1(ExecutionResultsV1 {
1427 written_objects,
1428 modified_objects: loaded_runtime_objects
1429 .into_iter()
1430 .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
1431 .collect(),
1432 created_object_ids: created_object_ids.into_iter().collect(),
1433 deleted_object_ids: deleted_object_ids.into_iter().collect(),
1434 user_events,
1435 }))
1436 }
1437
1438 pub fn load_type_from_struct(
1439 vm: &MoveVM,
1440 linkage_view: &LinkageView,
1441 new_packages: &[MovePackage],
1442 struct_tag: &StructTag,
1443 ) -> VMResult<Type> {
1444 fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1445 Err(PartialVMError::new(code).finish(Location::Undefined))
1446 }
1447
1448 let defining_id = struct_tag.address().into();
1450 let package = package_for_linkage(linkage_view, defining_id)?;
1451
1452 let original_address = linkage_view.set_linkage(&package).map_err(|e| {
1455 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1456 .with_message(e.to_string())
1457 .finish(Location::Undefined)
1458 })?;
1459
1460 let runtime_id = ModuleId::new(
1461 original_address,
1462 identifier_sdk_to_core(struct_tag.module()),
1463 );
1464 let data_store = IotaDataStore::new(linkage_view, new_packages);
1465 let res = vm.get_runtime().load_type(
1466 &runtime_id,
1467 &identifier_sdk_to_core(struct_tag.name()),
1468 &data_store,
1469 );
1470 linkage_view.reset_linkage().map_err(|e| {
1471 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1472 .with_message(e.to_string())
1473 .finish(Location::Undefined)
1474 })?;
1475 let (idx, struct_type) = res?;
1476
1477 let type_param_constraints = struct_type.type_param_constraints();
1479 if type_param_constraints.len() != struct_tag.type_params().len() {
1480 return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1481 }
1482
1483 if struct_tag.type_params().is_empty() {
1484 Ok(Type::Datatype(idx))
1485 } else {
1486 let loaded_type_params = struct_tag
1487 .type_params()
1488 .iter()
1489 .map(|type_param| load_type(vm, linkage_view, new_packages, type_param))
1490 .collect::<VMResult<Vec<_>>>()?;
1491
1492 for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1494 let abilities = vm.get_runtime().get_type_abilities(param)?;
1495 if !constraint.is_subset(abilities) {
1496 return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1497 }
1498 }
1499
1500 Ok(Type::DatatypeInstantiation(Box::new((
1501 idx,
1502 loaded_type_params,
1503 ))))
1504 }
1505 }
1506
1507 pub fn load_type(
1511 vm: &MoveVM,
1512 linkage_view: &LinkageView,
1513 new_packages: &[MovePackage],
1514 type_tag: &TypeTag,
1515 ) -> VMResult<Type> {
1516 Ok(match type_tag {
1517 TypeTag::Bool => Type::Bool,
1518 TypeTag::U8 => Type::U8,
1519 TypeTag::U16 => Type::U16,
1520 TypeTag::U32 => Type::U32,
1521 TypeTag::U64 => Type::U64,
1522 TypeTag::U128 => Type::U128,
1523 TypeTag::U256 => Type::U256,
1524 TypeTag::Address => Type::Address,
1525 TypeTag::Signer => Type::Signer,
1526
1527 TypeTag::Vector(inner) => {
1528 Type::Vector(Box::new(load_type(vm, linkage_view, new_packages, inner)?))
1529 }
1530 TypeTag::Struct(struct_tag) => {
1531 return load_type_from_struct(vm, linkage_view, new_packages, struct_tag);
1532 }
1533 })
1534 }
1535
1536 pub(crate) fn make_object_value(
1543 vm: &MoveVM,
1544 linkage_view: &mut LinkageView,
1545 new_packages: &[MovePackage],
1546 type_: StructTag,
1547 used_in_non_entry_move_call: bool,
1548 contents: &[u8],
1549 ) -> Result<ObjectValue, ExecutionError> {
1550 let contents = if type_.is_coin() {
1551 let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1552 invariant_violation!("Could not deserialize a coin")
1553 };
1554 ObjectContents::Coin(coin)
1555 } else {
1556 ObjectContents::Raw(contents.to_vec())
1557 };
1558
1559 let type_ = load_type_from_struct(vm, linkage_view, new_packages, &type_)
1560 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1561 let abilities = vm
1562 .get_runtime()
1563 .get_type_abilities(&type_)
1564 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1565 let has_public_transfer = abilities.has_store();
1566 Ok(ObjectValue {
1567 type_,
1568 has_public_transfer,
1569 used_in_non_entry_move_call,
1570 contents,
1571 })
1572 }
1573
1574 impl Arg {
1575 fn is_gas_coin(&self) -> bool {
1576 match self {
1578 Arg(Arg_::V1(a)) => matches!(a, Argument::Gas),
1579 Arg(Arg_::V2(n)) => matches!(n, NormalizedArg::GasCoin),
1580 }
1581 }
1582 }
1583
1584 impl From<Arg> for Argument {
1585 fn from(arg: Arg) -> Self {
1586 match arg.0 {
1587 Arg_::V1(a) => a,
1588 Arg_::V2(normalized) => match normalized {
1589 NormalizedArg::GasCoin => Argument::Gas,
1590 NormalizedArg::Input(i) => Argument::Input(i),
1591 NormalizedArg::Result(i, j) => Argument::NestedResult(i, j),
1592 },
1593 }
1594 }
1595 }
1596
1597 pub(crate) fn value_from_object(
1602 vm: &MoveVM,
1603 linkage_view: &mut LinkageView,
1604 new_packages: &[MovePackage],
1605 object: &Object,
1606 ) -> Result<ObjectValue, ExecutionError> {
1607 let ObjectInner {
1608 data: ObjectData::Struct(object),
1609 ..
1610 } = object.as_inner()
1611 else {
1612 invariant_violation!("Expected a Move object");
1613 };
1614
1615 let used_in_non_entry_move_call = false;
1616 make_object_value(
1617 vm,
1618 linkage_view,
1619 new_packages,
1620 object.struct_tag().clone(),
1621 used_in_non_entry_move_call,
1622 object.contents(),
1623 )
1624 }
1625
1626 fn load_object(
1628 vm: &MoveVM,
1629 state_view: &dyn ExecutionState,
1630 linkage_view: &mut LinkageView,
1631 new_packages: &[MovePackage],
1632 input_object_map: &mut BTreeMap<ObjectId, object_runtime::InputObject>,
1633 override_as_immutable: bool,
1634 id: ObjectId,
1635 ) -> Result<InputValue, ExecutionError> {
1636 let Some(obj) = state_view.read_object(&id) else {
1637 invariant_violation!("Object {} does not exist yet", id);
1639 };
1640 assert_invariant!(
1642 !override_as_immutable || matches!(obj.owner, Owner::Shared(_)),
1643 "override_as_immutable should only be set for shared objects"
1644 );
1645 let is_mutable_input = match obj.owner {
1646 Owner::Address(_) => true,
1647 Owner::Shared(_) => !override_as_immutable,
1648 Owner::Immutable => false,
1649 Owner::Object(_) => {
1650 invariant_violation!("Object-owned objects cannot be inputs")
1652 }
1653 _ => unimplemented!("a new Owner enum variant was added and needs to be handled"),
1654 };
1655 let owner = obj.owner;
1656 let version = obj.version();
1657 let object_metadata = InputObjectMetadata::InputObject {
1658 id,
1659 is_mutable_input,
1660 owner,
1661 version,
1662 };
1663 let obj_value = value_from_object(vm, linkage_view, new_packages, obj)?;
1664 let contained_uids = {
1665 let fully_annotated_layout = vm
1666 .get_runtime()
1667 .type_to_fully_annotated_layout(&obj_value.type_)
1668 .map_err(|e| convert_vm_error(e, vm, linkage_view))?;
1669 let mut bytes = vec![];
1670 obj_value.write_bcs_bytes(&mut bytes, None)?;
1671 match get_all_uids(&fully_annotated_layout, &bytes) {
1672 Err(e) => {
1673 invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1674 }
1675 Ok(uids) => uids,
1676 }
1677 };
1678 let runtime_input = object_runtime::InputObject {
1679 contained_uids,
1680 owner,
1681 version,
1682 };
1683 let prev = input_object_map.insert(id, runtime_input);
1684 assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1686 Ok(InputValue::new_object(object_metadata, obj_value))
1687 }
1688
1689 fn load_call_arg(
1691 vm: &MoveVM,
1692 state_view: &dyn ExecutionState,
1693 linkage_view: &mut LinkageView,
1694 new_packages: &[MovePackage],
1695 input_object_map: &mut BTreeMap<ObjectId, object_runtime::InputObject>,
1696 call_arg: CallArg,
1697 ) -> Result<InputValue, ExecutionError> {
1698 Ok(match call_arg {
1699 CallArg::Pure(value) => InputValue::new_raw(RawValueType::Any, value),
1700 other => load_object_arg(
1701 vm,
1702 state_view,
1703 linkage_view,
1704 new_packages,
1705 input_object_map,
1706 other,
1707 )?,
1708 })
1709 }
1710
1711 fn load_object_arg(
1714 vm: &MoveVM,
1715 state_view: &dyn ExecutionState,
1716 linkage_view: &mut LinkageView,
1717 new_packages: &[MovePackage],
1718 input_object_map: &mut BTreeMap<ObjectId, object_runtime::InputObject>,
1719 obj_arg: CallArg,
1720 ) -> Result<InputValue, ExecutionError> {
1721 match obj_arg {
1722 CallArg::ImmutableOrOwned(object_ref) => load_object(
1723 vm,
1724 state_view,
1725 linkage_view,
1726 new_packages,
1727 input_object_map,
1728 false,
1730 object_ref.object_id,
1731 ),
1732 CallArg::Shared(SharedObjectReference {
1733 object_id: id,
1734 mutable,
1735 ..
1736 }) => load_object(
1737 vm,
1738 state_view,
1739 linkage_view,
1740 new_packages,
1741 input_object_map,
1742 !mutable,
1744 id,
1745 ),
1746 CallArg::Receiving(object_ref) => Ok(InputValue::new_receiving_object(
1747 object_ref.object_id,
1748 object_ref.version,
1749 )),
1750 CallArg::Pure(_) => Err(ExecutionError::invariant_violation(
1751 "unexpected pure CallArg in load_object_arg",
1752 )),
1753 _ => Err(ExecutionError::invariant_violation(
1754 "a new CallArg enum variant was added and needs to be handled",
1755 )),
1756 }
1757 }
1758
1759 fn add_additional_write(
1761 additional_writes: &mut BTreeMap<ObjectId, AdditionalWrite>,
1762 owner: Owner,
1763 object_value: ObjectValue,
1764 ) -> Result<(), ExecutionError> {
1765 let ObjectValue {
1766 type_, contents, ..
1767 } = object_value;
1768 let bytes = match contents {
1769 ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
1770 ObjectContents::Raw(bytes) => bytes,
1771 };
1772 let object_id =
1773 ObjectId::from_bytes(bytes.get(..ObjectId::LENGTH).ok_or_else(|| {
1774 ExecutionError::invariant_violation("No id for Raw object bytes")
1775 })?)
1776 .expect("ObjectId::LENGTH bytes is always a valid ObjectId");
1777 let additional_write = AdditionalWrite {
1778 recipient: owner,
1779 type_,
1780 bytes,
1781 };
1782 additional_writes.insert(object_id, additional_write);
1783 Ok(())
1784 }
1785
1786 fn refund_max_gas_budget(
1790 additional_writes: &mut BTreeMap<ObjectId, AdditionalWrite>,
1791 gas_charger: &mut GasCharger,
1792 gas_id: ObjectId,
1793 ) -> Result<(), ExecutionError> {
1794 let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
1795 invariant_violation!("Gas object cannot be wrapped or destroyed")
1796 };
1797 let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
1798 invariant_violation!("Gas object must be a coin")
1799 };
1800 let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
1801 return Err(ExecutionError::new_with_source(
1802 ExecutionErrorKind::CoinBalanceOverflow,
1803 "Gas coin too large after returning the max gas budget",
1804 ));
1805 };
1806 coin.balance = Balance::new(new_balance);
1807 *bytes = coin.to_bcs_bytes();
1808 Ok(())
1809 }
1810
1811 fn create_written_object<Mode: ExecutionMode>(
1813 vm: &MoveVM,
1814 linkage_view: &LinkageView,
1815 protocol_config: &ProtocolConfig,
1816 objects_modified_at: &BTreeMap<ObjectId, LoadedRuntimeObject>,
1817 id: ObjectId,
1818 type_: Type,
1819 contents: Vec<u8>,
1820 ) -> Result<MoveStruct, ExecutionError> {
1821 debug_assert_eq!(
1822 id,
1823 ObjectId::from_bytes(&contents[..ObjectId::LENGTH])
1824 .expect("object contents should start with an id")
1825 );
1826 let old_obj_ver = objects_modified_at
1827 .get(&id)
1828 .map(|obj: &LoadedRuntimeObject| obj.version);
1829
1830 let type_tag = type_tag_core_to_sdk(
1831 &vm.get_runtime()
1832 .get_type_tag(&type_)
1833 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?,
1834 );
1835
1836 let struct_tag = match type_tag {
1837 TypeTag::Struct(inner) => *inner,
1838 _ => invariant_violation!("Non struct type for object"),
1839 };
1840 MoveStruct::new_from_execution(
1841 struct_tag,
1842 old_obj_ver.unwrap_or_default(),
1843 contents,
1844 protocol_config,
1845 Mode::packages_are_predefined(),
1846 )
1847 }
1848
1849 pub enum EitherError {
1850 CommandArgument(CommandArgumentError),
1851 Execution(ExecutionError),
1852 }
1853
1854 impl From<ExecutionError> for EitherError {
1855 fn from(e: ExecutionError) -> Self {
1856 EitherError::Execution(e)
1857 }
1858 }
1859
1860 impl From<CommandArgumentError> for EitherError {
1861 fn from(e: CommandArgumentError) -> Self {
1862 EitherError::CommandArgument(e)
1863 }
1864 }
1865
1866 impl EitherError {
1867 pub fn into_execution_error(self, command_index: usize) -> ExecutionError {
1868 match self {
1869 EitherError::CommandArgument(e) => command_argument_error(e, command_index),
1870 EitherError::Execution(e) => e,
1871 }
1872 }
1873 }
1874}