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