1use crate::{
5 adapter,
6 data_store::linked_data_store::LinkedDataStore,
7 execution_mode::ExecutionMode,
8 gas_charger::GasCharger,
9 gas_meter::SuiGasMeter,
10 programmable_transactions::{self as legacy_ptb},
11 sp,
12 static_programmable_transactions::{
13 env::Env,
14 execution::values::{Local, Locals, Value},
15 linkage::resolved_linkage::{ResolvedLinkage, RootedLinkage},
16 loading::ast::ObjectMutability,
17 typing::ast::{self as T, Type},
18 },
19};
20use indexmap::{IndexMap, IndexSet};
21use move_binary_format::{
22 CompiledModule,
23 errors::{Location, PartialVMError, VMResult},
24 file_format::FunctionDefinitionIndex,
25 file_format_common::VERSION_6,
26};
27use move_core_types::{
28 account_address::AccountAddress,
29 identifier::IdentStr,
30 language_storage::{ModuleId, StructTag},
31};
32use move_trace_format::format::MoveTraceBuilder;
33use move_vm_runtime::native_extensions::NativeContextExtensions;
34use move_vm_types::{
35 gas::GasMeter,
36 values::{VMValueCast, Value as VMValue},
37};
38use std::{
39 cell::RefCell,
40 collections::{BTreeMap, BTreeSet},
41 rc::Rc,
42 sync::Arc,
43};
44use sui_move_natives::object_runtime::{
45 self, LoadedRuntimeObject, ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
46};
47use sui_types::{
48 TypeTag,
49 base_types::{MoveObjectType, ObjectID, SequenceNumber, TxContext},
50 error::{ExecutionError, ExecutionErrorKind},
51 execution::ExecutionResults,
52 metrics::LimitsMetrics,
53 move_package::{MovePackage, UpgradeCap, UpgradeReceipt, UpgradeTicket},
54 object::{MoveObject, Object, Owner},
55};
56use sui_verifier::INIT_FN_NAME;
57use tracing::instrument;
58
59macro_rules! unwrap {
60 ($e:expr, $($args:expr),* $(,)?) => {
61 match $e {
62 Some(v) => v,
63 None => {
64 invariant_violation!("Unexpected none: {}", format!($($args),*))
65 }
66 }
67
68 };
69}
70
71macro_rules! object_runtime_mut {
72 ($context:ident) => {{
73 $context
74 .native_extensions
75 .get_mut::<ObjectRuntime>()
76 .map_err(|e| $context.env.convert_vm_error(e.finish(Location::Undefined)))
77 }};
78}
79
80macro_rules! charge_gas_ {
81 ($gas_charger:expr, $env:expr, $case:ident, $value_view:expr) => {{
82 SuiGasMeter($gas_charger.move_gas_status_mut())
83 .$case($value_view)
84 .map_err(|e| $env.convert_vm_error(e.finish(Location::Undefined)))
85 }};
86}
87
88macro_rules! charge_gas {
89 ($context:ident, $case:ident, $value_view:expr) => {{ charge_gas_!($context.gas_charger, $context.env, $case, $value_view) }};
90}
91
92#[derive(Debug)]
94pub struct CtxValue(Value);
95
96#[derive(Clone, Debug)]
97pub struct InputObjectMetadata {
98 pub id: ObjectID,
99 pub mutability: ObjectMutability,
100 pub owner: Owner,
101 pub version: SequenceNumber,
102 pub type_: Type,
103}
104
105#[derive(Copy, Clone)]
106enum UsageKind {
107 Move,
108 Copy,
109 Borrow,
110}
111
112struct Locations {
114 tx_context_value: Locals,
116 gas: Option<(InputObjectMetadata, Locals)>,
118 input_object_metadata: Vec<(T::InputIndex, InputObjectMetadata)>,
120 object_inputs: Locals,
121 pure_input_bytes: IndexSet<Vec<u8>>,
122 pure_input_metadata: Vec<T::PureInput>,
123 pure_inputs: Locals,
124 receiving_input_metadata: Vec<T::ReceivingInput>,
125 receiving_inputs: Locals,
126 results: Vec<Locals>,
130}
131
132enum ResolvedLocation<'a> {
133 Local(Local<'a>),
134 Pure {
135 bytes: &'a [u8],
136 metadata: &'a T::PureInput,
137 local: Local<'a>,
138 },
139 Receiving {
140 metadata: &'a T::ReceivingInput,
141 local: Local<'a>,
142 },
143}
144
145pub struct Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas> {
147 pub env: &'env Env<'pc, 'vm, 'state, 'linkage>,
148 pub metrics: Arc<LimitsMetrics>,
150 pub native_extensions: NativeContextExtensions<'env>,
151 pub tx_context: Rc<RefCell<TxContext>>,
154 pub gas_charger: &'gas mut GasCharger,
156 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
158 locations: Locations,
160}
161
162impl Locations {
163 fn resolve(&mut self, location: T::Location) -> Result<ResolvedLocation<'_>, ExecutionError> {
166 Ok(match location {
167 T::Location::TxContext => ResolvedLocation::Local(self.tx_context_value.local(0)?),
168 T::Location::GasCoin => {
169 let (_, gas_locals) = unwrap!(self.gas.as_mut(), "Gas coin not provided");
170 ResolvedLocation::Local(gas_locals.local(0)?)
171 }
172 T::Location::ObjectInput(i) => ResolvedLocation::Local(self.object_inputs.local(i)?),
173 T::Location::Result(i, j) => {
174 let result = unwrap!(self.results.get_mut(i as usize), "bounds already verified");
175 ResolvedLocation::Local(result.local(j)?)
176 }
177 T::Location::PureInput(i) => {
178 let local = self.pure_inputs.local(i)?;
179 let metadata = &self.pure_input_metadata[i as usize];
180 let bytes = self
181 .pure_input_bytes
182 .get_index(metadata.byte_index)
183 .ok_or_else(|| {
184 make_invariant_violation!(
185 "Pure input {} bytes out of bounds at index {}",
186 metadata.original_input_index.0,
187 metadata.byte_index,
188 )
189 })?;
190 ResolvedLocation::Pure {
191 bytes,
192 metadata,
193 local,
194 }
195 }
196 T::Location::ReceivingInput(i) => ResolvedLocation::Receiving {
197 metadata: &self.receiving_input_metadata[i as usize],
198 local: self.receiving_inputs.local(i)?,
199 },
200 })
201 }
202}
203
204impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas> {
205 #[instrument(name = "Context::new", level = "trace", skip_all)]
206 pub fn new(
207 env: &'env Env<'pc, 'vm, 'state, 'linkage>,
208 metrics: Arc<LimitsMetrics>,
209 tx_context: Rc<RefCell<TxContext>>,
210 gas_charger: &'gas mut GasCharger,
211 pure_input_bytes: IndexSet<Vec<u8>>,
212 object_inputs: Vec<T::ObjectInput>,
213 pure_input_metadata: Vec<T::PureInput>,
214 receiving_input_metadata: Vec<T::ReceivingInput>,
215 ) -> Result<Self, ExecutionError>
216 where
217 'pc: 'state,
218 {
219 let mut input_object_map = BTreeMap::new();
220 let mut input_object_metadata = Vec::with_capacity(object_inputs.len());
221 let mut object_values = Vec::with_capacity(object_inputs.len());
222 for object_input in object_inputs {
223 let (i, m, v) = load_object_arg(gas_charger, env, &mut input_object_map, object_input)?;
224 input_object_metadata.push((i, m));
225 object_values.push(Some(v));
226 }
227 let object_inputs = Locals::new(object_values)?;
228 let pure_inputs = Locals::new_invalid(pure_input_metadata.len())?;
229 let receiving_inputs = Locals::new_invalid(receiving_input_metadata.len())?;
230 let gas = match gas_charger.gas_coin() {
231 Some(gas_coin) => {
232 let ty = env.gas_coin_type()?;
233 let (gas_metadata, gas_value) = load_object_arg_impl(
234 gas_charger,
235 env,
236 &mut input_object_map,
237 gas_coin,
238 ObjectMutability::Mutable,
239 ty,
240 )?;
241 let mut gas_locals = Locals::new([Some(gas_value)])?;
242 let gas_local = gas_locals.local(0)?;
243 let gas_ref = gas_local.borrow()?;
244 let max_gas_in_balance = gas_charger.gas_budget();
246 gas_ref.coin_ref_subtract_balance(max_gas_in_balance)?;
247 Some((gas_metadata, gas_locals))
248 }
249 None => None,
250 };
251 let native_extensions = adapter::new_native_extensions(
252 env.state_view.as_child_resolver(),
253 input_object_map,
254 !gas_charger.is_unmetered(),
255 env.protocol_config,
256 metrics.clone(),
257 tx_context.clone(),
258 );
259
260 let tx_context_value = Locals::new(vec![Some(Value::new_tx_context(
261 tx_context.borrow().digest(),
262 )?)])?;
263 Ok(Self {
264 env,
265 metrics,
266 native_extensions,
267 tx_context,
268 gas_charger,
269 user_events: vec![],
270 locations: Locations {
271 tx_context_value,
272 gas,
273 input_object_metadata,
274 object_inputs,
275 pure_input_bytes,
276 pure_input_metadata,
277 pure_inputs,
278 receiving_input_metadata,
279 receiving_inputs,
280 results: vec![],
281 },
282 })
283 }
284
285 pub fn finish<Mode: ExecutionMode>(mut self) -> Result<ExecutionResults, ExecutionError> {
286 assert_invariant!(
287 !self.locations.tx_context_value.local(0)?.is_invalid()?,
288 "tx context value should be present"
289 );
290 let gas = std::mem::take(&mut self.locations.gas);
291 let object_input_metadata = std::mem::take(&mut self.locations.input_object_metadata);
292 let mut object_inputs =
293 std::mem::replace(&mut self.locations.object_inputs, Locals::new_invalid(0)?);
294 let mut loaded_runtime_objects = BTreeMap::new();
295 let mut by_value_shared_objects = BTreeSet::new();
296 let mut consensus_owner_objects = BTreeMap::new();
297 let gas = gas
298 .map(|(m, mut g)| Result::<_, ExecutionError>::Ok((m, g.local(0)?.move_if_valid()?)))
299 .transpose()?;
300 let gas_id_opt = gas.as_ref().map(|(m, _)| m.id);
301 let object_inputs = object_input_metadata
302 .into_iter()
303 .enumerate()
304 .map(|(i, (_, m))| {
305 let v_opt = object_inputs.local(i as u16)?.move_if_valid()?;
306 Ok((m, v_opt))
307 })
308 .collect::<Result<Vec<_>, ExecutionError>>()?;
309 for (metadata, value_opt) in object_inputs.into_iter().chain(gas) {
310 let InputObjectMetadata {
311 id,
312 mutability,
313 owner,
314 version,
315 type_,
316 } = metadata;
317 match mutability {
318 ObjectMutability::Immutable => continue,
319 ObjectMutability::NonExclusiveWrite | ObjectMutability::Mutable => (),
323 }
324 loaded_runtime_objects.insert(
325 id,
326 LoadedRuntimeObject {
327 version,
328 is_modified: true,
329 },
330 );
331 if let Some(object) = value_opt {
332 self.transfer_object_(
333 owner,
334 type_,
335 CtxValue(object),
336 true,
337 )?;
338 } else if owner.is_shared() {
339 by_value_shared_objects.insert(id);
340 } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
341 consensus_owner_objects.insert(id, owner.clone());
342 }
343 }
344
345 let Self {
346 env,
347 mut native_extensions,
348 tx_context,
349 gas_charger,
350 user_events,
351 ..
352 } = self;
353 let ref_context: &RefCell<TxContext> = &tx_context;
354 let tx_context: &TxContext = &ref_context.borrow();
355 let tx_digest = ref_context.borrow().digest();
356
357 let object_runtime: ObjectRuntime = native_extensions
358 .remove()
359 .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
360
361 let RuntimeResults {
362 mut writes,
363 user_events: remaining_events,
364 loaded_child_objects,
365 mut created_object_ids,
366 deleted_object_ids,
367 accumulator_events,
368 settlement_input_sui,
369 settlement_output_sui,
370 } = object_runtime.finish()?;
371 assert_invariant!(
372 remaining_events.is_empty(),
373 "Events should be taken after every Move call"
374 );
375 if let Some(gas_id) = gas_id_opt {
377 refund_max_gas_budget(&mut writes, gas_charger, gas_id)?;
378 }
379
380 loaded_runtime_objects.extend(loaded_child_objects);
381
382 let mut written_objects = BTreeMap::new();
383
384 for (id, (recipient, ty, value)) in writes {
385 let ty: Type = env.load_type_from_struct(&ty.clone().into())?;
386 let abilities = ty.abilities();
387 let has_public_transfer = abilities.has_store();
388 let Some(bytes) = value.serialize() else {
389 invariant_violation!("Failed to serialize already deserialized Move value");
390 };
391 let move_object = unsafe {
393 create_written_object::<Mode>(
394 env,
395 &loaded_runtime_objects,
396 id,
397 ty,
398 has_public_transfer,
399 bytes,
400 )?
401 };
402 let object = Object::new_move(move_object, recipient, tx_digest);
403 written_objects.insert(id, object);
404 }
405
406 for package in self.env.linkable_store.to_new_packages().into_iter() {
407 let package_obj = Object::new_from_package(package, tx_digest);
408 let id = package_obj.id();
409 created_object_ids.insert(id);
410 written_objects.insert(id, package_obj);
411 }
412
413 legacy_ptb::context::finish(
414 env.protocol_config,
415 env.state_view,
416 gas_charger,
417 tx_context,
418 &by_value_shared_objects,
419 &consensus_owner_objects,
420 loaded_runtime_objects,
421 written_objects,
422 created_object_ids,
423 deleted_object_ids,
424 user_events,
425 accumulator_events,
426 settlement_input_sui,
427 settlement_output_sui,
428 )
429 }
430
431 pub fn object_runtime(&self) -> Result<&ObjectRuntime<'_>, ExecutionError> {
432 self.native_extensions
433 .get::<ObjectRuntime>()
434 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))
435 }
436
437 pub fn take_user_events(
438 &mut self,
439 storage_id: ModuleId,
440 function_def_idx: FunctionDefinitionIndex,
441 instr_length: u16,
442 linkage: &RootedLinkage,
443 ) -> Result<(), ExecutionError> {
444 let events = object_runtime_mut!(self)?.take_user_events();
445 let num_events = self.user_events.len() + events.len();
446 let max_events = self.env.protocol_config.max_num_event_emit();
447 if num_events as u64 > max_events {
448 let err = max_event_error(max_events)
449 .at_code_offset(function_def_idx, instr_length)
450 .finish(Location::Module(storage_id.clone()));
451 return Err(self.env.convert_linked_vm_error(err, linkage));
452 }
453 let new_events = events
454 .into_iter()
455 .map(|(tag, value)| {
456 let Some(bytes) = value.serialize() else {
457 invariant_violation!("Failed to serialize Move event");
458 };
459 Ok((storage_id.clone(), tag, bytes))
460 })
461 .collect::<Result<Vec<_>, ExecutionError>>()?;
462 self.user_events.extend(new_events);
463 Ok(())
464 }
465
466 fn location(
471 &mut self,
472 usage: UsageKind,
473 location: T::Location,
474 ) -> Result<Value, ExecutionError> {
475 let resolved = self.locations.resolve(location)?;
476 let mut local = match resolved {
477 ResolvedLocation::Local(l) => l,
478 ResolvedLocation::Pure {
479 bytes,
480 metadata,
481 mut local,
482 } => {
483 if local.is_invalid()? {
484 let v = load_pure_value(self.gas_charger, self.env, bytes, metadata)?;
485 local.store(v)?;
486 }
487 local
488 }
489 ResolvedLocation::Receiving {
490 metadata,
491 mut local,
492 } => {
493 if local.is_invalid()? {
494 let v = load_receiving_value(self.gas_charger, self.env, metadata)?;
495 local.store(v)?;
496 }
497 local
498 }
499 };
500 Ok(match usage {
501 UsageKind::Move => local.move_()?,
502 UsageKind::Copy => {
503 let value = local.copy()?;
504 charge_gas_!(self.gas_charger, self.env, charge_copy_loc, &value)?;
505 value
506 }
507 UsageKind::Borrow => local.borrow()?,
508 })
509 }
510
511 fn location_usage(&mut self, usage: T::Usage) -> Result<Value, ExecutionError> {
512 match usage {
513 T::Usage::Move(location) => self.location(UsageKind::Move, location),
514 T::Usage::Copy { location, .. } => self.location(UsageKind::Copy, location),
515 }
516 }
517
518 fn argument_value(&mut self, sp!(_, (arg_, _)): T::Argument) -> Result<Value, ExecutionError> {
519 match arg_ {
520 T::Argument__::Use(usage) => self.location_usage(usage),
521 T::Argument__::Freeze(usage) => self.location_usage(usage),
523 T::Argument__::Borrow(_, location) => self.location(UsageKind::Borrow, location),
524 T::Argument__::Read(usage) => {
525 let reference = self.location_usage(usage)?;
526 charge_gas!(self, charge_read_ref, &reference)?;
527 reference.read_ref()
528 }
529 }
530 }
531
532 pub fn argument<V>(&mut self, arg: T::Argument) -> Result<V, ExecutionError>
533 where
534 VMValue: VMValueCast<V>,
535 {
536 let value = self.argument_value(arg)?;
537 let value: V = value.cast()?;
538 Ok(value)
539 }
540
541 pub fn arguments<V>(&mut self, args: Vec<T::Argument>) -> Result<Vec<V>, ExecutionError>
542 where
543 VMValue: VMValueCast<V>,
544 {
545 args.into_iter().map(|arg| self.argument(arg)).collect()
546 }
547
548 pub fn result(&mut self, result: Vec<Option<CtxValue>>) -> Result<(), ExecutionError> {
549 self.locations
550 .results
551 .push(Locals::new(result.into_iter().map(|v| v.map(|v| v.0)))?);
552 Ok(())
553 }
554
555 pub fn copy_value(&mut self, value: &CtxValue) -> Result<CtxValue, ExecutionError> {
556 Ok(CtxValue(copy_value(self.gas_charger, self.env, &value.0)?))
557 }
558
559 pub fn new_coin(&mut self, amount: u64) -> Result<CtxValue, ExecutionError> {
560 let id = self.tx_context.borrow_mut().fresh_id();
561 object_runtime_mut!(self)?
562 .new_id(id)
563 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
564 Ok(CtxValue(Value::coin(id, amount)))
565 }
566
567 pub fn destroy_coin(&mut self, coin: CtxValue) -> Result<u64, ExecutionError> {
568 let (id, amount) = coin.0.unpack_coin()?;
569 object_runtime_mut!(self)?
570 .delete_id(id)
571 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
572 Ok(amount)
573 }
574
575 pub fn new_upgrade_cap(&mut self, storage_id: ObjectID) -> Result<CtxValue, ExecutionError> {
576 let id = self.tx_context.borrow_mut().fresh_id();
577 object_runtime_mut!(self)?
578 .new_id(id)
579 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
580 let cap = UpgradeCap::new(id, storage_id);
581 Ok(CtxValue(Value::upgrade_cap(cap)))
582 }
583
584 pub fn upgrade_receipt(
585 &self,
586 upgrade_ticket: UpgradeTicket,
587 upgraded_package_id: ObjectID,
588 ) -> CtxValue {
589 let receipt = UpgradeReceipt::new(upgrade_ticket, upgraded_package_id);
590 CtxValue(Value::upgrade_receipt(receipt))
591 }
592
593 pub fn vm_move_call(
598 &mut self,
599 function: T::LoadedFunction,
600 args: Vec<CtxValue>,
601 trace_builder_opt: Option<&mut MoveTraceBuilder>,
602 ) -> Result<Vec<CtxValue>, ExecutionError> {
603 let result = self.execute_function_bypass_visibility(
604 &function.runtime_id,
605 &function.name,
606 &function.type_arguments,
607 args,
608 &function.linkage,
609 trace_builder_opt,
610 )?;
611 self.take_user_events(
612 function.storage_id,
613 function.definition_index,
614 function.instruction_length,
615 &function.linkage,
616 )?;
617 Ok(result)
618 }
619
620 pub fn execute_function_bypass_visibility(
621 &mut self,
622 runtime_id: &ModuleId,
623 function_name: &IdentStr,
624 ty_args: &[Type],
625 args: Vec<CtxValue>,
626 linkage: &RootedLinkage,
627 tracer: Option<&mut MoveTraceBuilder>,
628 ) -> Result<Vec<CtxValue>, ExecutionError> {
629 let ty_args = ty_args
630 .iter()
631 .enumerate()
632 .map(|(idx, ty)| self.env.load_vm_type_argument_from_adapter_type(idx, ty))
633 .collect::<Result<_, _>>()?;
634 let gas_status = self.gas_charger.move_gas_status_mut();
635 let mut data_store = LinkedDataStore::new(linkage, self.env.linkable_store);
636 let values = self
637 .env
638 .vm
639 .get_runtime()
640 .execute_function_with_values_bypass_visibility(
641 runtime_id,
642 function_name,
643 ty_args,
644 args.into_iter().map(|v| v.0.into()).collect(),
645 &mut data_store,
646 &mut SuiGasMeter(gas_status),
647 &mut self.native_extensions,
648 tracer,
649 )
650 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
651 Ok(values.into_iter().map(|v| CtxValue(v.into())).collect())
652 }
653
654 pub fn deserialize_modules(
660 &mut self,
661 module_bytes: &[Vec<u8>],
662 is_upgrade: bool,
663 ) -> Result<Vec<CompiledModule>, ExecutionError> {
664 assert_invariant!(
665 !module_bytes.is_empty(),
666 "empty package is checked in transaction input checker"
667 );
668 let total_bytes = module_bytes.iter().map(|v| v.len()).sum();
669 if is_upgrade {
670 self.gas_charger.charge_upgrade_package(total_bytes)?
671 } else {
672 self.gas_charger.charge_publish_package(total_bytes)?
673 }
674
675 let binary_config = self.env.protocol_config.binary_config(None);
676 let modules = module_bytes
677 .iter()
678 .map(|b| {
679 CompiledModule::deserialize_with_config(b, &binary_config)
680 .map_err(|e| e.finish(Location::Undefined))
681 })
682 .collect::<VMResult<Vec<CompiledModule>>>()
683 .map_err(|e| self.env.convert_vm_error(e))?;
684 Ok(modules)
685 }
686
687 fn fetch_package(
688 &mut self,
689 dependency_id: &ObjectID,
690 ) -> Result<Rc<MovePackage>, ExecutionError> {
691 let [fetched_package] = self.fetch_packages(&[*dependency_id])?.try_into().map_err(
692 |_| {
693 make_invariant_violation!(
694 "We should always fetch a single package for each object or return a dependency error."
695 )
696 },
697 )?;
698 Ok(fetched_package)
699 }
700
701 fn fetch_packages(
702 &mut self,
703 dependency_ids: &[ObjectID],
704 ) -> Result<Vec<Rc<MovePackage>>, ExecutionError> {
705 let mut fetched = vec![];
706 let mut missing = vec![];
707
708 for id in dependency_ids {
709 match self.env.linkable_store.get_package(id) {
710 Err(e) => {
711 return Err(ExecutionError::new_with_source(
712 ExecutionErrorKind::PublishUpgradeMissingDependency,
713 e,
714 ));
715 }
716 Ok(Some(inner)) => {
717 fetched.push(inner);
718 }
719 Ok(None) => {
720 missing.push(*id);
721 }
722 }
723 }
724
725 if missing.is_empty() {
726 assert_invariant!(
727 fetched.len() == dependency_ids.len(),
728 "all dependencies should be fetched"
729 );
730 Ok(fetched)
731 } else {
732 let msg = format!(
733 "Missing dependencies: {}",
734 missing
735 .into_iter()
736 .map(|dep| format!("{}", dep))
737 .collect::<Vec<_>>()
738 .join(", ")
739 );
740 Err(ExecutionError::new_with_source(
741 ExecutionErrorKind::PublishUpgradeMissingDependency,
742 msg,
743 ))
744 }
745 }
746
747 fn publish_and_verify_modules(
748 &mut self,
749 package_id: ObjectID,
750 modules: &[CompiledModule],
751 linkage: &RootedLinkage,
752 ) -> Result<(), ExecutionError> {
753 let binary_version = self.env.protocol_config.move_binary_format_version();
755 let new_module_bytes: Vec<_> = modules
756 .iter()
757 .map(|m| {
758 let mut bytes = Vec::new();
759 let version = if binary_version > VERSION_6 {
760 m.version
761 } else {
762 VERSION_6
763 };
764 m.serialize_with_version(version, &mut bytes).unwrap();
765 bytes
766 })
767 .collect();
768 let mut data_store = LinkedDataStore::new(linkage, self.env.linkable_store);
769 self.env
770 .vm
771 .get_runtime()
772 .publish_module_bundle(
773 new_module_bytes,
774 AccountAddress::from(package_id),
775 &mut data_store,
776 &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
777 )
778 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
779
780 for module in modules {
782 sui_verifier::verifier::sui_verify_module_unmetered(
785 module,
786 &BTreeMap::new(),
787 &self
788 .env
789 .protocol_config
790 .verifier_config(None),
791 )?;
792 }
793
794 Ok(())
795 }
796
797 fn init_modules(
798 &mut self,
799 package_id: ObjectID,
800 modules: &[CompiledModule],
801 linkage: &RootedLinkage,
802 mut trace_builder_opt: Option<&mut MoveTraceBuilder>,
803 ) -> Result<(), ExecutionError> {
804 for module in modules {
805 let Some((fdef_idx, fdef)) = module.find_function_def_by_name(INIT_FN_NAME.as_str())
806 else {
807 continue;
808 };
809 let fhandle = module.function_handle_at(fdef.function);
810 let fparameters = module.signature_at(fhandle.parameters);
811 assert_invariant!(
812 fparameters.0.len() <= 2,
813 "init function should have at most 2 parameters"
814 );
815 let has_otw = fparameters.0.len() == 2;
816 let tx_context = self
817 .location(UsageKind::Borrow, T::Location::TxContext)
818 .map_err(|e| {
819 make_invariant_violation!("Failed to get tx context for init function: {}", e)
820 })?;
821 let args = if has_otw {
822 vec![CtxValue(Value::one_time_witness()?), CtxValue(tx_context)]
823 } else {
824 vec![CtxValue(tx_context)]
825 };
826 let return_values = self.execute_function_bypass_visibility(
827 &module.self_id(),
828 INIT_FN_NAME,
829 &[],
830 args,
831 linkage,
832 trace_builder_opt.as_deref_mut(),
833 )?;
834
835 let storage_id = ModuleId::new(package_id.into(), module.self_id().name().to_owned());
836 self.take_user_events(
837 storage_id,
838 fdef_idx,
839 fdef.code.as_ref().map(|c| c.code.len() as u16).unwrap_or(0),
840 linkage,
841 )?;
842 assert_invariant!(
843 return_values.is_empty(),
844 "init should not have return values"
845 )
846 }
847
848 Ok(())
849 }
850
851 pub fn publish_and_init_package<Mode: ExecutionMode>(
852 &mut self,
853 mut modules: Vec<CompiledModule>,
854 dep_ids: &[ObjectID],
855 linkage: ResolvedLinkage,
856 trace_builder_opt: Option<&mut MoveTraceBuilder>,
857 ) -> Result<ObjectID, ExecutionError> {
858 let runtime_id = if <Mode>::packages_are_predefined() {
859 (*modules[0].self_id().address()).into()
861 } else {
862 let id = self.tx_context.borrow_mut().fresh_id();
866 adapter::substitute_package_id(&mut modules, id)?;
867 id
868 };
869
870 let dependencies = self.fetch_packages(dep_ids)?;
871 let package = Rc::new(MovePackage::new_initial(
872 &modules,
873 self.env.protocol_config,
874 dependencies.iter().map(|p| p.as_ref()),
875 )?);
876 let package_id = package.id();
877
878 let linkage = RootedLinkage::new_for_publication(package_id, runtime_id, linkage);
884
885 self.env.linkable_store.push_package(package_id, package)?;
886 let res = self
887 .publish_and_verify_modules(runtime_id, &modules, &linkage)
888 .and_then(|_| self.init_modules(package_id, &modules, &linkage, trace_builder_opt));
889 match res {
890 Ok(()) => Ok(runtime_id),
891 Err(e) => {
892 self.env.linkable_store.pop_package(package_id)?;
893 Err(e)
894 }
895 }
896 }
897
898 pub fn upgrade(
899 &mut self,
900 mut modules: Vec<CompiledModule>,
901 dep_ids: &[ObjectID],
902 current_package_id: ObjectID,
903 upgrade_ticket_policy: u8,
904 linkage: ResolvedLinkage,
905 ) -> Result<ObjectID, ExecutionError> {
906 let current_move_package = self.fetch_package(¤t_package_id)?;
908
909 let runtime_id = current_move_package.original_package_id();
910 adapter::substitute_package_id(&mut modules, runtime_id)?;
911
912 let storage_id = self.tx_context.borrow_mut().fresh_id();
917
918 let dependencies = self.fetch_packages(dep_ids)?;
919 let package = current_move_package.new_upgraded(
920 storage_id,
921 &modules,
922 self.env.protocol_config,
923 dependencies.iter().map(|p| p.as_ref()),
924 )?;
925
926 let linkage = RootedLinkage::new_for_publication(storage_id, runtime_id, linkage);
927 self.publish_and_verify_modules(runtime_id, &modules, &linkage)?;
928
929 legacy_ptb::execution::check_compatibility(
930 self.env.protocol_config,
931 current_move_package.as_ref(),
932 &modules,
933 upgrade_ticket_policy,
934 )?;
935
936 if self.env.protocol_config.check_for_init_during_upgrade() {
937 let current_module_names: BTreeSet<&str> = current_move_package
940 .serialized_module_map()
941 .keys()
942 .map(|s| s.as_str())
943 .collect();
944 let upgrade_module_names: BTreeSet<&str> = package
945 .serialized_module_map()
946 .keys()
947 .map(|s| s.as_str())
948 .collect();
949 let new_module_names = upgrade_module_names
950 .difference(¤t_module_names)
951 .copied()
952 .collect::<BTreeSet<&str>>();
953 let new_modules = modules
954 .iter()
955 .filter(|m| {
956 let name = m.identifier_at(m.self_handle().name).as_str();
957 new_module_names.contains(name)
958 })
959 .collect::<Vec<&CompiledModule>>();
960 let new_module_has_init = new_modules.iter().any(|module| {
961 module.function_defs.iter().any(|fdef| {
962 let fhandle = module.function_handle_at(fdef.function);
963 let fname = module.identifier_at(fhandle.name);
964 fname == INIT_FN_NAME
965 })
966 });
967 if new_module_has_init {
968 return Err(ExecutionError::new_with_source(
970 ExecutionErrorKind::FeatureNotYetSupported,
971 "`init` in new modules on upgrade is not yet supported",
972 ));
973 }
974 }
975
976 self.env
977 .linkable_store
978 .push_package(storage_id, Rc::new(package))?;
979 Ok(storage_id)
980 }
981
982 pub fn transfer_object(
987 &mut self,
988 recipient: Owner,
989 ty: Type,
990 object: CtxValue,
991 ) -> Result<(), ExecutionError> {
992 self.transfer_object_(recipient, ty, object, false)
993 }
994
995 fn transfer_object_(
996 &mut self,
997 recipient: Owner,
998 ty: Type,
999 object: CtxValue,
1000 end_of_transaction: bool,
1001 ) -> Result<(), ExecutionError> {
1002 let tag = TypeTag::try_from(ty)
1003 .map_err(|_| make_invariant_violation!("Unable to convert Type to TypeTag"))?;
1004 let TypeTag::Struct(tag) = tag else {
1005 invariant_violation!("Expected struct type tag");
1006 };
1007 let ty = MoveObjectType::from(*tag);
1008 object_runtime_mut!(self)?
1009 .transfer(recipient, ty, object.0.into(), end_of_transaction)
1010 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
1011 Ok(())
1012 }
1013
1014 pub fn argument_updates(
1019 &mut self,
1020 args: Vec<T::Argument>,
1021 ) -> Result<Vec<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, ExecutionError> {
1022 args.into_iter()
1023 .filter_map(|arg| self.argument_update(arg).transpose())
1024 .collect()
1025 }
1026
1027 fn argument_update(
1028 &mut self,
1029 sp!(_, (arg, ty)): T::Argument,
1030 ) -> Result<Option<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, ExecutionError> {
1031 use sui_types::transaction::Argument as TxArgument;
1032 let ty = match ty {
1033 Type::Reference(true, inner) => (*inner).clone(),
1034 ty => {
1035 debug_assert!(
1036 false,
1037 "Unexpected non reference type in location update: {ty:?}"
1038 );
1039 return Ok(None);
1040 }
1041 };
1042 let Ok(tag): Result<TypeTag, _> = ty.clone().try_into() else {
1043 invariant_violation!("unable to generate type tag from type")
1044 };
1045 let location = arg.location();
1046 let resolved = self.locations.resolve(location)?;
1047 let local = match resolved {
1048 ResolvedLocation::Local(local)
1049 | ResolvedLocation::Pure { local, .. }
1050 | ResolvedLocation::Receiving { local, .. } => local,
1051 };
1052 if local.is_invalid()? {
1053 return Ok(None);
1054 }
1055 let value = local.copy()?;
1057 let value = match arg {
1058 T::Argument__::Use(_) => {
1059 value.read_ref()?
1061 }
1062 T::Argument__::Borrow(_, _) => {
1063 value
1065 }
1066 T::Argument__::Freeze(_) => {
1067 invariant_violation!("freeze should not be used for a mutable reference")
1068 }
1069 T::Argument__::Read(_) => {
1070 invariant_violation!("read should not return a reference")
1071 }
1072 };
1073 let Some(bytes) = value.serialize() else {
1074 invariant_violation!("Failed to serialize Move value");
1075 };
1076 let arg = match location {
1077 T::Location::TxContext => return Ok(None),
1078 T::Location::GasCoin => TxArgument::GasCoin,
1079 T::Location::Result(i, j) => TxArgument::NestedResult(i, j),
1080 T::Location::ObjectInput(i) => {
1081 TxArgument::Input(self.locations.input_object_metadata[i as usize].0.0)
1082 }
1083 T::Location::PureInput(i) => TxArgument::Input(
1084 self.locations.pure_input_metadata[i as usize]
1085 .original_input_index
1086 .0,
1087 ),
1088 T::Location::ReceivingInput(i) => TxArgument::Input(
1089 self.locations.receiving_input_metadata[i as usize]
1090 .original_input_index
1091 .0,
1092 ),
1093 };
1094 Ok(Some((arg, bytes, tag)))
1095 }
1096
1097 pub fn tracked_results(
1098 &self,
1099 results: &[CtxValue],
1100 result_tys: &T::ResultType,
1101 ) -> Result<Vec<(Vec<u8>, TypeTag)>, ExecutionError> {
1102 assert_invariant!(
1103 results.len() == result_tys.len(),
1104 "results and result types should match"
1105 );
1106 results
1107 .iter()
1108 .zip(result_tys)
1109 .map(|(v, ty)| self.tracked_result(&v.0, ty.clone()))
1110 .collect()
1111 }
1112
1113 fn tracked_result(
1114 &self,
1115 result: &Value,
1116 ty: Type,
1117 ) -> Result<(Vec<u8>, TypeTag), ExecutionError> {
1118 let inner_value;
1119 let (v, ty) = match ty {
1120 Type::Reference(_, inner) => {
1121 inner_value = result.copy()?.read_ref()?;
1122 (&inner_value, (*inner).clone())
1123 }
1124 _ => (result, ty),
1125 };
1126 let Some(bytes) = v.serialize() else {
1127 invariant_violation!("Failed to serialize Move value");
1128 };
1129 let Ok(tag): Result<TypeTag, _> = ty.try_into() else {
1130 invariant_violation!("unable to generate type tag from type")
1131 };
1132 Ok((bytes, tag))
1133 }
1134}
1135
1136impl VMValueCast<CtxValue> for VMValue {
1137 fn cast(self) -> Result<CtxValue, PartialVMError> {
1138 Ok(CtxValue(self.into()))
1139 }
1140}
1141
1142impl CtxValue {
1143 pub fn vec_pack(ty: Type, values: Vec<CtxValue>) -> Result<CtxValue, ExecutionError> {
1144 Ok(CtxValue(Value::vec_pack(
1145 ty,
1146 values.into_iter().map(|v| v.0).collect(),
1147 )?))
1148 }
1149
1150 pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
1151 self.0.coin_ref_value()
1152 }
1153
1154 pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
1155 self.0.coin_ref_subtract_balance(amount)
1156 }
1157
1158 pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
1159 self.0.coin_ref_add_balance(amount)
1160 }
1161
1162 pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
1163 self.0.into_upgrade_ticket()
1164 }
1165}
1166
1167fn load_object_arg(
1168 meter: &mut GasCharger,
1169 env: &Env,
1170 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1171 input: T::ObjectInput,
1172) -> Result<(T::InputIndex, InputObjectMetadata, Value), ExecutionError> {
1173 let id = input.arg.id();
1174 let mutability = input.arg.mutability();
1175 let (metadata, value) =
1176 load_object_arg_impl(meter, env, input_object_map, id, mutability, input.ty)?;
1177 Ok((input.original_input_index, metadata, value))
1178}
1179
1180fn load_object_arg_impl(
1181 meter: &mut GasCharger,
1182 env: &Env,
1183 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1184 id: ObjectID,
1185 mutability: ObjectMutability,
1186 ty: T::Type,
1187) -> Result<(InputObjectMetadata, Value), ExecutionError> {
1188 let obj = env.read_object(&id)?;
1189 let owner = obj.owner.clone();
1190 let version = obj.version();
1191 let object_metadata = InputObjectMetadata {
1192 id,
1193 mutability,
1194 owner: owner.clone(),
1195 version,
1196 type_: ty.clone(),
1197 };
1198 let sui_types::object::ObjectInner {
1199 data: sui_types::object::Data::Move(move_obj),
1200 ..
1201 } = obj.as_inner()
1202 else {
1203 invariant_violation!("Expected a Move object");
1204 };
1205 let contained_uids = {
1206 let fully_annotated_layout = env.fully_annotated_layout(&ty)?;
1207 get_all_uids(&fully_annotated_layout, move_obj.contents()).map_err(|e| {
1208 make_invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1209 })?
1210 };
1211 input_object_map.insert(
1212 id,
1213 object_runtime::InputObject {
1214 contained_uids,
1215 version,
1216 owner,
1217 },
1218 );
1219
1220 let v = Value::deserialize(env, move_obj.contents(), ty)?;
1221 charge_gas_!(meter, env, charge_copy_loc, &v)?;
1222 Ok((object_metadata, v))
1223}
1224
1225fn load_pure_value(
1226 meter: &mut GasCharger,
1227 env: &Env,
1228 bytes: &[u8],
1229 metadata: &T::PureInput,
1230) -> Result<Value, ExecutionError> {
1231 let loaded = Value::deserialize(env, bytes, metadata.ty.clone())?;
1232 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1234 Ok(loaded)
1235}
1236
1237fn load_receiving_value(
1238 meter: &mut GasCharger,
1239 env: &Env,
1240 metadata: &T::ReceivingInput,
1241) -> Result<Value, ExecutionError> {
1242 let (id, version, _) = metadata.object_ref;
1243 let loaded = Value::receiving(id, version);
1244 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1245 Ok(loaded)
1246}
1247
1248fn copy_value(meter: &mut GasCharger, env: &Env, value: &Value) -> Result<Value, ExecutionError> {
1249 charge_gas_!(meter, env, charge_copy_loc, value)?;
1250 value.copy()
1251}
1252
1253fn refund_max_gas_budget<OType>(
1256 writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1257 gas_charger: &mut GasCharger,
1258 gas_id: ObjectID,
1259) -> Result<(), ExecutionError> {
1260 let Some((_, _, value_ref)) = writes.get_mut(&gas_id) else {
1261 invariant_violation!("Gas object cannot be wrapped or destroyed")
1262 };
1263 let value = std::mem::replace(value_ref, VMValue::u8(0));
1265 let mut locals = Locals::new([Some(value.into())])?;
1266 let mut local = locals.local(0)?;
1267 let coin_value = local.borrow()?.coin_ref_value()?;
1268 let additional = gas_charger.gas_budget();
1269 if coin_value.checked_add(additional).is_none() {
1270 return Err(ExecutionError::new_with_source(
1271 ExecutionErrorKind::CoinBalanceOverflow,
1272 "Gas coin too large after returning the max gas budget",
1273 ));
1274 };
1275 local.borrow()?.coin_ref_add_balance(additional)?;
1276 *value_ref = local.move_()?.into();
1278 Ok(())
1279}
1280
1281unsafe fn create_written_object<Mode: ExecutionMode>(
1287 env: &Env,
1288 objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1289 id: ObjectID,
1290 type_: Type,
1291 has_public_transfer: bool,
1292 contents: Vec<u8>,
1293) -> Result<MoveObject, ExecutionError> {
1294 debug_assert_eq!(
1295 id,
1296 MoveObject::id_opt(&contents).expect("object contents should start with an id")
1297 );
1298 let old_obj_ver = objects_modified_at
1299 .get(&id)
1300 .map(|obj: &LoadedRuntimeObject| obj.version);
1301
1302 let Ok(type_tag): Result<TypeTag, _> = type_.try_into() else {
1303 invariant_violation!("unable to generate type tag from type")
1304 };
1305
1306 let struct_tag = match type_tag {
1307 TypeTag::Struct(inner) => *inner,
1308 _ => invariant_violation!("Non struct type for object"),
1309 };
1310 unsafe {
1311 MoveObject::new_from_execution(
1312 struct_tag.into(),
1313 has_public_transfer,
1314 old_obj_ver.unwrap_or_default(),
1315 contents,
1316 env.protocol_config,
1317 Mode::packages_are_predefined(),
1318 )
1319 }
1320}