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