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