sui_adapter_v3/static_programmable_transactions/execution/
context.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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::{
15            trace_utils,
16            values::{Local, Locals, Value},
17        },
18        linkage::resolved_linkage::{ResolvedLinkage, RootedLinkage},
19        loading::ast::{Datatype, ObjectMutability},
20        typing::ast::{self as T, Type},
21    },
22};
23use indexmap::{IndexMap, IndexSet};
24use move_binary_format::{
25    CompiledModule,
26    errors::{Location, PartialVMError, VMResult},
27    file_format::FunctionDefinitionIndex,
28    file_format_common::VERSION_6,
29};
30use move_core_types::{
31    account_address::AccountAddress,
32    identifier::IdentStr,
33    language_storage::{ModuleId, StructTag},
34};
35use move_trace_format::format::MoveTraceBuilder;
36use move_vm_runtime::native_extensions::NativeContextExtensions;
37use move_vm_types::{
38    gas::{GasMeter, SimpleInstruction},
39    values::{VMValueCast, Value as VMValue},
40};
41use std::{
42    cell::RefCell,
43    collections::{BTreeMap, BTreeSet},
44    rc::Rc,
45    sync::Arc,
46};
47use sui_move_natives::object_runtime::{
48    self, LoadedRuntimeObject, ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
49};
50use sui_types::{
51    TypeTag,
52    base_types::{MoveObjectType, ObjectID, SequenceNumber, TxContext},
53    error::{ExecutionError, SafeIndex},
54    execution::ExecutionResults,
55    execution_status::ExecutionErrorKind,
56    metrics::LimitsMetrics,
57    move_package::{MovePackage, UpgradeCap, UpgradeReceipt, UpgradeTicket},
58    object::{MoveObject, Object, Owner},
59};
60use sui_verifier::INIT_FN_NAME;
61use tracing::instrument;
62
63macro_rules! unwrap {
64    ($e:expr, $($args:expr),* $(,)?) => {
65        match $e {
66            Some(v) => v,
67            None => {
68                invariant_violation!("Unexpected none: {}", format!($($args),*))
69            }
70        }
71
72    };
73}
74
75macro_rules! object_runtime_mut {
76    ($context:ident) => {{
77        $context
78            .native_extensions
79            .get_mut::<ObjectRuntime>()
80            .map_err(|e| $context.env.convert_vm_error(e.finish(Location::Undefined)))
81    }};
82}
83
84macro_rules! charge_gas_ {
85    ($gas_charger:expr, $env:expr, $call:ident($($args:expr),*)) => {{
86        SuiGasMeter($gas_charger.move_gas_status_mut())
87            .$call($($args),*)
88            .map_err(|e| $env.convert_vm_error(e.finish(Location::Undefined)))
89    }};
90    ($gas_charger:expr, $env:expr, $case:ident, $value_view:expr) => {
91        charge_gas_!($gas_charger, $env, $case($value_view))
92    };
93}
94
95macro_rules! charge_gas {
96    ($context:ident, $case:ident, $value_view:expr) => {{ charge_gas_!($context.gas_charger, $context.env, $case, $value_view) }};
97}
98
99/// Type wrapper around Value to ensure safe usage
100#[derive(Debug)]
101pub struct CtxValue(Value);
102
103#[derive(Clone, Debug)]
104pub struct InputObjectMetadata {
105    pub id: ObjectID,
106    pub mutability: ObjectMutability,
107    pub owner: Owner,
108    pub version: SequenceNumber,
109    pub type_: Type,
110}
111
112#[derive(Copy, Clone)]
113enum UsageKind {
114    Move,
115    Copy,
116    Borrow,
117}
118
119// Locals and metadata for all `Location`s. Separated from `Context` for lifetime reasons.
120struct Locations {
121    // A single local for holding the TxContext
122    tx_context_value: Locals,
123    /// The runtime value for the Gas coin, None if no gas coin is provided
124    gas: Option<(InputObjectMetadata, Locals)>,
125    /// The runtime value for the input objects args
126    input_object_metadata: Vec<(T::InputIndex, InputObjectMetadata)>,
127    object_inputs: Locals,
128    input_withdrawal_metadata: Vec<T::WithdrawalInput>,
129    withdrawal_inputs: Locals,
130    pure_input_bytes: IndexSet<Vec<u8>>,
131    pure_input_metadata: Vec<T::PureInput>,
132    pure_inputs: Locals,
133    receiving_input_metadata: Vec<T::ReceivingInput>,
134    receiving_inputs: Locals,
135    /// The results of a given command. For most commands, the inner vector will have length 1.
136    /// It will only not be 1 for Move calls with multiple return values.
137    /// Inner values are None if taken/moved by-value
138    results: Vec<Locals>,
139}
140
141enum ResolvedLocation<'a> {
142    Local(Local<'a>),
143    Pure {
144        bytes: &'a [u8],
145        metadata: &'a T::PureInput,
146        local: Local<'a>,
147    },
148    Receiving {
149        metadata: &'a T::ReceivingInput,
150        local: Local<'a>,
151    },
152}
153
154/// Maintains all runtime state specific to programmable transactions
155pub struct Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas> {
156    pub env: &'env Env<'pc, 'vm, 'state, 'linkage>,
157    /// Metrics for reporting exceeded limits
158    pub metrics: Arc<LimitsMetrics>,
159    pub native_extensions: NativeContextExtensions<'env>,
160    /// A shared transaction context, contains transaction digest information and manages the
161    /// creation of new object IDs
162    pub tx_context: Rc<RefCell<TxContext>>,
163    /// The gas charger used for metering
164    pub gas_charger: &'gas mut GasCharger,
165    /// User events are claimed after each Move call
166    user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
167    // runtime data
168    locations: Locations,
169}
170
171impl Locations {
172    /// NOTE! This does not charge gas and should not be used directly. It is exposed for
173    /// dev-inspect
174    fn resolve(&mut self, location: T::Location) -> Result<ResolvedLocation<'_>, ExecutionError> {
175        Ok(match location {
176            T::Location::TxContext => ResolvedLocation::Local(self.tx_context_value.local(0)?),
177            T::Location::GasCoin => {
178                let (_, gas_locals) = unwrap!(self.gas.as_mut(), "Gas coin not provided");
179                ResolvedLocation::Local(gas_locals.local(0)?)
180            }
181            T::Location::ObjectInput(i) => ResolvedLocation::Local(self.object_inputs.local(i)?),
182            T::Location::WithdrawalInput(i) => {
183                ResolvedLocation::Local(self.withdrawal_inputs.local(i)?)
184            }
185            T::Location::Result(i, j) => {
186                let result = unwrap!(self.results.get_mut(i as usize), "bounds already verified");
187                ResolvedLocation::Local(result.local(j)?)
188            }
189            T::Location::PureInput(i) => {
190                let local = self.pure_inputs.local(i)?;
191                let metadata = &self.pure_input_metadata.safe_get(i as usize)?;
192                let bytes = self
193                    .pure_input_bytes
194                    .get_index(metadata.byte_index)
195                    .ok_or_else(|| {
196                        make_invariant_violation!(
197                            "Pure input {} bytes out of bounds at index {}",
198                            metadata.original_input_index.0,
199                            metadata.byte_index,
200                        )
201                    })?;
202                ResolvedLocation::Pure {
203                    bytes,
204                    metadata,
205                    local,
206                }
207            }
208            T::Location::ReceivingInput(i) => ResolvedLocation::Receiving {
209                metadata: self.receiving_input_metadata.safe_get(i as usize)?,
210                local: self.receiving_inputs.local(i)?,
211            },
212        })
213    }
214}
215
216impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas> {
217    #[instrument(name = "Context::new", level = "trace", skip_all)]
218    pub fn new(
219        env: &'env Env<'pc, 'vm, 'state, 'linkage>,
220        metrics: Arc<LimitsMetrics>,
221        tx_context: Rc<RefCell<TxContext>>,
222        gas_charger: &'gas mut GasCharger,
223        gas_coin: Option<ObjectID>,
224        pure_input_bytes: IndexSet<Vec<u8>>,
225        object_inputs: Vec<T::ObjectInput>,
226        input_withdrawal_metadata: Vec<T::WithdrawalInput>,
227        pure_input_metadata: Vec<T::PureInput>,
228        receiving_input_metadata: Vec<T::ReceivingInput>,
229    ) -> Result<Self, ExecutionError>
230    where
231        'pc: 'state,
232    {
233        let mut input_object_map = BTreeMap::new();
234        let mut input_object_metadata = Vec::with_capacity(object_inputs.len());
235        let mut object_values = Vec::with_capacity(object_inputs.len());
236        for object_input in object_inputs {
237            let (i, m, v) = load_object_arg(gas_charger, env, &mut input_object_map, object_input)?;
238            input_object_metadata.push((i, m));
239            object_values.push(Some(v));
240        }
241        let object_inputs = Locals::new(object_values)?;
242        let mut withdrawal_values = Vec::with_capacity(input_withdrawal_metadata.len());
243        for withdrawal_input in &input_withdrawal_metadata {
244            let v = load_withdrawal_arg(gas_charger, env, withdrawal_input)?;
245            withdrawal_values.push(Some(v));
246        }
247        let withdrawal_inputs = Locals::new(withdrawal_values)?;
248        let pure_inputs = Locals::new_invalid(pure_input_metadata.len())?;
249        let receiving_inputs = Locals::new_invalid(receiving_input_metadata.len())?;
250        let gas = match gas_coin {
251            Some(gas_coin) => {
252                let ty = env.gas_coin_type()?;
253                let (gas_metadata, gas_value) = load_object_arg_impl(
254                    gas_charger,
255                    env,
256                    &mut input_object_map,
257                    gas_coin,
258                    ObjectMutability::Mutable,
259                    ty,
260                )?;
261                let mut gas_locals = Locals::new([Some(gas_value)])?;
262                let gas_local = gas_locals.local(0)?;
263                let gas_ref = gas_local.borrow()?;
264                // We have already checked that the gas balance is enough to cover the gas budget
265                let max_gas_in_balance = gas_charger.gas_budget();
266                gas_ref.coin_ref_subtract_balance(max_gas_in_balance)?;
267                Some((gas_metadata, gas_locals))
268            }
269            None => None,
270        };
271        let native_extensions = adapter::new_native_extensions(
272            env.state_view.as_child_resolver(),
273            input_object_map,
274            !gas_charger.is_unmetered(),
275            env.protocol_config,
276            metrics.clone(),
277            tx_context.clone(),
278        );
279
280        debug_assert_eq!(gas_charger.move_gas_status().stack_height_current(), 0);
281        let tx_context_value = Locals::new(vec![Some(Value::new_tx_context(
282            tx_context.borrow().digest(),
283        )?)])?;
284        Ok(Self {
285            env,
286            metrics,
287            native_extensions,
288            tx_context,
289            gas_charger,
290            user_events: vec![],
291            locations: Locations {
292                tx_context_value,
293                gas,
294                input_object_metadata,
295                object_inputs,
296                input_withdrawal_metadata,
297                withdrawal_inputs,
298                pure_input_bytes,
299                pure_input_metadata,
300                pure_inputs,
301                receiving_input_metadata,
302                receiving_inputs,
303                results: vec![],
304            },
305        })
306    }
307
308    pub fn finish<Mode: ExecutionMode>(mut self) -> Result<ExecutionResults, ExecutionError> {
309        assert_invariant!(
310            !self.locations.tx_context_value.local(0)?.is_invalid()?,
311            "tx context value should be present"
312        );
313        let gas = std::mem::take(&mut self.locations.gas);
314        let object_input_metadata = std::mem::take(&mut self.locations.input_object_metadata);
315        let mut object_inputs =
316            std::mem::replace(&mut self.locations.object_inputs, Locals::new_invalid(0)?);
317        let mut loaded_runtime_objects = BTreeMap::new();
318        let mut by_value_shared_objects = BTreeSet::new();
319        let mut consensus_owner_objects = BTreeMap::new();
320        let gas = gas
321            .map(|(m, mut g)| Result::<_, ExecutionError>::Ok((m, g.local(0)?.move_if_valid()?)))
322            .transpose()?;
323        let gas_id_opt = gas.as_ref().map(|(m, _)| m.id);
324        let object_inputs = object_input_metadata
325            .into_iter()
326            .enumerate()
327            .map(|(i, (_, m))| {
328                let v_opt = object_inputs.local(checked_as!(i, u16)?)?.move_if_valid()?;
329                Ok((m, v_opt))
330            })
331            .collect::<Result<Vec<_>, ExecutionError>>()?;
332        for (metadata, value_opt) in object_inputs.into_iter().chain(gas) {
333            let InputObjectMetadata {
334                id,
335                mutability,
336                owner,
337                version,
338                type_,
339            } = metadata;
340            match mutability {
341                ObjectMutability::Immutable => continue,
342                // It is illegal to mutate NonExclusiveWrites, but they are passed as &mut T,
343                // so we need to treat them as mutable here. After execution, we check if they
344                // have been mutated, and abort the tx if they have.
345                ObjectMutability::NonExclusiveWrite | ObjectMutability::Mutable => (),
346            }
347            loaded_runtime_objects.insert(
348                id,
349                LoadedRuntimeObject {
350                    version,
351                    is_modified: true,
352                },
353            );
354            if let Some(object) = value_opt {
355                self.transfer_object_(
356                    owner,
357                    type_,
358                    CtxValue(object),
359                    /* end of transaction */ true,
360                )?;
361            } else if owner.is_shared() {
362                by_value_shared_objects.insert(id);
363            } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
364                consensus_owner_objects.insert(id, owner.clone());
365            }
366        }
367
368        let Self {
369            env,
370            mut native_extensions,
371            tx_context,
372            gas_charger,
373            user_events,
374            ..
375        } = self;
376        let ref_context: &RefCell<TxContext> = &tx_context;
377        let tx_context: &TxContext = &ref_context.borrow();
378        let tx_digest = ref_context.borrow().digest();
379
380        let object_runtime: ObjectRuntime = native_extensions
381            .remove()
382            .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
383
384        let RuntimeResults {
385            mut writes,
386            user_events: remaining_events,
387            loaded_child_objects,
388            mut created_object_ids,
389            deleted_object_ids,
390            accumulator_events,
391            settlement_input_sui,
392            settlement_output_sui,
393        } = object_runtime.finish()?;
394        assert_invariant!(
395            remaining_events.is_empty(),
396            "Events should be taken after every Move call"
397        );
398        // Refund unused gas
399        if let Some(gas_id) = gas_id_opt {
400            refund_max_gas_budget(&mut writes, gas_charger, gas_id)?;
401        }
402
403        loaded_runtime_objects.extend(loaded_child_objects);
404
405        let mut written_objects = BTreeMap::new();
406
407        for (id, (recipient, ty, value)) in writes {
408            let (ty, layout) = env.load_type_and_layout_from_struct(&ty.clone().into())?;
409            let abilities = ty.abilities();
410            let has_public_transfer = abilities.has_store();
411            let Some(bytes) = value.typed_serialize(&layout) else {
412                invariant_violation!("Failed to serialize already deserialized Move value");
413            };
414            // safe because has_public_transfer has been determined by the abilities
415            let move_object = unsafe {
416                create_written_object::<Mode>(
417                    env,
418                    &loaded_runtime_objects,
419                    id,
420                    ty,
421                    has_public_transfer,
422                    bytes,
423                )?
424            };
425            let object = Object::new_move(move_object, recipient, tx_digest);
426            written_objects.insert(id, object);
427        }
428
429        for package in self.env.linkable_store.to_new_packages().into_iter() {
430            let package_obj = Object::new_from_package(package, tx_digest);
431            let id = package_obj.id();
432            created_object_ids.insert(id);
433            written_objects.insert(id, package_obj);
434        }
435
436        legacy_ptb::context::finish(
437            env.protocol_config,
438            env.state_view,
439            gas_charger,
440            tx_context,
441            &by_value_shared_objects,
442            &consensus_owner_objects,
443            loaded_runtime_objects,
444            written_objects,
445            created_object_ids,
446            deleted_object_ids,
447            user_events,
448            accumulator_events,
449            settlement_input_sui,
450            settlement_output_sui,
451        )
452    }
453
454    pub fn object_runtime(&self) -> Result<&ObjectRuntime<'_>, ExecutionError> {
455        self.native_extensions
456            .get::<ObjectRuntime>()
457            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))
458    }
459
460    pub fn take_user_events(
461        &mut self,
462        storage_id: ModuleId,
463        function_def_idx: FunctionDefinitionIndex,
464        instr_length: u16,
465        linkage: &RootedLinkage,
466    ) -> Result<(), ExecutionError> {
467        let events = object_runtime_mut!(self)?.take_user_events();
468        let Some(num_events) = self.user_events.len().checked_add(events.len()) else {
469            invariant_violation!("usize overflow, too many events emitted")
470        };
471        let max_events = self.env.protocol_config.max_num_event_emit();
472        if num_events as u64 > max_events {
473            let err = max_event_error(max_events)
474                .at_code_offset(function_def_idx, instr_length)
475                .finish(Location::Module(storage_id.clone()));
476            return Err(self.env.convert_linked_vm_error(err, linkage));
477        }
478        let new_events = events
479            .into_iter()
480            .map(|(tag, value)| {
481                let layout = self.env.type_layout_for_struct(&tag)?;
482                let Some(bytes) = value.typed_serialize(&layout) else {
483                    invariant_violation!("Failed to serialize Move event");
484                };
485                Ok((storage_id.clone(), tag, bytes))
486            })
487            .collect::<Result<Vec<_>, ExecutionError>>()?;
488        self.user_events.extend(new_events);
489        Ok(())
490    }
491
492    //
493    // Arguments and Values
494    //
495
496    fn location(
497        &mut self,
498        usage: UsageKind,
499        location: T::Location,
500    ) -> Result<Value, ExecutionError> {
501        let resolved = self.locations.resolve(location)?;
502        let mut local = match resolved {
503            ResolvedLocation::Local(l) => l,
504            ResolvedLocation::Pure {
505                bytes,
506                metadata,
507                mut local,
508            } => {
509                if local.is_invalid()? {
510                    let v = load_pure_value(self.gas_charger, self.env, bytes, metadata)?;
511                    local.store(v)?;
512                }
513                local
514            }
515            ResolvedLocation::Receiving {
516                metadata,
517                mut local,
518            } => {
519                if local.is_invalid()? {
520                    let v = load_receiving_value(self.gas_charger, self.env, metadata)?;
521                    local.store(v)?;
522                }
523                local
524            }
525        };
526        Ok(match usage {
527            UsageKind::Move => {
528                let value = local.move_()?;
529                charge_gas_!(self.gas_charger, self.env, charge_move_loc, &value)?;
530                value
531            }
532            UsageKind::Copy => {
533                let value = local.copy()?;
534                charge_gas_!(self.gas_charger, self.env, charge_copy_loc, &value)?;
535                value
536            }
537            UsageKind::Borrow => {
538                charge_gas_!(
539                    self.gas_charger,
540                    self.env,
541                    charge_simple_instr(SimpleInstruction::MutBorrowLoc)
542                )?;
543                local.borrow()?
544            }
545        })
546    }
547
548    fn location_usage(&mut self, usage: T::Usage) -> Result<Value, ExecutionError> {
549        match usage {
550            T::Usage::Move(location) => self.location(UsageKind::Move, location),
551            T::Usage::Copy { location, .. } => self.location(UsageKind::Copy, location),
552        }
553    }
554
555    fn argument_value(&mut self, sp!(_, (arg_, _)): T::Argument) -> Result<Value, ExecutionError> {
556        match arg_ {
557            T::Argument__::Use(usage) => self.location_usage(usage),
558            // freeze is a no-op for references since the value does not track mutability
559            T::Argument__::Freeze(usage) => self.location_usage(usage),
560            T::Argument__::Borrow(_, location) => self.location(UsageKind::Borrow, location),
561            T::Argument__::Read(usage) => {
562                let reference = self.location_usage(usage)?;
563                charge_gas!(self, charge_read_ref, &reference)?;
564                reference.read_ref()
565            }
566        }
567    }
568
569    pub fn argument<V>(&mut self, arg: T::Argument) -> Result<V, ExecutionError>
570    where
571        VMValue: VMValueCast<V>,
572    {
573        let before_height = self.gas_charger.move_gas_status().stack_height_current();
574        let value = self.argument_value(arg)?;
575        let after_height = self.gas_charger.move_gas_status().stack_height_current();
576        debug_assert_eq!(before_height.saturating_add(1), after_height);
577        let value: V = value.cast()?;
578        Ok(value)
579    }
580
581    pub fn arguments<V>(&mut self, args: Vec<T::Argument>) -> Result<Vec<V>, ExecutionError>
582    where
583        VMValue: VMValueCast<V>,
584    {
585        args.into_iter().map(|arg| self.argument(arg)).collect()
586    }
587
588    pub fn result(&mut self, result: Vec<Option<CtxValue>>) -> Result<(), ExecutionError> {
589        self.locations
590            .results
591            .push(Locals::new(result.into_iter().map(|v| v.map(|v| v.0)))?);
592        Ok(())
593    }
594
595    pub fn charge_command(
596        &mut self,
597        is_move_call: bool,
598        num_args: usize,
599        num_return: usize,
600    ) -> Result<(), ExecutionError> {
601        let move_gas_status = self.gas_charger.move_gas_status_mut();
602        let before_size = move_gas_status.stack_size_current();
603        // Pop all of the arguments
604        // If the return values came from the Move VM directly (via a Move call), pop those
605        // as well
606        let num_popped = if is_move_call {
607            num_args.checked_add(num_return).ok_or_else(|| {
608                make_invariant_violation!("usize overflow when charging gas for command",)
609            })?
610        } else {
611            num_args
612        };
613        move_gas_status
614            .charge(1, 0, num_popped as u64, 0, /* unused */ 1)
615            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
616        let after_size = move_gas_status.stack_size_current();
617        assert_invariant!(
618            before_size == after_size,
619            "We assume currently that the stack size is not decremented. \
620            If this changes, we need to actually account for it here"
621        );
622        Ok(())
623    }
624
625    pub fn copy_value(&mut self, value: &CtxValue) -> Result<CtxValue, ExecutionError> {
626        Ok(CtxValue(copy_value(self.gas_charger, self.env, &value.0)?))
627    }
628
629    pub fn new_coin(&mut self, amount: u64) -> Result<CtxValue, ExecutionError> {
630        let id = self.tx_context.borrow_mut().fresh_id();
631        object_runtime_mut!(self)?
632            .new_id(id)
633            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
634        Ok(CtxValue(Value::coin(id, amount)))
635    }
636
637    pub fn destroy_coin(&mut self, coin: CtxValue) -> Result<u64, ExecutionError> {
638        let (id, amount) = coin.0.unpack_coin()?;
639        object_runtime_mut!(self)?
640            .delete_id(id)
641            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
642        Ok(amount)
643    }
644
645    pub fn new_upgrade_cap(&mut self, storage_id: ObjectID) -> Result<CtxValue, ExecutionError> {
646        let id = self.tx_context.borrow_mut().fresh_id();
647        object_runtime_mut!(self)?
648            .new_id(id)
649            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
650        let cap = UpgradeCap::new(id, storage_id);
651        Ok(CtxValue(Value::upgrade_cap(cap)))
652    }
653
654    pub fn upgrade_receipt(
655        &self,
656        upgrade_ticket: UpgradeTicket,
657        upgraded_package_id: ObjectID,
658    ) -> CtxValue {
659        let receipt = UpgradeReceipt::new(upgrade_ticket, upgraded_package_id);
660        CtxValue(Value::upgrade_receipt(receipt))
661    }
662
663    //
664    // Move calls
665    //
666
667    pub fn vm_move_call(
668        &mut self,
669        function: T::LoadedFunction,
670        args: Vec<CtxValue>,
671        trace_builder_opt: &mut Option<MoveTraceBuilder>,
672    ) -> Result<Vec<CtxValue>, ExecutionError> {
673        let result = self.execute_function_bypass_visibility(
674            &function.runtime_id,
675            &function.name,
676            &function.type_arguments,
677            args,
678            &function.linkage,
679            trace_builder_opt,
680        )?;
681        self.take_user_events(
682            function.storage_id,
683            function.definition_index,
684            function.instruction_length,
685            &function.linkage,
686        )?;
687        Ok(result)
688    }
689
690    pub fn execute_function_bypass_visibility(
691        &mut self,
692        runtime_id: &ModuleId,
693        function_name: &IdentStr,
694        ty_args: &[Type],
695        args: Vec<CtxValue>,
696        linkage: &RootedLinkage,
697        tracer: &mut Option<MoveTraceBuilder>,
698    ) -> Result<Vec<CtxValue>, ExecutionError> {
699        let ty_args = ty_args
700            .iter()
701            .enumerate()
702            .map(|(idx, ty)| self.env.load_vm_type_argument_from_adapter_type(idx, ty))
703            .collect::<Result<_, _>>()?;
704        let gas_status = self.gas_charger.move_gas_status_mut();
705        let mut data_store = LinkedDataStore::new(linkage, self.env.linkable_store);
706        let values = self
707            .env
708            .vm
709            .get_runtime()
710            .execute_function_with_values_bypass_visibility(
711                runtime_id,
712                function_name,
713                ty_args,
714                args.into_iter().map(|v| v.0.into()).collect(),
715                &mut data_store,
716                &mut SuiGasMeter(gas_status),
717                &mut self.native_extensions,
718                tracer.as_mut(),
719            )
720            .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
721        Ok(values.into_iter().map(|v| CtxValue(v.into())).collect())
722    }
723
724    //
725    // Publish and Upgrade
726    //
727
728    // is_upgrade is used for gas charging. Assumed to be a new publish if false.
729    pub fn deserialize_modules(
730        &mut self,
731        module_bytes: &[Vec<u8>],
732        is_upgrade: bool,
733    ) -> Result<Vec<CompiledModule>, ExecutionError> {
734        assert_invariant!(
735            !module_bytes.is_empty(),
736            "empty package is checked in transaction input checker"
737        );
738        let total_bytes = module_bytes.iter().map(|v| v.len()).sum();
739        if is_upgrade {
740            self.gas_charger.charge_upgrade_package(total_bytes)?
741        } else {
742            self.gas_charger.charge_publish_package(total_bytes)?
743        }
744
745        let binary_config = self.env.protocol_config.binary_config(None);
746        let modules = module_bytes
747            .iter()
748            .map(|b| {
749                CompiledModule::deserialize_with_config(b, &binary_config)
750                    .map_err(|e| e.finish(Location::Undefined))
751            })
752            .collect::<VMResult<Vec<CompiledModule>>>()
753            .map_err(|e| self.env.convert_vm_error(e))?;
754        Ok(modules)
755    }
756
757    fn fetch_package(
758        &mut self,
759        dependency_id: &ObjectID,
760    ) -> Result<Rc<MovePackage>, ExecutionError> {
761        let [fetched_package] = self.fetch_packages(&[*dependency_id])?.try_into().map_err(
762            |_| {
763                make_invariant_violation!(
764                    "We should always fetch a single package for each object or return a dependency error."
765                )
766            },
767        )?;
768        Ok(fetched_package)
769    }
770
771    fn fetch_packages(
772        &mut self,
773        dependency_ids: &[ObjectID],
774    ) -> Result<Vec<Rc<MovePackage>>, ExecutionError> {
775        let mut fetched = vec![];
776        let mut missing = vec![];
777
778        // Collect into a set to avoid duplicate fetches and preserve existing behavior
779        let dependency_ids: BTreeSet<_> = dependency_ids.iter().collect();
780
781        for id in &dependency_ids {
782            match self.env.linkable_store.get_package(id) {
783                Err(e) => {
784                    return Err(ExecutionError::new_with_source(
785                        ExecutionErrorKind::PublishUpgradeMissingDependency,
786                        e,
787                    ));
788                }
789                Ok(Some(inner)) => {
790                    fetched.push(inner);
791                }
792                Ok(None) => {
793                    missing.push(*id);
794                }
795            }
796        }
797
798        if missing.is_empty() {
799            assert_invariant!(
800                fetched.len() == dependency_ids.len(),
801                "all dependencies should be fetched"
802            );
803            Ok(fetched)
804        } else {
805            let msg = format!(
806                "Missing dependencies: {}",
807                missing
808                    .into_iter()
809                    .map(|dep| format!("{}", dep))
810                    .collect::<Vec<_>>()
811                    .join(", ")
812            );
813            Err(ExecutionError::new_with_source(
814                ExecutionErrorKind::PublishUpgradeMissingDependency,
815                msg,
816            ))
817        }
818    }
819
820    fn publish_and_verify_modules(
821        &mut self,
822        package_id: ObjectID,
823        modules: &[CompiledModule],
824        linkage: &RootedLinkage,
825    ) -> Result<(), ExecutionError> {
826        // TODO(https://github.com/MystenLabs/sui/issues/69): avoid this redundant serialization by exposing VM API that allows us to run the linker directly on `Vec<CompiledModule>`
827        let binary_version = self.env.protocol_config.move_binary_format_version();
828        let new_module_bytes: Vec<_> = modules
829            .iter()
830            .map(|m| {
831                let mut bytes = Vec::new();
832                let version = if binary_version > VERSION_6 {
833                    m.version
834                } else {
835                    VERSION_6
836                };
837                m.serialize_with_version(version, &mut bytes).unwrap();
838                bytes
839            })
840            .collect();
841        let mut data_store = LinkedDataStore::new(linkage, self.env.linkable_store);
842        self.env
843            .vm
844            .get_runtime()
845            .publish_module_bundle(
846                new_module_bytes,
847                AccountAddress::from(package_id),
848                &mut data_store,
849                &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
850            )
851            .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
852
853        // run the Sui verifier
854        for module in modules {
855            // Run Sui bytecode verifier, which runs some additional checks that assume the Move
856            // bytecode verifier has passed.
857            sui_verifier::verifier::sui_verify_module_unmetered(
858                module,
859                &BTreeMap::new(),
860                &self
861                    .env
862                    .protocol_config
863                    .verifier_config(/* signing_limits */ None),
864            )?;
865        }
866
867        Ok(())
868    }
869
870    fn init_modules(
871        &mut self,
872        package_id: ObjectID,
873        modules: &[CompiledModule],
874        linkage: &RootedLinkage,
875        trace_builder_opt: &mut Option<MoveTraceBuilder>,
876    ) -> Result<(), ExecutionError> {
877        for module in modules {
878            let Some((fdef_idx, fdef)) = module.find_function_def_by_name(INIT_FN_NAME.as_str())
879            else {
880                continue;
881            };
882            let fhandle = module.function_handle_at(fdef.function);
883            let fparameters = module.signature_at(fhandle.parameters);
884            assert_invariant!(
885                fparameters.0.len() <= 2,
886                "init function should have at most 2 parameters"
887            );
888            let has_otw = fparameters.0.len() == 2;
889            let tx_context = self
890                .location(UsageKind::Borrow, T::Location::TxContext)
891                .map_err(|e| {
892                    make_invariant_violation!("Failed to get tx context for init function: {}", e)
893                })?;
894            // balance the stack after borrowing the tx context
895            charge_gas!(self, charge_store_loc, &tx_context)?;
896
897            let args = if has_otw {
898                vec![CtxValue(Value::one_time_witness()?), CtxValue(tx_context)]
899            } else {
900                vec![CtxValue(tx_context)]
901            };
902            debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
903            trace_utils::trace_move_call_start(trace_builder_opt);
904            let return_values = self.execute_function_bypass_visibility(
905                &module.self_id(),
906                INIT_FN_NAME,
907                &[],
908                args,
909                linkage,
910                trace_builder_opt,
911            )?;
912            trace_utils::trace_move_call_end(trace_builder_opt);
913
914            let storage_id = ModuleId::new(package_id.into(), module.self_id().name().to_owned());
915            self.take_user_events(
916                storage_id,
917                fdef_idx,
918                fdef.code
919                    .as_ref()
920                    .map(|c| checked_as!(c.code.len(), u16))
921                    .transpose()?
922                    .unwrap_or(0),
923                linkage,
924            )?;
925            assert_invariant!(
926                return_values.is_empty(),
927                "init should not have return values"
928            );
929            debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
930        }
931
932        Ok(())
933    }
934
935    pub fn publish_and_init_package<Mode: ExecutionMode>(
936        &mut self,
937        mut modules: Vec<CompiledModule>,
938        dep_ids: &[ObjectID],
939        linkage: ResolvedLinkage,
940        trace_builder_opt: &mut Option<MoveTraceBuilder>,
941    ) -> Result<ObjectID, ExecutionError> {
942        let runtime_id = if <Mode>::packages_are_predefined() {
943            // do not calculate or substitute id for predefined packages
944            (*modules.safe_get(0)?.self_id().address()).into()
945        } else {
946            // It should be fine that this does not go through the object runtime since it does not
947            // need to know about new packages created, since Move objects and Move packages
948            // cannot interact
949            let id = self.tx_context.borrow_mut().fresh_id();
950            adapter::substitute_package_id(&mut modules, id)?;
951            id
952        };
953
954        let dependencies = self.fetch_packages(dep_ids)?;
955        let package = Rc::new(MovePackage::new_initial(
956            &modules,
957            self.env.protocol_config,
958            dependencies.iter().map(|p| p.as_ref()),
959        )?);
960        let package_id = package.id();
961
962        // Here we optimistically push the package that is being published/upgraded
963        // and if there is an error of any kind (verification or module init) we
964        // remove it.
965        // The call to `pop_last_package` later is fine because we cannot re-enter and
966        // the last package we pushed is the one we are verifying and running the init from
967        let linkage = RootedLinkage::new_for_publication(package_id, runtime_id, linkage);
968
969        self.env.linkable_store.push_package(package_id, package)?;
970        let res = self
971            .publish_and_verify_modules(runtime_id, &modules, &linkage)
972            .and_then(|_| self.init_modules(package_id, &modules, &linkage, trace_builder_opt));
973        match res {
974            Ok(()) => Ok(runtime_id),
975            Err(e) => {
976                self.env.linkable_store.pop_package(package_id)?;
977                Err(e)
978            }
979        }
980    }
981
982    pub fn upgrade(
983        &mut self,
984        mut modules: Vec<CompiledModule>,
985        dep_ids: &[ObjectID],
986        current_package_id: ObjectID,
987        upgrade_ticket_policy: u8,
988        linkage: ResolvedLinkage,
989    ) -> Result<ObjectID, ExecutionError> {
990        // Check that this package ID points to a package and get the package we're upgrading.
991        let current_move_package = self.fetch_package(&current_package_id)?;
992
993        let runtime_id = current_move_package.original_package_id();
994        adapter::substitute_package_id(&mut modules, runtime_id)?;
995
996        // Upgraded packages share their predecessor's runtime ID but get a new storage ID.
997        // It should be fine that this does not go through the object runtime since it does not
998        // need to know about new packages created, since Move objects and Move packages
999        // cannot interact
1000        let storage_id = self.tx_context.borrow_mut().fresh_id();
1001
1002        let dependencies = self.fetch_packages(dep_ids)?;
1003        let package = current_move_package.new_upgraded(
1004            storage_id,
1005            &modules,
1006            self.env.protocol_config,
1007            dependencies.iter().map(|p| p.as_ref()),
1008        )?;
1009
1010        let linkage = RootedLinkage::new_for_publication(storage_id, runtime_id, linkage);
1011        self.publish_and_verify_modules(runtime_id, &modules, &linkage)?;
1012
1013        legacy_ptb::execution::check_compatibility(
1014            self.env.protocol_config,
1015            current_move_package.as_ref(),
1016            &modules,
1017            upgrade_ticket_policy,
1018        )?;
1019
1020        if self.env.protocol_config.check_for_init_during_upgrade() {
1021            // find newly added modules to the package,
1022            // and error if they have init functions
1023            let current_module_names: BTreeSet<&str> = current_move_package
1024                .serialized_module_map()
1025                .keys()
1026                .map(|s| s.as_str())
1027                .collect();
1028            let upgrade_module_names: BTreeSet<&str> = package
1029                .serialized_module_map()
1030                .keys()
1031                .map(|s| s.as_str())
1032                .collect();
1033            let new_module_names = upgrade_module_names
1034                .difference(&current_module_names)
1035                .copied()
1036                .collect::<BTreeSet<&str>>();
1037            let new_modules = modules
1038                .iter()
1039                .filter(|m| {
1040                    let name = m.identifier_at(m.self_handle().name).as_str();
1041                    new_module_names.contains(name)
1042                })
1043                .collect::<Vec<&CompiledModule>>();
1044            let new_module_has_init = new_modules.iter().any(|module| {
1045                module.function_defs.iter().any(|fdef| {
1046                    let fhandle = module.function_handle_at(fdef.function);
1047                    let fname = module.identifier_at(fhandle.name);
1048                    fname == INIT_FN_NAME
1049                })
1050            });
1051            if new_module_has_init {
1052                // TODO we cannot run 'init' on upgrade yet due to global type cache limitations
1053                return Err(ExecutionError::new_with_source(
1054                    ExecutionErrorKind::FeatureNotYetSupported,
1055                    "`init` in new modules on upgrade is not yet supported",
1056                ));
1057            }
1058        }
1059
1060        self.env
1061            .linkable_store
1062            .push_package(storage_id, Rc::new(package))?;
1063        Ok(storage_id)
1064    }
1065
1066    //
1067    // Commands
1068    //
1069
1070    pub fn transfer_object(
1071        &mut self,
1072        recipient: Owner,
1073        ty: Type,
1074        object: CtxValue,
1075    ) -> Result<(), ExecutionError> {
1076        self.transfer_object_(recipient, ty, object, /* end of transaction */ false)
1077    }
1078
1079    fn transfer_object_(
1080        &mut self,
1081        recipient: Owner,
1082        ty: Type,
1083        object: CtxValue,
1084        end_of_transaction: bool,
1085    ) -> Result<(), ExecutionError> {
1086        let tag = TypeTag::try_from(ty)
1087            .map_err(|_| make_invariant_violation!("Unable to convert Type to TypeTag"))?;
1088        let TypeTag::Struct(tag) = tag else {
1089            invariant_violation!("Expected struct type tag");
1090        };
1091        let ty = MoveObjectType::from(*tag);
1092        object_runtime_mut!(self)?
1093            .transfer(recipient, ty, object.0.into(), end_of_transaction)
1094            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
1095        Ok(())
1096    }
1097
1098    //
1099    // Dev Inspect tracking
1100    //
1101
1102    pub fn argument_updates(
1103        &mut self,
1104        args: Vec<T::Argument>,
1105    ) -> Result<Vec<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, ExecutionError> {
1106        args.into_iter()
1107            .filter_map(|arg| self.argument_update(arg).transpose())
1108            .collect()
1109    }
1110
1111    fn argument_update(
1112        &mut self,
1113        sp!(_, (arg, ty)): T::Argument,
1114    ) -> Result<Option<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, ExecutionError> {
1115        use sui_types::transaction::Argument as TxArgument;
1116        let ty = match ty {
1117            Type::Reference(true, inner) => (*inner).clone(),
1118            ty => {
1119                debug_assert!(
1120                    false,
1121                    "Unexpected non reference type in location update: {ty:?}"
1122                );
1123                return Ok(None);
1124            }
1125        };
1126        let Ok(tag): Result<TypeTag, _> = ty.clone().try_into() else {
1127            invariant_violation!("unable to generate type tag from type")
1128        };
1129        let location = arg.location();
1130        let resolved = self.locations.resolve(location)?;
1131        let local = match resolved {
1132            ResolvedLocation::Local(local)
1133            | ResolvedLocation::Pure { local, .. }
1134            | ResolvedLocation::Receiving { local, .. } => local,
1135        };
1136        if local.is_invalid()? {
1137            return Ok(None);
1138        }
1139        // copy the value from the local
1140        let value = local.copy()?;
1141        let value = match arg {
1142            T::Argument__::Use(_) => {
1143                // dereference the reference
1144                value.read_ref()?
1145            }
1146            T::Argument__::Borrow(_, _) => {
1147                // value is not a reference, nothing to do
1148                value
1149            }
1150            T::Argument__::Freeze(_) => {
1151                invariant_violation!("freeze should not be used for a mutable reference")
1152            }
1153            T::Argument__::Read(_) => {
1154                invariant_violation!("read should not return a reference")
1155            }
1156        };
1157        let layout = self.env.runtime_layout(&ty)?;
1158        let Some(bytes) = value.typed_serialize(&layout) else {
1159            invariant_violation!("Failed to serialize Move value");
1160        };
1161        let arg = match location {
1162            T::Location::TxContext => return Ok(None),
1163            T::Location::GasCoin => TxArgument::GasCoin,
1164            T::Location::Result(i, j) => TxArgument::NestedResult(i, j),
1165            T::Location::ObjectInput(i) => TxArgument::Input(
1166                self.locations
1167                    .input_object_metadata
1168                    .safe_get(i as usize)?
1169                    .0
1170                    .0,
1171            ),
1172            T::Location::WithdrawalInput(i) => TxArgument::Input(
1173                self.locations
1174                    .input_withdrawal_metadata
1175                    .safe_get(i as usize)?
1176                    .original_input_index
1177                    .0,
1178            ),
1179            T::Location::PureInput(i) => TxArgument::Input(
1180                self.locations
1181                    .pure_input_metadata
1182                    .safe_get(i as usize)?
1183                    .original_input_index
1184                    .0,
1185            ),
1186            T::Location::ReceivingInput(i) => TxArgument::Input(
1187                self.locations
1188                    .receiving_input_metadata
1189                    .safe_get(i as usize)?
1190                    .original_input_index
1191                    .0,
1192            ),
1193        };
1194        Ok(Some((arg, bytes, tag)))
1195    }
1196
1197    pub fn tracked_results(
1198        &self,
1199        results: &[CtxValue],
1200        result_tys: &T::ResultType,
1201    ) -> Result<Vec<(Vec<u8>, TypeTag)>, ExecutionError> {
1202        assert_invariant!(
1203            results.len() == result_tys.len(),
1204            "results and result types should match"
1205        );
1206        results
1207            .iter()
1208            .zip(result_tys)
1209            .map(|(v, ty)| self.tracked_result(&v.0, ty.clone()))
1210            .collect()
1211    }
1212
1213    fn tracked_result(
1214        &self,
1215        result: &Value,
1216        ty: Type,
1217    ) -> Result<(Vec<u8>, TypeTag), ExecutionError> {
1218        let inner_value;
1219        let (v, ty) = match ty {
1220            Type::Reference(_, inner) => {
1221                inner_value = result.copy()?.read_ref()?;
1222                (&inner_value, (*inner).clone())
1223            }
1224            _ => (result, ty),
1225        };
1226        let layout = self.env.runtime_layout(&ty)?;
1227        let Some(bytes) = v.typed_serialize(&layout) else {
1228            invariant_violation!("Failed to serialize Move value");
1229        };
1230        let Ok(tag): Result<TypeTag, _> = ty.try_into() else {
1231            invariant_violation!("unable to generate type tag from type")
1232        };
1233        Ok((bytes, tag))
1234    }
1235}
1236
1237impl VMValueCast<CtxValue> for VMValue {
1238    fn cast(self) -> Result<CtxValue, PartialVMError> {
1239        Ok(CtxValue(self.into()))
1240    }
1241}
1242
1243impl CtxValue {
1244    pub fn vec_pack(ty: Type, values: Vec<CtxValue>) -> Result<CtxValue, ExecutionError> {
1245        Ok(CtxValue(Value::vec_pack(
1246            ty,
1247            values.into_iter().map(|v| v.0).collect(),
1248        )?))
1249    }
1250
1251    pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
1252        self.0.coin_ref_value()
1253    }
1254
1255    pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
1256        self.0.coin_ref_subtract_balance(amount)
1257    }
1258
1259    pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
1260        self.0.coin_ref_add_balance(amount)
1261    }
1262
1263    pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
1264        self.0.into_upgrade_ticket()
1265    }
1266
1267    /// Used to get access the inner Value for tracing.
1268    pub(super) fn inner_for_tracing(&self) -> &Value {
1269        &self.0
1270    }
1271}
1272
1273fn load_object_arg(
1274    meter: &mut GasCharger,
1275    env: &Env,
1276    input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1277    input: T::ObjectInput,
1278) -> Result<(T::InputIndex, InputObjectMetadata, Value), ExecutionError> {
1279    let id = input.arg.id();
1280    let mutability = input.arg.mutability();
1281    let (metadata, value) =
1282        load_object_arg_impl(meter, env, input_object_map, id, mutability, input.ty)?;
1283    Ok((input.original_input_index, metadata, value))
1284}
1285
1286fn load_object_arg_impl(
1287    meter: &mut GasCharger,
1288    env: &Env,
1289    input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1290    id: ObjectID,
1291    mutability: ObjectMutability,
1292    ty: T::Type,
1293) -> Result<(InputObjectMetadata, Value), ExecutionError> {
1294    let obj = env.read_object(&id)?;
1295    let owner = obj.owner.clone();
1296    let version = obj.version();
1297    let object_metadata = InputObjectMetadata {
1298        id,
1299        mutability,
1300        owner: owner.clone(),
1301        version,
1302        type_: ty.clone(),
1303    };
1304    let sui_types::object::ObjectInner {
1305        data: sui_types::object::Data::Move(move_obj),
1306        ..
1307    } = obj.as_inner()
1308    else {
1309        invariant_violation!("Expected a Move object");
1310    };
1311    assert_expected_move_object_type(&object_metadata.type_, move_obj.type_())?;
1312    let contained_uids = {
1313        let fully_annotated_layout = env.fully_annotated_layout(&ty)?;
1314        get_all_uids(&fully_annotated_layout, move_obj.contents()).map_err(|e| {
1315            make_invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1316        })?
1317    };
1318    input_object_map.insert(
1319        id,
1320        object_runtime::InputObject {
1321            contained_uids,
1322            version,
1323            owner,
1324        },
1325    );
1326
1327    let v = Value::deserialize(env, move_obj.contents(), ty)?;
1328    charge_gas_!(meter, env, charge_copy_loc, &v)?;
1329    charge_gas_!(meter, env, charge_store_loc, &v)?;
1330    Ok((object_metadata, v))
1331}
1332
1333fn load_withdrawal_arg(
1334    meter: &mut GasCharger,
1335    env: &Env,
1336    withdrawal: &T::WithdrawalInput,
1337) -> Result<Value, ExecutionError> {
1338    let T::WithdrawalInput {
1339        original_input_index: _,
1340        ty: _,
1341        owner,
1342        amount,
1343    } = withdrawal;
1344    let loaded = Value::funds_accumulator_withdrawal(*owner, *amount);
1345    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1346    charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1347    Ok(loaded)
1348}
1349
1350fn load_pure_value(
1351    meter: &mut GasCharger,
1352    env: &Env,
1353    bytes: &[u8],
1354    metadata: &T::PureInput,
1355) -> Result<Value, ExecutionError> {
1356    let loaded = Value::deserialize(env, bytes, metadata.ty.clone())?;
1357    // ByteValue::Receiving { id, version } => Value::receiving(*id, *version),
1358    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1359    charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1360    Ok(loaded)
1361}
1362
1363fn load_receiving_value(
1364    meter: &mut GasCharger,
1365    env: &Env,
1366    metadata: &T::ReceivingInput,
1367) -> Result<Value, ExecutionError> {
1368    let (id, version, _) = metadata.object_ref;
1369    let loaded = Value::receiving(id, version);
1370    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1371    charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1372    Ok(loaded)
1373}
1374
1375fn copy_value(meter: &mut GasCharger, env: &Env, value: &Value) -> Result<Value, ExecutionError> {
1376    charge_gas_!(meter, env, charge_copy_loc, value)?;
1377    charge_gas_!(meter, env, charge_pop, value)?;
1378    value.copy()
1379}
1380
1381/// The max budget was deducted from the gas coin at the beginning of the transaction,
1382/// now we return exactly that amount. Gas will be charged by the execution engine
1383fn refund_max_gas_budget<OType>(
1384    writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1385    gas_charger: &mut GasCharger,
1386    gas_id: ObjectID,
1387) -> Result<(), ExecutionError> {
1388    let Some((_, _, value_ref)) = writes.get_mut(&gas_id) else {
1389        invariant_violation!("Gas object cannot be wrapped or destroyed")
1390    };
1391    // replace with dummy value
1392    let value = std::mem::replace(value_ref, VMValue::u8(0));
1393    let mut locals = Locals::new([Some(value.into())])?;
1394    let mut local = locals.local(0)?;
1395    let coin_value = local.borrow()?.coin_ref_value()?;
1396    let additional = gas_charger.gas_budget();
1397    if coin_value.checked_add(additional).is_none() {
1398        return Err(ExecutionError::new_with_source(
1399            ExecutionErrorKind::CoinBalanceOverflow,
1400            "Gas coin too large after returning the max gas budget",
1401        ));
1402    };
1403    local.borrow()?.coin_ref_add_balance(additional)?;
1404    // put the value back
1405    *value_ref = local.move_()?.into();
1406    Ok(())
1407}
1408
1409/// Generate an MoveObject given an updated/written object
1410/// # Safety
1411///
1412/// This function assumes proper generation of has_public_transfer, either from the abilities of
1413/// the StructTag, or from the runtime correctly propagating from the inputs
1414unsafe fn create_written_object<Mode: ExecutionMode>(
1415    env: &Env,
1416    objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1417    id: ObjectID,
1418    type_: Type,
1419    has_public_transfer: bool,
1420    contents: Vec<u8>,
1421) -> Result<MoveObject, ExecutionError> {
1422    debug_assert_eq!(
1423        id,
1424        MoveObject::id_opt(&contents).expect("object contents should start with an id")
1425    );
1426    let old_obj_ver = objects_modified_at
1427        .get(&id)
1428        .map(|obj: &LoadedRuntimeObject| obj.version);
1429
1430    let Ok(type_tag): Result<TypeTag, _> = type_.try_into() else {
1431        invariant_violation!("unable to generate type tag from type")
1432    };
1433
1434    let struct_tag = match type_tag {
1435        TypeTag::Struct(inner) => *inner,
1436        _ => invariant_violation!("Non struct type for object"),
1437    };
1438    unsafe {
1439        MoveObject::new_from_execution(
1440            struct_tag.into(),
1441            has_public_transfer,
1442            old_obj_ver.unwrap_or_default(),
1443            contents,
1444            env.protocol_config,
1445            Mode::packages_are_predefined(),
1446        )
1447    }
1448}
1449
1450/// Assert the type inferred matches the object's type. This has already been done during loading,
1451/// but is checked again as an invariant. This may be removed safely at a later time if needed.
1452fn assert_expected_move_object_type(
1453    actual: &Type,
1454    expected: &MoveObjectType,
1455) -> Result<(), ExecutionError> {
1456    let Type::Datatype(actual) = actual else {
1457        invariant_violation!("Expected a datatype for a Move object");
1458    };
1459    let (a, m, n) = actual.qualified_ident();
1460    assert_invariant!(
1461        a == &expected.address(),
1462        "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
1463    );
1464    assert_invariant!(
1465        m == expected.module(),
1466        "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
1467    );
1468    assert_invariant!(
1469        n == expected.name(),
1470        "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
1471    );
1472    let actual_type_arguments = &actual.type_arguments;
1473    let expected_type_arguments = expected.type_params();
1474    assert_invariant!(
1475        actual_type_arguments.len() == expected_type_arguments.len(),
1476        "Actual type arg length does not match expected. \
1477       actual: {actual:?} vs expected: {expected:?}",
1478    );
1479    for (actual_ty, expected_ty) in actual_type_arguments.iter().zip(&expected_type_arguments) {
1480        assert_expected_type(actual_ty, expected_ty)?;
1481    }
1482    Ok(())
1483}
1484
1485/// Assert the type inferred matches the expected type. This has already been done during typing,
1486/// but is checked again as an invariant. This may be removed safely at a later time if needed.
1487fn assert_expected_type(actual: &Type, expected: &TypeTag) -> Result<(), ExecutionError> {
1488    match (actual, expected) {
1489        (Type::Bool, TypeTag::Bool)
1490        | (Type::U8, TypeTag::U8)
1491        | (Type::U16, TypeTag::U16)
1492        | (Type::U32, TypeTag::U32)
1493        | (Type::U64, TypeTag::U64)
1494        | (Type::U128, TypeTag::U128)
1495        | (Type::U256, TypeTag::U256)
1496        | (Type::Address, TypeTag::Address)
1497        | (Type::Signer, TypeTag::Signer) => Ok(()),
1498        (Type::Vector(inner_actual), TypeTag::Vector(inner_expected)) => {
1499            assert_expected_type(&inner_actual.element_type, inner_expected)
1500        }
1501        (Type::Datatype(actual_dt), TypeTag::Struct(expected_st)) => {
1502            assert_expected_data_type(actual_dt, expected_st)
1503        }
1504        _ => invariant_violation!(
1505            "Type mismatch between actual: {actual:?} and expected: {expected:?}"
1506        ),
1507    }
1508}
1509/// Assert the type inferred matches the expected type. This has already been done during typing,
1510/// but is checked again as an invariant. This may be removed safely at a later time if needed.
1511fn assert_expected_data_type(
1512    actual: &Datatype,
1513    expected: &StructTag,
1514) -> Result<(), ExecutionError> {
1515    let (a, m, n) = actual.qualified_ident();
1516    assert_invariant!(
1517        a == &expected.address,
1518        "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
1519    );
1520    assert_invariant!(
1521        m == expected.module.as_ident_str(),
1522        "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
1523    );
1524    assert_invariant!(
1525        n == expected.name.as_ident_str(),
1526        "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
1527    );
1528    let actual_type_arguments = &actual.type_arguments;
1529    let expected_type_arguments = &expected.type_params;
1530    assert_invariant!(
1531        actual_type_arguments.len() == expected_type_arguments.len(),
1532        "Actual type arg length does not match expected. \
1533       actual: {actual:?} vs expected: {expected:?}",
1534    );
1535    for (actual_ty, expected_ty) in actual_type_arguments.iter().zip(expected_type_arguments) {
1536        assert_expected_type(actual_ty, expected_ty)?;
1537    }
1538    Ok(())
1539}