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