1pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7mod checked {
8
9 use std::{
10 collections::{BTreeMap, BTreeSet, HashMap},
11 sync::Arc,
12 };
13
14 use crate::adapter::new_native_extensions;
15 use crate::error::convert_vm_error;
16 use crate::execution_mode::ExecutionMode;
17 use crate::execution_value::{
18 CommandKind, ExecutionState, InputObjectMetadata, InputValue, ObjectContents, ObjectValue,
19 RawValueType, ResultValue, TryFromValue, UsageKind, Value,
20 };
21 use crate::gas_charger::GasCharger;
22 use crate::programmable_transactions::linkage_view::{LinkageInfo, LinkageView, SavedLinkage};
23 use crate::type_resolver::TypeTagResolver;
24 use move_binary_format::{
25 errors::{Location, VMError, VMResult},
26 file_format::{CodeOffset, FunctionDefinitionIndex, TypeParameterIndex},
27 CompiledModule,
28 };
29 use move_core_types::{
30 account_address::AccountAddress,
31 language_storage::{ModuleId, StructTag, TypeTag},
32 };
33 use move_vm_runtime::{move_vm::MoveVM, session::Session};
34 use move_vm_types::loaded_data::runtime_types::Type;
35 use sui_move_natives::object_runtime::{
36 self, get_all_uids, max_event_error, ObjectRuntime, RuntimeResults,
37 };
38 use sui_protocol_config::ProtocolConfig;
39 use sui_types::execution_status::CommandArgumentError;
40 use sui_types::storage::PackageObject;
41 use sui_types::{
42 balance::Balance,
43 base_types::{MoveObjectType, ObjectID, SequenceNumber, SuiAddress, TxContext},
44 coin::Coin,
45 error::{command_argument_error, ExecutionError},
46 event::Event,
47 execution::{ExecutionResults, ExecutionResultsV1},
48 execution_status::ExecutionErrorKind,
49 metrics::LimitsMetrics,
50 move_package::MovePackage,
51 object::{Data, MoveObject, Object, ObjectInner, Owner},
52 storage::{
53 BackingPackageStore, ChildObjectResolver, DeleteKind, DeleteKindWithOldVersion,
54 ObjectChange, WriteKind,
55 },
56 transaction::{Argument, CallArg, ObjectArg},
57 };
58
59 pub struct ExecutionContext<'vm, 'state, 'a> {
61 pub protocol_config: &'a ProtocolConfig,
63 pub metrics: Arc<LimitsMetrics>,
65 pub vm: &'vm MoveVM,
67 pub state_view: &'state dyn ExecutionState,
69 pub tx_context: &'a mut TxContext,
72 pub gas_charger: &'a mut GasCharger,
74 pub session: Session<'state, 'vm, LinkageView<'state>>,
76 additional_transfers: Vec<(SuiAddress, ObjectValue)>,
78 new_packages: Vec<Object>,
80 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
82 gas: InputValue,
85 inputs: Vec<InputValue>,
87 results: Vec<Vec<ResultValue>>,
91 borrowed: HashMap<Argument, bool>,
94 }
95
96 struct AdditionalWrite {
98 recipient: Owner,
100 type_: Type,
102 has_public_transfer: bool,
104 bytes: Vec<u8>,
106 }
107
108 impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
109 pub fn new(
110 protocol_config: &'a ProtocolConfig,
111 metrics: Arc<LimitsMetrics>,
112 vm: &'vm MoveVM,
113 state_view: &'state dyn ExecutionState,
114 tx_context: &'a mut TxContext,
115 gas_charger: &'a mut GasCharger,
116 inputs: Vec<CallArg>,
117 ) -> Result<Self, ExecutionError> {
118 let init_linkage = if protocol_config.package_upgrades_supported() {
119 LinkageInfo::Unset
120 } else {
121 LinkageInfo::Universal
122 };
123
124 let linkage = LinkageView::new(Box::new(state_view.as_sui_resolver()), init_linkage);
127 let mut tmp_session = new_session(
128 vm,
129 linkage,
130 state_view.as_child_resolver(),
131 BTreeMap::new(),
132 !gas_charger.is_unmetered(),
133 protocol_config,
134 metrics.clone(),
135 );
136 let mut input_object_map = BTreeMap::new();
137 let inputs = inputs
138 .into_iter()
139 .map(|call_arg| {
140 load_call_arg(
141 vm,
142 state_view,
143 &mut tmp_session,
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 vm,
152 state_view,
153 &mut tmp_session,
154 &mut input_object_map,
155 false,
156 gas_coin,
157 )?;
158 let Some(Value::Object(ObjectValue {
162 contents: ObjectContents::Coin(coin),
163 ..
164 })) = &mut gas.inner.value
165 else {
166 invariant_violation!("Gas object should be a populated coin")
167 };
168 let max_gas_in_balance = gas_charger.gas_budget();
169 let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else {
170 invariant_violation!(
171 "Transaction input checker should check that there is enough gas"
172 );
173 };
174 coin.balance = Balance::new(new_balance);
175 gas
176 } else {
177 InputValue {
178 object_metadata: None,
179 inner: ResultValue {
180 last_usage_kind: None,
181 value: None,
182 },
183 }
184 };
185 let (res, linkage) = tmp_session.finish();
188 let change_set = res.map_err(|e| crate::error::convert_vm_error(e, vm, &linkage))?;
189 assert_invariant!(change_set.accounts().is_empty(), "Change set must be empty");
190 let session = new_session(
192 vm,
193 linkage,
194 state_view.as_child_resolver(),
195 input_object_map,
196 !gas_charger.is_unmetered(),
197 protocol_config,
198 metrics.clone(),
199 );
200
201 Ok(Self {
202 protocol_config,
203 metrics,
204 vm,
205 state_view,
206 tx_context,
207 gas_charger,
208 session,
209 gas,
210 inputs,
211 results: vec![],
212 additional_transfers: vec![],
213 new_packages: vec![],
214 user_events: vec![],
215 borrowed: HashMap::new(),
216 })
217 }
218
219 pub fn fresh_id(&mut self) -> Result<ObjectID, ExecutionError> {
221 let object_id = self.tx_context.fresh_id();
222 let object_runtime: &mut ObjectRuntime = self.session.get_native_extensions().get_mut();
223 object_runtime
224 .new_id(object_id)
225 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
226 Ok(object_id)
227 }
228
229 pub fn delete_id(&mut self, object_id: ObjectID) -> Result<(), ExecutionError> {
231 let object_runtime: &mut ObjectRuntime = self.session.get_native_extensions().get_mut();
232 object_runtime
233 .delete_id(object_id)
234 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
235 }
236
237 pub fn set_link_context(
240 &mut self,
241 package_id: ObjectID,
242 ) -> Result<AccountAddress, ExecutionError> {
243 let resolver = self.session.get_resolver();
244 if resolver.has_linkage(package_id) {
245 return Ok(resolver.original_package_id().unwrap_or(*package_id));
247 }
248
249 let package = package_for_linkage(&self.session, package_id)
250 .map_err(|e| self.convert_vm_error(e))?;
251
252 set_linkage(&mut self.session, package.move_package())
253 }
254
255 pub fn set_linkage(
258 &mut self,
259 package: &MovePackage,
260 ) -> Result<AccountAddress, ExecutionError> {
261 set_linkage(&mut self.session, package)
262 }
263
264 pub fn reset_linkage(&mut self) {
267 reset_linkage(&mut self.session);
268 }
269
270 pub fn steal_linkage(&mut self) -> Option<SavedLinkage> {
272 steal_linkage(&mut self.session)
273 }
274
275 pub fn restore_linkage(
277 &mut self,
278 saved: Option<SavedLinkage>,
279 ) -> Result<(), ExecutionError> {
280 restore_linkage(&mut self.session, saved)
281 }
282
283 pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
285 load_type(&mut self.session, type_tag)
286 }
287
288 pub fn take_user_events(
291 &mut self,
292 module_id: &ModuleId,
293 function: FunctionDefinitionIndex,
294 last_offset: CodeOffset,
295 ) -> Result<(), ExecutionError> {
296 let object_runtime: &mut ObjectRuntime = self.session.get_native_extensions().get_mut();
297 let events = object_runtime.take_user_events();
298 let num_events = self.user_events.len() + events.len();
299 let max_events = self.protocol_config.max_num_event_emit();
300 if num_events as u64 > max_events {
301 let err = max_event_error(max_events)
302 .at_code_offset(function, last_offset)
303 .finish(Location::Module(module_id.clone()));
304 return Err(self.convert_vm_error(err));
305 }
306 let new_events = events
307 .into_iter()
308 .map(|(ty, tag, value)| {
309 let layout = self
310 .session
311 .type_to_type_layout(&ty)
312 .map_err(|e| self.convert_vm_error(e))?;
313 let Some(bytes) = value.simple_serialize(&layout) else {
314 invariant_violation!("Failed to deserialize already serialized Move value");
315 };
316 Ok((module_id.clone(), tag, bytes))
317 })
318 .collect::<Result<Vec<_>, ExecutionError>>()?;
319 self.user_events.extend(new_events);
320 Ok(())
321 }
322
323 pub fn by_value_arg<V: TryFromValue>(
328 &mut self,
329 command_kind: CommandKind<'_>,
330 arg_idx: usize,
331 arg: Argument,
332 ) -> Result<V, ExecutionError> {
333 self.by_value_arg_(command_kind, arg)
334 .map_err(|e| command_argument_error(e, arg_idx))
335 }
336 fn by_value_arg_<V: TryFromValue>(
337 &mut self,
338 command_kind: CommandKind<'_>,
339 arg: Argument,
340 ) -> Result<V, CommandArgumentError> {
341 let is_borrowed = self.arg_is_borrowed(&arg);
342 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::ByValue)?;
343 let is_copyable = if let Some(val) = val_opt {
344 val.is_copyable()
345 } else {
346 return Err(CommandArgumentError::InvalidValueUsage);
347 };
348 if !is_copyable && is_borrowed {
354 return Err(CommandArgumentError::InvalidValueUsage);
355 }
356 if matches!(arg, Argument::GasCoin)
358 && !matches!(command_kind, CommandKind::TransferObjects)
359 {
360 return Err(CommandArgumentError::InvalidGasCoinUsage);
361 }
362 if matches!(
364 input_metadata_opt,
365 Some(InputObjectMetadata::InputObject {
366 owner: Owner::Immutable | Owner::Shared { .. },
367 ..
368 })
369 ) {
370 return Err(CommandArgumentError::InvalidObjectByValue);
371 }
372 let val = if is_copyable {
373 val_opt.as_ref().unwrap().clone()
374 } else {
375 val_opt.take().unwrap()
376 };
377 V::try_from_value(val)
378 }
379
380 pub fn borrow_arg_mut<V: TryFromValue>(
386 &mut self,
387 arg_idx: usize,
388 arg: Argument,
389 ) -> Result<V, ExecutionError> {
390 self.borrow_arg_mut_(arg)
391 .map_err(|e| command_argument_error(e, arg_idx))
392 }
393 fn borrow_arg_mut_<V: TryFromValue>(
394 &mut self,
395 arg: Argument,
396 ) -> Result<V, CommandArgumentError> {
397 if self.arg_is_borrowed(&arg) {
399 return Err(CommandArgumentError::InvalidValueUsage);
400 }
401 self.borrowed.insert(arg, true);
402 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowMut)?;
403 let is_copyable = if let Some(val) = val_opt {
404 val.is_copyable()
405 } else {
406 return Err(CommandArgumentError::InvalidValueUsage);
408 };
409 if let Some(InputObjectMetadata::InputObject {
410 is_mutable_input: false,
411 ..
412 }) = input_metadata_opt
413 {
414 return Err(CommandArgumentError::InvalidObjectByMutRef);
415 }
416 let val = if is_copyable {
419 val_opt.as_ref().unwrap().clone()
420 } else {
421 val_opt.take().unwrap()
422 };
423 V::try_from_value(val)
424 }
425
426 pub fn borrow_arg<V: TryFromValue>(
430 &mut self,
431 arg_idx: usize,
432 arg: Argument,
433 ) -> Result<V, ExecutionError> {
434 self.borrow_arg_(arg)
435 .map_err(|e| command_argument_error(e, arg_idx))
436 }
437 fn borrow_arg_<V: TryFromValue>(
438 &mut self,
439 arg: Argument,
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 V::try_from_value(val_opt.as_ref().unwrap().clone())
453 }
454
455 pub fn restore_arg<Mode: ExecutionMode>(
457 &mut self,
458 updates: &mut Mode::ArgumentUpdates,
459 arg: Argument,
460 value: Value,
461 ) -> Result<(), ExecutionError> {
462 Mode::add_argument_update(self, updates, arg, &value)?;
463 let was_mut_opt = self.borrowed.remove(&arg);
464 assert_invariant!(
465 was_mut_opt.is_some() && was_mut_opt.unwrap(),
466 "Should never restore a non-mut borrowed value. \
467 The take+restore is an implementation detail of mutable references"
468 );
469 let Ok((_, value_opt)) = self.borrow_mut_impl(arg, None) else {
471 invariant_violation!("Should be able to borrow argument to restore it")
472 };
473 let old_value = value_opt.replace(value);
474 assert_invariant!(
475 old_value.is_none() || old_value.unwrap().is_copyable(),
476 "Should never restore a non-taken value, unless it is copyable. \
477 The take+restore is an implementation detail of mutable references"
478 );
479 Ok(())
480 }
481
482 pub fn transfer_object(
484 &mut self,
485 obj: ObjectValue,
486 addr: SuiAddress,
487 ) -> Result<(), ExecutionError> {
488 self.additional_transfers.push((addr, obj));
489 Ok(())
490 }
491
492 pub fn new_package<'p>(
494 &self,
495 modules: &[CompiledModule],
496 dependencies: impl IntoIterator<Item = &'p MovePackage>,
497 ) -> Result<Object, ExecutionError> {
498 Object::new_package(
499 modules,
500 self.tx_context.digest(),
501 self.protocol_config,
502 dependencies,
503 )
504 }
505
506 pub fn upgrade_package<'p>(
508 &self,
509 storage_id: ObjectID,
510 previous_package: &MovePackage,
511 new_modules: &[CompiledModule],
512 dependencies: impl IntoIterator<Item = &'p MovePackage>,
513 ) -> Result<Object, ExecutionError> {
514 Object::new_upgraded_package(
515 previous_package,
516 storage_id,
517 new_modules,
518 self.tx_context.digest(),
519 self.protocol_config,
520 dependencies,
521 )
522 }
523
524 pub fn write_package(&mut self, package: Object) -> Result<(), ExecutionError> {
526 assert_invariant!(package.is_package(), "Must be a package");
527 self.new_packages.push(package);
528 Ok(())
529 }
530
531 pub fn push_command_results(&mut self, results: Vec<Value>) -> Result<(), ExecutionError> {
533 assert_invariant!(
534 self.borrowed.values().all(|is_mut| !is_mut),
535 "all mut borrows should be restored"
536 );
537 self.borrowed = HashMap::new();
539 self.results
540 .push(results.into_iter().map(ResultValue::new).collect());
541 Ok(())
542 }
543
544 pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
546 let Self {
547 protocol_config,
548 metrics,
549 vm,
550 state_view,
551 tx_context,
552 gas_charger,
553 session,
554 additional_transfers,
555 new_packages,
556 gas,
557 inputs,
558 results,
559 user_events,
560 ..
561 } = self;
562 let tx_digest = tx_context.digest();
563 let mut additional_writes = BTreeMap::new();
564 let mut input_object_metadata = BTreeMap::new();
565 let mut by_value_inputs = BTreeSet::new();
569 let mut add_input_object_write = |input| -> Result<(), ExecutionError> {
570 let InputValue {
571 object_metadata: object_metadata_opt,
572 inner: ResultValue { value, .. },
573 } = input;
574 let Some(object_metadata) = object_metadata_opt else {
575 return Ok(());
576 };
577 let InputObjectMetadata::InputObject {
578 is_mutable_input,
579 owner,
580 id,
581 ..
582 } = &object_metadata
583 else {
584 unreachable!("Found non-input object metadata for input object when adding writes to input objects -- impossible in v0");
585 };
586 input_object_metadata.insert(object_metadata.id(), object_metadata.clone());
587 let Some(Value::Object(object_value)) = value else {
588 by_value_inputs.insert(*id);
589 return Ok(());
590 };
591 if *is_mutable_input {
592 add_additional_write(&mut additional_writes, owner.clone(), object_value)?;
593 }
594 Ok(())
595 };
596 let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
597 add_input_object_write(gas)?;
598 for input in inputs {
599 add_input_object_write(input)?
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(_, _, _)) => {
647 unreachable!("Impossible to hit Receiving in v0")
648 }
649 }
650 }
651 }
652 }
653 for (recipient, object_value) in additional_transfers {
655 let owner = Owner::AddressOwner(recipient);
656 add_additional_write(&mut additional_writes, owner, object_value)?;
657 }
658 if let Some(gas_id) = gas_id_opt {
660 refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
661 }
662
663 let (res, linkage) = session.finish_with_extensions();
664 let (_, mut native_context_extensions) =
665 res.map_err(|e| convert_vm_error(e, vm, &linkage))?;
666 let object_runtime: ObjectRuntime = native_context_extensions.remove();
667 let new_ids = object_runtime.new_ids().clone();
668 let external_transfers = additional_writes.keys().copied().collect();
670 let RuntimeResults {
671 writes,
672 deletions,
673 user_events: remaining_events,
674 loaded_child_objects,
675 } = object_runtime.finish(by_value_inputs, external_transfers)?;
676 assert_invariant!(
677 remaining_events.is_empty(),
678 "Events should be taken after every Move call"
679 );
680 let mut object_changes = BTreeMap::new();
681 for package in new_packages {
682 let id = package.id();
683 let change = ObjectChange::Write(package, WriteKind::Create);
684 object_changes.insert(id, change);
685 }
686 let tmp_session = new_session(
689 vm,
690 linkage,
691 state_view.as_child_resolver(),
692 BTreeMap::new(),
693 !gas_charger.is_unmetered(),
694 protocol_config,
695 metrics,
696 );
697 for (id, additional_write) in additional_writes {
698 let AdditionalWrite {
699 recipient,
700 type_,
701 has_public_transfer,
702 bytes,
703 } = additional_write;
704 let write_kind = if input_object_metadata.contains_key(&id)
705 || loaded_child_objects.contains_key(&id)
706 {
707 assert_invariant!(
708 !new_ids.contains_key(&id),
709 "new id should not be in mutations"
710 );
711 WriteKind::Mutate
712 } else if new_ids.contains_key(&id) {
713 WriteKind::Create
714 } else {
715 WriteKind::Unwrap
716 };
717 let move_object = unsafe {
719 create_written_object(
720 vm,
721 &tmp_session,
722 protocol_config,
723 &input_object_metadata,
724 &loaded_child_objects,
725 id,
726 type_,
727 has_public_transfer,
728 bytes,
729 write_kind,
730 )?
731 };
732 let object = Object::new_move(move_object, recipient, tx_digest);
733 let change = ObjectChange::Write(object, write_kind);
734 object_changes.insert(id, change);
735 }
736
737 for (id, (write_kind, recipient, ty, value)) in writes {
738 let abilities = tmp_session
739 .get_type_abilities(&ty)
740 .map_err(|e| convert_vm_error(e, vm, tmp_session.get_resolver()))?;
741 let has_public_transfer = abilities.has_store();
742 let layout = tmp_session
743 .type_to_type_layout(&ty)
744 .map_err(|e| convert_vm_error(e, vm, tmp_session.get_resolver()))?;
745 let Some(bytes) = value.simple_serialize(&layout) else {
746 invariant_violation!("Failed to deserialize already serialized Move value");
747 };
748 let move_object = unsafe {
750 create_written_object(
751 vm,
752 &tmp_session,
753 protocol_config,
754 &input_object_metadata,
755 &loaded_child_objects,
756 id,
757 ty,
758 has_public_transfer,
759 bytes,
760 write_kind,
761 )?
762 };
763 let object = Object::new_move(move_object, recipient, tx_digest);
764 let change = ObjectChange::Write(object, write_kind);
765 object_changes.insert(id, change);
766 }
767 for (id, delete_kind) in deletions {
768 let delete_kind_with_seq = match delete_kind {
774 DeleteKind::Normal | DeleteKind::Wrap => {
775 let old_version = match input_object_metadata.get(&id) {
776 Some(metadata) => {
777 assert_invariant!(
778 !matches!(metadata, InputObjectMetadata::InputObject { owner: Owner::Immutable, .. }),
779 "Attempting to delete immutable object {id} via delete kind {delete_kind}"
780 );
781 metadata.version()
782 }
783 None => {
784 match loaded_child_objects.get(&id) {
785 Some(version) => *version,
786 None => invariant_violation!("Deleted/wrapped object {id} must be either in input or loaded child objects")
787 }
788 }
789 };
790 if delete_kind == DeleteKind::Normal {
791 DeleteKindWithOldVersion::Normal(old_version)
792 } else {
793 DeleteKindWithOldVersion::Wrap(old_version)
794 }
795 }
796 DeleteKind::UnwrapThenDelete => {
797 if protocol_config.simplified_unwrap_then_delete() {
798 DeleteKindWithOldVersion::UnwrapThenDelete
799 } else {
800 let old_version =
801 match state_view.get_latest_parent_entry_ref_deprecated(id) {
802 Some((_, previous_version, _)) => previous_version,
803 None => continue,
806 };
807 DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(old_version)
808 }
809 }
810 };
811 object_changes.insert(id, ObjectChange::Delete(delete_kind_with_seq));
812 }
813
814 let (res, linkage) = tmp_session.finish();
815 let change_set = res.map_err(|e| convert_vm_error(e, vm, &linkage))?;
816
817 assert_invariant!(change_set.accounts().is_empty(), "Change set must be empty");
820
821 Ok(ExecutionResults::V1(ExecutionResultsV1 {
822 object_changes,
823 user_events: user_events
824 .into_iter()
825 .map(|(module_id, tag, contents)| {
826 Event::new(
827 module_id.address(),
828 module_id.name(),
829 tx_context.sender(),
830 tag,
831 contents,
832 )
833 })
834 .collect(),
835 }))
836 }
837
838 pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
840 crate::error::convert_vm_error(error, self.vm, self.session.get_resolver())
841 }
842
843 pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
845 use move_core_types::vm_status::StatusCode;
846 use sui_types::execution_status::TypeArgumentError;
847 match error.major_status() {
848 StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
849 ExecutionErrorKind::TypeArityMismatch.into()
850 }
851 StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
852 argument_idx: idx as TypeParameterIndex,
853 kind: TypeArgumentError::TypeNotFound,
854 }
855 .into(),
856 StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
857 argument_idx: idx as TypeParameterIndex,
858 kind: TypeArgumentError::ConstraintNotSatisfied,
859 }
860 .into(),
861 _ => self.convert_vm_error(error),
862 }
863 }
864
865 fn arg_is_borrowed(&self, arg: &Argument) -> bool {
867 self.borrowed.contains_key(arg)
868 }
869
870 fn arg_is_mut_borrowed(&self, arg: &Argument) -> bool {
872 matches!(self.borrowed.get(arg), Some(true))
873 }
874
875 fn borrow_mut(
877 &mut self,
878 arg: Argument,
879 usage: UsageKind,
880 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
881 {
882 self.borrow_mut_impl(arg, Some(usage))
883 }
884
885 fn borrow_mut_impl(
888 &mut self,
889 arg: Argument,
890 update_last_usage: Option<UsageKind>,
891 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
892 {
893 let (metadata, result_value) = match arg {
894 Argument::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
895 Argument::Input(i) => {
896 let Some(input_value) = self.inputs.get_mut(i as usize) else {
897 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
898 };
899 (input_value.object_metadata.as_ref(), &mut input_value.inner)
900 }
901 Argument::Result(i) => {
902 let Some(command_result) = self.results.get_mut(i as usize) else {
903 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
904 };
905 if command_result.len() != 1 {
906 return Err(CommandArgumentError::InvalidResultArity { result_idx: i });
907 }
908 (None, &mut command_result[0])
909 }
910 Argument::NestedResult(i, j) => {
911 let Some(command_result) = self.results.get_mut(i as usize) else {
912 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
913 };
914 let Some(result_value) = command_result.get_mut(j as usize) else {
915 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
916 result_idx: i,
917 secondary_idx: j,
918 });
919 };
920 (None, result_value)
921 }
922 };
923 if let Some(usage) = update_last_usage {
924 result_value.last_usage_kind = Some(usage);
925 }
926 Ok((metadata, &mut result_value.value))
927 }
928 }
929
930 impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
931 fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
932 self.session
933 .get_type_tag(type_)
934 .map_err(|e| self.convert_vm_error(e))
935 }
936 }
937
938 pub(crate) fn new_session<'state, 'vm>(
939 vm: &'vm MoveVM,
940 linkage: LinkageView<'state>,
941 child_resolver: &'state dyn ChildObjectResolver,
942 input_objects: BTreeMap<ObjectID, object_runtime::InputObject>,
943 is_metered: bool,
944 protocol_config: &ProtocolConfig,
945 metrics: Arc<LimitsMetrics>,
946 ) -> Session<'state, 'vm, LinkageView<'state>> {
947 vm.new_session_with_extensions(
948 linkage,
949 new_native_extensions(
950 child_resolver,
951 input_objects,
952 is_metered,
953 protocol_config,
954 metrics,
955 ),
956 )
957 }
958
959 pub(crate) fn new_session_for_linkage<'vm, 'state>(
961 vm: &'vm MoveVM,
962 linkage: LinkageView<'state>,
963 ) -> Session<'state, 'vm, LinkageView<'state>> {
964 vm.new_session(linkage)
965 }
966
967 pub fn set_linkage(
969 session: &mut Session<LinkageView>,
970 linkage: &MovePackage,
971 ) -> Result<AccountAddress, ExecutionError> {
972 session.get_resolver_mut().set_linkage(linkage)
973 }
974
975 pub fn reset_linkage(session: &mut Session<LinkageView>) {
978 session.get_resolver_mut().reset_linkage();
979 }
980
981 pub fn steal_linkage(session: &mut Session<LinkageView>) -> Option<SavedLinkage> {
982 session.get_resolver_mut().steal_linkage()
983 }
984
985 pub fn restore_linkage(
986 session: &mut Session<LinkageView>,
987 saved: Option<SavedLinkage>,
988 ) -> Result<(), ExecutionError> {
989 session.get_resolver_mut().restore_linkage(saved)
990 }
991
992 fn package_for_linkage(
995 session: &Session<LinkageView>,
996 package_id: ObjectID,
997 ) -> VMResult<PackageObject> {
998 use move_binary_format::errors::PartialVMError;
999 use move_core_types::vm_status::StatusCode;
1000
1001 match session.get_resolver().get_package_object(&package_id) {
1002 Ok(Some(package)) => Ok(package),
1003 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1004 .with_message(format!("Cannot find link context {package_id} in store"))
1005 .finish(Location::Undefined)),
1006 Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1007 .with_message(format!(
1008 "Error loading link context {package_id} from store: {err}"
1009 ))
1010 .finish(Location::Undefined)),
1011 }
1012 }
1013
1014 pub fn load_type(session: &mut Session<LinkageView>, type_tag: &TypeTag) -> VMResult<Type> {
1017 use move_binary_format::errors::PartialVMError;
1018 use move_core_types::vm_status::StatusCode;
1019
1020 fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1021 Err(PartialVMError::new(code).finish(Location::Undefined))
1022 }
1023
1024 Ok(match type_tag {
1025 TypeTag::Bool => Type::Bool,
1026 TypeTag::U8 => Type::U8,
1027 TypeTag::U16 => Type::U16,
1028 TypeTag::U32 => Type::U32,
1029 TypeTag::U64 => Type::U64,
1030 TypeTag::U128 => Type::U128,
1031 TypeTag::U256 => Type::U256,
1032 TypeTag::Address => Type::Address,
1033 TypeTag::Signer => Type::Signer,
1034
1035 TypeTag::Vector(inner) => Type::Vector(Box::new(load_type(session, inner)?)),
1036 TypeTag::Struct(struct_tag) => {
1037 let StructTag {
1038 address,
1039 module,
1040 name,
1041 type_params,
1042 } = struct_tag.as_ref();
1043
1044 let defining_id = ObjectID::from_address(*address);
1046 let package = package_for_linkage(session, defining_id)?;
1047
1048 let original_address =
1051 set_linkage(session, package.move_package()).map_err(|e| {
1052 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1053 .with_message(e.to_string())
1054 .finish(Location::Undefined)
1055 })?;
1056
1057 let runtime_id = ModuleId::new(original_address, module.clone());
1058 let res = session.load_struct(&runtime_id, name);
1059 reset_linkage(session);
1060 let (idx, struct_type) = res?;
1061
1062 let type_param_constraints = struct_type.type_param_constraints();
1064 if type_param_constraints.len() != type_params.len() {
1065 return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1066 }
1067
1068 if type_params.is_empty() {
1069 Type::Datatype(idx)
1070 } else {
1071 let loaded_type_params = type_params
1072 .iter()
1073 .map(|type_param| load_type(session, type_param))
1074 .collect::<VMResult<Vec<_>>>()?;
1075
1076 for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1078 let abilities = session.get_type_abilities(param)?;
1079 if !constraint.is_subset(abilities) {
1080 return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1081 }
1082 }
1083
1084 Type::DatatypeInstantiation(Box::new((idx, loaded_type_params)))
1085 }
1086 }
1087 })
1088 }
1089
1090 pub(crate) fn make_object_value<'vm, 'state>(
1091 vm: &'vm MoveVM,
1092 session: &mut Session<'state, 'vm, LinkageView<'state>>,
1093 type_: MoveObjectType,
1094 has_public_transfer: bool,
1095 used_in_non_entry_move_call: bool,
1096 contents: &[u8],
1097 ) -> Result<ObjectValue, ExecutionError> {
1098 let contents = if type_.is_coin() {
1099 let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1100 invariant_violation!("Could not deserialize a coin")
1101 };
1102 ObjectContents::Coin(coin)
1103 } else {
1104 ObjectContents::Raw(contents.to_vec())
1105 };
1106
1107 let tag: StructTag = type_.into();
1108 let type_ = load_type(session, &TypeTag::Struct(Box::new(tag)))
1109 .map_err(|e| crate::error::convert_vm_error(e, vm, session.get_resolver()))?;
1110 Ok(ObjectValue {
1111 type_,
1112 has_public_transfer,
1113 used_in_non_entry_move_call,
1114 contents,
1115 })
1116 }
1117
1118 pub(crate) fn value_from_object<'vm, 'state>(
1119 vm: &'vm MoveVM,
1120 session: &mut Session<'state, 'vm, LinkageView<'state>>,
1121 object: &Object,
1122 ) -> Result<ObjectValue, ExecutionError> {
1123 let ObjectInner {
1124 data: Data::Move(object),
1125 ..
1126 } = object.as_inner()
1127 else {
1128 invariant_violation!("Expected a Move object");
1129 };
1130
1131 let used_in_non_entry_move_call = false;
1132 make_object_value(
1133 vm,
1134 session,
1135 object.type_().clone(),
1136 object.has_public_transfer(),
1137 used_in_non_entry_move_call,
1138 object.contents(),
1139 )
1140 }
1141
1142 fn load_object<'vm, 'state>(
1144 vm: &'vm MoveVM,
1145 state_view: &'state dyn ExecutionState,
1146 session: &mut Session<'state, 'vm, LinkageView<'state>>,
1147 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1148 override_as_immutable: bool,
1149 id: ObjectID,
1150 ) -> Result<InputValue, ExecutionError> {
1151 let Some(obj) = state_view.read_object(&id) else {
1152 invariant_violation!("Object {} does not exist yet", id);
1154 };
1155 assert_invariant!(
1157 !override_as_immutable || matches!(obj.owner, Owner::Shared { .. }),
1158 "override_as_immutable should only be set for shared objects"
1159 );
1160 let is_mutable_input = match obj.owner {
1161 Owner::AddressOwner(_) => true,
1162 Owner::Shared { .. } => !override_as_immutable,
1163 Owner::Immutable => false,
1164 Owner::ObjectOwner(_) => {
1165 invariant_violation!("ObjectOwner objects cannot be input")
1167 }
1168 Owner::ConsensusAddressOwner { .. } => {
1169 unimplemented!("ConsensusAddressOwner does not exist for this execution version")
1170 }
1171 };
1172 let owner = obj.owner.clone();
1173 let version = obj.version();
1174 let object_metadata = InputObjectMetadata::InputObject {
1175 id,
1176 is_mutable_input,
1177 owner: owner.clone(),
1178 version,
1179 };
1180 let obj_value = value_from_object(vm, session, obj)?;
1181 let contained_uids = {
1182 let fully_annotated_layout =
1183 session
1184 .type_to_fully_annotated_layout(&obj_value.type_)
1185 .map_err(|e| convert_vm_error(e, vm, session.get_resolver()))?;
1186 let mut bytes = vec![];
1187 obj_value.write_bcs_bytes(&mut bytes);
1188 match get_all_uids(&fully_annotated_layout, &bytes) {
1189 Err(e) => {
1190 invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1191 }
1192 Ok(uids) => uids,
1193 }
1194 };
1195 let runtime_input = object_runtime::InputObject {
1196 contained_uids,
1197 owner,
1198 version,
1199 };
1200 let prev = input_object_map.insert(id, runtime_input);
1201 assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1203 Ok(InputValue::new_object(object_metadata, obj_value))
1204 }
1205
1206 fn load_call_arg<'vm, 'state>(
1208 vm: &'vm MoveVM,
1209 state_view: &'state dyn ExecutionState,
1210 session: &mut Session<'state, 'vm, LinkageView<'state>>,
1211 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1212 call_arg: CallArg,
1213 ) -> Result<InputValue, ExecutionError> {
1214 Ok(match call_arg {
1215 CallArg::Pure(bytes) => InputValue::new_raw(RawValueType::Any, bytes),
1216 CallArg::Object(obj_arg) => {
1217 load_object_arg(vm, state_view, session, input_object_map, obj_arg)?
1218 }
1219 CallArg::FundsWithdrawal(_) => unreachable!("Impossible to hit BalanceWithdraw in v0"),
1220 })
1221 }
1222
1223 fn load_object_arg<'vm, 'state>(
1225 vm: &'vm MoveVM,
1226 state_view: &'state dyn ExecutionState,
1227 session: &mut Session<'state, 'vm, LinkageView<'state>>,
1228 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1229 obj_arg: ObjectArg,
1230 ) -> Result<InputValue, ExecutionError> {
1231 match obj_arg {
1232 ObjectArg::ImmOrOwnedObject((id, _, _)) => load_object(
1233 vm,
1234 state_view,
1235 session,
1236 input_object_map,
1237 false,
1238 id,
1239 ),
1240 ObjectArg::SharedObject { id, mutability, .. } => load_object(
1241 vm,
1242 state_view,
1243 session,
1244 input_object_map,
1245 !mutability.is_exclusive(),
1246 id,
1247 ),
1248 ObjectArg::Receiving(_) => unreachable!("Impossible to hit Receiving in v0"),
1249 }
1250 }
1251
1252 fn add_additional_write(
1254 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1255 owner: Owner,
1256 object_value: ObjectValue,
1257 ) -> Result<(), ExecutionError> {
1258 let ObjectValue {
1259 type_,
1260 has_public_transfer,
1261 contents,
1262 ..
1263 } = object_value;
1264 let bytes = match contents {
1265 ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
1266 ObjectContents::Raw(bytes) => bytes,
1267 };
1268 let object_id = MoveObject::id_opt(&bytes).map_err(|e| {
1269 ExecutionError::invariant_violation(format!("No id for Raw object bytes. {e}"))
1270 })?;
1271 let additional_write = AdditionalWrite {
1272 recipient: owner,
1273 type_,
1274 has_public_transfer,
1275 bytes,
1276 };
1277 additional_writes.insert(object_id, additional_write);
1278 Ok(())
1279 }
1280
1281 fn refund_max_gas_budget(
1284 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1285 gas_charger: &mut GasCharger,
1286 gas_id: ObjectID,
1287 ) -> Result<(), ExecutionError> {
1288 let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
1289 invariant_violation!("Gas object cannot be wrapped or destroyed")
1290 };
1291 let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
1292 invariant_violation!("Gas object must be a coin")
1293 };
1294 let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
1295 return Err(ExecutionError::new_with_source(
1296 ExecutionErrorKind::CoinBalanceOverflow,
1297 "Gas coin too large after returning the max gas budget",
1298 ));
1299 };
1300 coin.balance = Balance::new(new_balance);
1301 *bytes = coin.to_bcs_bytes();
1302 Ok(())
1303 }
1304
1305 unsafe fn create_written_object<'vm, 'state>(
1311 vm: &'vm MoveVM,
1312 session: &Session<'state, 'vm, LinkageView<'state>>,
1313 protocol_config: &ProtocolConfig,
1314 input_object_metadata: &BTreeMap<ObjectID, InputObjectMetadata>,
1315 loaded_child_objects: &BTreeMap<ObjectID, SequenceNumber>,
1316 id: ObjectID,
1317 type_: Type,
1318 has_public_transfer: bool,
1319 contents: Vec<u8>,
1320 write_kind: WriteKind,
1321 ) -> Result<MoveObject, ExecutionError> {
1322 debug_assert_eq!(
1323 id,
1324 MoveObject::id_opt(&contents).expect("object contents should start with an id")
1325 );
1326 let metadata_opt = input_object_metadata.get(&id);
1327 let loaded_child_version_opt = loaded_child_objects.get(&id);
1328 assert_invariant!(
1329 metadata_opt.is_none() || loaded_child_version_opt.is_none(),
1330 "Loaded {id} as a child, but that object was an input object",
1331 );
1332
1333 let old_obj_ver = metadata_opt
1334 .map(|metadata| metadata.version())
1335 .or_else(|| loaded_child_version_opt.copied());
1336
1337 debug_assert!(
1338 (write_kind == WriteKind::Mutate) == old_obj_ver.is_some(),
1339 "Inconsistent state: write_kind: {write_kind:?}, old ver: {old_obj_ver:?}"
1340 );
1341
1342 let type_tag = session
1343 .get_type_tag(&type_)
1344 .map_err(|e| crate::error::convert_vm_error(e, vm, session.get_resolver()))?;
1345
1346 let struct_tag = match type_tag {
1347 TypeTag::Struct(inner) => *inner,
1348 _ => invariant_violation!("Non struct type for object"),
1349 };
1350 MoveObject::new_from_execution(
1351 struct_tag.into(),
1352 has_public_transfer,
1353 old_obj_ver.unwrap_or_default(),
1354 contents,
1355 protocol_config,
1356 false,
1357 )
1358 }
1359}