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    execution_mode::ExecutionMode,
7    execution_value::ExecutionState,
8    gas_charger::{GasCharger, GasPayment, PaymentLocation},
9    gas_meter::SuiGasMeter,
10    sp,
11    static_programmable_transactions::{
12        env::Env,
13        execution::{
14            self, trace_utils,
15            values::{Local, Locals, Value},
16        },
17        linkage::resolved_linkage::{ExecutableLinkage, ResolvedLinkage},
18        loading::ast::{Datatype, ObjectMutability},
19        typing::ast::{self as T, Type},
20    },
21};
22use indexmap::{IndexMap, IndexSet};
23use move_binary_format::{
24    CompiledModule,
25    compatibility::{Compatibility, InclusionCheck},
26    errors::{Location, PartialVMError, PartialVMResult, VMResult},
27    file_format::FunctionDefinitionIndex,
28    normalized,
29};
30use move_core_types::{
31    account_address::AccountAddress,
32    identifier::IdentStr,
33    language_storage::{ModuleId, StructTag},
34    u256::U256,
35};
36use move_trace_format::format::MoveTraceBuilder;
37use move_vm_runtime::{
38    execution::{
39        Type as VMType, TypeSubst as _,
40        values::{VMValueCast, Value as VMValue},
41        vm::{LoadedFunctionInformation, MoveVM},
42    },
43    natives::extensions::NativeExtensions,
44    shared::{
45        gas::{GasMeter as _, SimpleInstruction},
46        linkage_context::LinkageHash,
47    },
48    validation::verification::ast::Package as VerifiedPackage,
49};
50use mysten_common::ZipDebugEqIteratorExt;
51use mysten_common::debug_fatal;
52use nonempty::nonempty;
53use quick_cache::unsync::Cache as QCache;
54use serde::{Deserialize, de::DeserializeSeed};
55use std::{
56    cell::RefCell,
57    collections::{BTreeMap, BTreeSet},
58    fmt,
59    rc::Rc,
60    sync::Arc,
61};
62use sui_move_natives::object_runtime::{
63    self, LoadedRuntimeObject, MoveAccumulatorAction, MoveAccumulatorEvent, MoveAccumulatorValue,
64    ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
65};
66use sui_protocol_config::ProtocolConfig;
67use sui_types::{
68    TypeTag,
69    accumulator_event::AccumulatorEvent,
70    accumulator_root::{self, AccumulatorObjId},
71    balance::Balance,
72    base_types::{
73        MoveObjectType, ObjectID, RESOLVED_ASCII_STR, RESOLVED_UTF8_STR, SequenceNumber,
74        SuiAddress, TxContext,
75    },
76    effects::{AccumulatorAddress, AccumulatorValue, AccumulatorWriteV1},
77    error::{ExecutionError, ExecutionErrorTrait, SafeIndex, command_argument_error},
78    event::Event,
79    execution::{ExecutionResults, ExecutionResultsV2},
80    execution_status::{CommandArgumentError, ExecutionErrorKind, PackageUpgradeError},
81    metrics::ExecutionMetrics,
82    move_package::{
83        MovePackage, UpgradeCap, UpgradePolicy, UpgradeReceipt, UpgradeTicket,
84        normalize_deserialized_modules,
85    },
86    object::{MoveObject, Object, Owner},
87    storage::{BackingPackageStore, DenyListResult, PackageObject, get_package_objects},
88};
89use sui_verifier::INIT_FN_NAME;
90use tracing::instrument;
91
92macro_rules! unwrap {
93    ($e:expr, $($args:expr),* $(,)?) => {
94        match $e {
95            Some(v) => v,
96            None => {
97                invariant_violation!("Unexpected none: {}", format!($($args),*))
98            }
99        }
100
101    };
102}
103
104#[macro_export]
105macro_rules! object_runtime {
106    ($context:ident) => {
107        $context
108            .native_extensions
109            .try_borrow()
110            .map_err(|_| {
111                make_invariant_violation!(
112                    "Should be able to borrow object runtime native extension"
113                )
114            })?
115            .get::<sui_move_natives::object_runtime::ObjectRuntime>()
116            .map_err(|e| {
117                $context
118                    .env
119                    .convert_vm_error(e.finish(move_binary_format::errors::Location::Undefined))
120            })
121    };
122}
123
124macro_rules! object_runtime_mut {
125    ($context:ident) => {
126        $context
127            .native_extensions
128            .try_borrow_mut()
129            .map_err(|_| {
130                make_invariant_violation!(
131                    "Should be able to borrow object runtime native extension"
132                )
133            })?
134            .get_mut::<ObjectRuntime>()
135            .map_err(|e| $context.env.convert_vm_error(e.finish(Location::Undefined)))
136    };
137}
138
139macro_rules! charge_gas_ {
140    ($gas_charger:expr, $env:expr, $call:ident($($args:expr),*)) => {{
141        SuiGasMeter($gas_charger.move_gas_status_mut())
142            .$call($($args),*)
143            .map_err(|e| $env.convert_vm_error(e.finish(Location::Undefined)))
144    }};
145    ($gas_charger:expr, $env:expr, $case:ident, $value_view:expr) => {
146        charge_gas_!($gas_charger, $env, $case($value_view))
147    };
148}
149
150macro_rules! charge_gas {
151    ($context:ident, $case:ident, $value_view:expr) => {{ charge_gas_!($context.gas_charger, $context.env, $case, $value_view) }};
152}
153
154// Helper macro to manage Move VM cache for different linkage contexts. If the given linkage is
155// found the VM is reused, otherwise a new VM is created and inserted into the cache.
156macro_rules! with_vm {
157    ($self:ident, $linkage:expr, $error:ty, $body:expr) => {{
158        let link_context = $linkage.linkage_context::<$error>()?;
159        let linkage_hash = link_context.to_linkage_hash();
160        let mut vm = if let Some((_, vm)) = $self.executable_vm_cache.remove(&linkage_hash) {
161            vm
162        } else {
163            let data_store = &$self.env.linkable_store.package_store;
164            $self
165                .env
166                .vm
167                .make_vm_with_native_extensions(
168                    data_store,
169                    link_context.clone(),
170                    $self.native_extensions.clone(),
171                )
172                .map_err(|e| $self.env.convert_linked_vm_error(e, $linkage))?
173        };
174        let result = $body(&mut vm)?;
175        $self.executable_vm_cache.insert(linkage_hash, vm);
176        Ok(result)
177    }};
178}
179
180/// Type wrapper around Value to ensure safe usage
181#[derive(Debug)]
182pub struct CtxValue(Value);
183
184#[derive(Clone, Debug)]
185pub struct InputObjectMetadata {
186    pub newly_created: bool,
187    pub id: ObjectID,
188    pub mutability: ObjectMutability,
189    pub owner: Owner,
190    pub version: SequenceNumber,
191    pub type_: Type,
192}
193
194/// Metadata in the case that the GasCoin is transferred, either as an object to another recipient
195/// or as an address balance via `sui::coin::send_funds`. This is needed at the end to
196/// both refund the gas budget and set the correct location from which to charge gas.
197#[derive(Debug, Clone, Copy)]
198pub(crate) enum GasCoinTransfer {
199    /// Sent using the TransferObjects command
200    TransferObjects,
201    /// Sent with the `sui::coin::send_funds` command
202    SendFunds {
203        /// The recipient for `send_funds`.
204        recipient: AccountAddress,
205    },
206}
207
208#[derive(Copy, Clone)]
209enum UsageKind {
210    Move,
211    Copy,
212    Borrow,
213}
214
215// Locals and metadata for all `Location`s. Separated from `Context` for lifetime reasons.
216struct Locations {
217    // A single local for holding the TxContext
218    tx_context_value: Locals,
219    /// The runtime value for the Gas coin, None if no gas coin is provided
220    gas: Option<(GasPayment, InputObjectMetadata, Locals)>,
221    /// The runtime value for the input objects args
222    input_object_metadata: Vec<(T::InputIndex, InputObjectMetadata)>,
223    object_inputs: Locals,
224    input_withdrawal_metadata: Vec<T::WithdrawalInput>,
225    withdrawal_inputs: Locals,
226    pure_input_bytes: IndexSet<Vec<u8>>,
227    pure_input_metadata: Vec<T::PureInput>,
228    pure_inputs: Locals,
229    receiving_input_metadata: Vec<T::ReceivingInput>,
230    receiving_inputs: Locals,
231    /// The results of a given command. For most commands, the inner vector will have length 1.
232    /// It will only not be 1 for Move calls with multiple return values.
233    /// Inner values are None if taken/moved by-value
234    results: Vec<Locals>,
235}
236
237enum ResolvedLocation<'a> {
238    Local(Local<'a>),
239    Pure {
240        bytes: &'a [u8],
241        metadata: &'a T::PureInput,
242        local: Local<'a>,
243    },
244    Receiving {
245        metadata: &'a T::ReceivingInput,
246        local: Local<'a>,
247    },
248}
249
250/// Maintains all runtime state specific to programmable transactions
251pub struct Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas, 'extension, Mode>
252where
253    Mode: ExecutionMode,
254{
255    pub env: &'env Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
256    /// Metrics for reporting exceeded limits
257    pub metrics: Arc<ExecutionMetrics>,
258    pub native_extensions: NativeExtensions<'env>,
259    /// A shared transaction context, contains transaction digest information and manages the
260    /// creation of new object IDs
261    pub tx_context: Rc<RefCell<TxContext>>,
262    /// The gas charger used for metering
263    pub gas_charger: &'gas mut GasCharger,
264    /// User events are claimed after each Move call
265    user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
266    // runtime data
267    locations: Locations,
268    /// Tracks where the gas coin was sent, if it was moved by value
269    gas_coin_transfer: Option<GasCoinTransfer>,
270    // cache of Move VMs created this transaction for different linkage contexts so that we can reuse them.
271    executable_vm_cache: QCache<LinkageHash, MoveVM<'env>>,
272}
273
274impl Locations {
275    /// NOTE! This does not charge gas and should not be used directly. It is exposed for
276    /// dev-inspect
277    fn resolve(&mut self, location: T::Location) -> Result<ResolvedLocation<'_>, ExecutionError> {
278        Ok(match location {
279            T::Location::TxContext => ResolvedLocation::Local(self.tx_context_value.local(0)?),
280            T::Location::GasCoin => {
281                let (_, _, gas_locals) = unwrap!(self.gas.as_mut(), "Gas coin not provided");
282                ResolvedLocation::Local(gas_locals.local(0)?)
283            }
284            T::Location::ObjectInput(i) => ResolvedLocation::Local(self.object_inputs.local(i)?),
285            T::Location::WithdrawalInput(i) => {
286                ResolvedLocation::Local(self.withdrawal_inputs.local(i)?)
287            }
288            T::Location::Result(i, j) => {
289                let result = unwrap!(self.results.get_mut(i as usize), "bounds already verified");
290                ResolvedLocation::Local(result.local(j)?)
291            }
292            T::Location::PureInput(i) => {
293                let local = self.pure_inputs.local(i)?;
294                let metadata = &self.pure_input_metadata.safe_get(i as usize)?;
295                let bytes = self
296                    .pure_input_bytes
297                    .get_index(metadata.byte_index)
298                    .ok_or_else(|| {
299                        make_invariant_violation!(
300                            "Pure input {} bytes out of bounds at index {}",
301                            metadata.original_input_index.0,
302                            metadata.byte_index,
303                        )
304                    })?;
305                ResolvedLocation::Pure {
306                    bytes,
307                    metadata,
308                    local,
309                }
310            }
311            T::Location::ReceivingInput(i) => ResolvedLocation::Receiving {
312                metadata: self.receiving_input_metadata.safe_get(i as usize)?,
313                local: self.receiving_inputs.local(i)?,
314            },
315        })
316    }
317}
318
319impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas, 'extension, Mode>
320    Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas, 'extension, Mode>
321where
322    Mode: ExecutionMode,
323{
324    #[instrument(name = "Context::new", level = "trace", skip_all)]
325    pub fn new(
326        env: &'env Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
327        metrics: Arc<ExecutionMetrics>,
328        tx_context: Rc<RefCell<TxContext>>,
329        gas_charger: &'gas mut GasCharger,
330        payment_location: Option<GasPayment>,
331        pure_input_bytes: IndexSet<Vec<u8>>,
332        object_inputs: Vec<T::ObjectInput>,
333        input_withdrawal_metadata: Vec<T::WithdrawalInput>,
334        pure_input_metadata: Vec<T::PureInput>,
335        receiving_input_metadata: Vec<T::ReceivingInput>,
336    ) -> Result<Self, Mode::Error>
337    where
338        'pc: 'state,
339    {
340        let mut input_object_map = BTreeMap::new();
341        let mut input_object_metadata = Vec::with_capacity(object_inputs.len());
342        let mut object_values = Vec::with_capacity(object_inputs.len());
343        for object_input in object_inputs {
344            let (i, m, v) = load_object_arg(gas_charger, env, &mut input_object_map, object_input)?;
345            input_object_metadata.push((i, m));
346            object_values.push(Some(v));
347        }
348        let object_inputs = Locals::new(object_values)?;
349        let mut withdrawal_values = Vec::with_capacity(input_withdrawal_metadata.len());
350        for withdrawal_input in &input_withdrawal_metadata {
351            let v = load_withdrawal_arg(gas_charger, env, withdrawal_input)?;
352            withdrawal_values.push(Some(v));
353        }
354        let withdrawal_inputs = Locals::new(withdrawal_values)?;
355        let pure_inputs = Locals::new_invalid(pure_input_metadata.len())?;
356        let receiving_inputs = Locals::new_invalid(receiving_input_metadata.len())?;
357        let mut new_gas_coin_id = None;
358        let gas = match payment_location {
359            Some(gas_payment)
360                if matches!(gas_payment.location, PaymentLocation::AddressBalance(_))
361                    && !env.protocol_config.gasless_transaction_drop_safety() =>
362            {
363                None
364            }
365            Some(gas_payment) => {
366                let ty = env.gas_coin_type()?;
367                let (gas_metadata, gas_value) = match gas_payment.location {
368                    PaymentLocation::AddressBalance(sui_address) => {
369                        assert_invariant!(
370                            env.protocol_config.enable_address_balance_gas_payments(),
371                            "Address balance gas payments must be enabled to have an address \
372                             balance payment location"
373                        );
374                        let max_gas_in_balance = gas_charger.gas_budget();
375                        assert_invariant!(
376                            gas_payment.amount >= max_gas_in_balance,
377                            "not enough gas to pay. How did we get this far?"
378                        );
379                        let id = tx_context.borrow_mut().fresh_id();
380                        new_gas_coin_id = Some(id);
381
382                        let metadata = InputObjectMetadata {
383                            newly_created: true,
384                            id,
385                            mutability: ObjectMutability::Mutable,
386                            owner: Owner::AddressOwner(sui_address),
387                            version: SequenceNumber::new(),
388                            type_: ty,
389                        };
390                        let coin = Value::coin(id, gas_payment.amount);
391                        (metadata, coin)
392                    }
393                    PaymentLocation::Coin(gas_coin_id) => load_object_arg_impl(
394                        gas_charger,
395                        env,
396                        &mut input_object_map,
397                        gas_coin_id,
398                        ObjectMutability::Mutable,
399                        ty,
400                    )?,
401                };
402                let mut gas_locals = Locals::new([Some(gas_value)])?;
403                let mut gas_local = gas_locals.local(0)?;
404                let gas_ref = gas_local.borrow()?;
405                // We have already checked that the gas balance is enough to cover the gas budget
406                let max_gas_in_balance = gas_charger.gas_budget();
407                gas_ref.coin_ref_subtract_balance(max_gas_in_balance)?;
408                Some((gas_payment, gas_metadata, gas_locals))
409            }
410            None => None,
411        };
412        let native_extensions = adapter::new_native_extensions(
413            env.state_view.as_child_resolver(),
414            input_object_map,
415            !gas_charger.is_unmetered(),
416            env.protocol_config,
417            metrics.clone(),
418            tx_context.clone(),
419        )?;
420        if let Some(new_gas_coin_id) = new_gas_coin_id {
421            // If we created a new gas coin for the transaction,
422            // we need to add it to the object runtime
423            native_extensions
424                .try_borrow_mut()
425                .map_err(|_| {
426                    make_invariant_violation!(
427                        "Should be able to borrow object runtime native extension"
428                    )
429                })?
430                .get_mut::<ObjectRuntime>()
431                .and_then(|object_runtime| object_runtime.new_id(new_gas_coin_id))
432                .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
433        }
434
435        debug_assert_eq!(gas_charger.move_gas_status().stack_height_current(), 0);
436        let tx_context_value = Locals::new(vec![Some(Value::new_tx_context(
437            tx_context.borrow().digest(),
438        )?)])?;
439        Ok(Self {
440            env,
441            metrics,
442            native_extensions,
443            tx_context,
444            gas_charger,
445            user_events: vec![],
446            locations: Locations {
447                tx_context_value,
448                gas,
449                input_object_metadata,
450                object_inputs,
451                input_withdrawal_metadata,
452                withdrawal_inputs,
453                pure_input_bytes,
454                pure_input_metadata,
455                pure_inputs,
456                receiving_input_metadata,
457                receiving_inputs,
458                results: vec![],
459            },
460            gas_coin_transfer: None,
461            executable_vm_cache: QCache::new(1024),
462        })
463    }
464
465    pub(crate) fn record_gas_coin_transfer(
466        &mut self,
467        transfer: GasCoinTransfer,
468    ) -> Result<(), Mode::Error> {
469        // send funds transfer ==> accumulators/address balances are enabled
470        assert_invariant!(
471            !matches!(transfer, GasCoinTransfer::SendFunds { .. })
472                || self.env.protocol_config.enable_accumulators(),
473            "Gas coin transfers with send_funds are not allowed unless accumulators are enabled"
474        );
475        if self.gas_coin_transfer.is_some() {
476            invariant_violation!("Gas coin destination set more than once");
477        }
478        self.gas_coin_transfer = Some(transfer);
479        Ok(())
480    }
481
482    pub fn finish(mut self) -> Result<ExecutionResults, Mode::Error> {
483        assert_invariant!(
484            !self.locations.tx_context_value.local(0)?.is_invalid()?,
485            "tx context value should be present"
486        );
487        let gas_coin_transfer = self.gas_coin_transfer;
488        let gas = std::mem::take(&mut self.locations.gas);
489        let object_input_metadata = std::mem::take(&mut self.locations.input_object_metadata);
490        let mut object_inputs =
491            std::mem::replace(&mut self.locations.object_inputs, Locals::new_invalid(0)?);
492        let mut created_input_object_ids = BTreeSet::new();
493        let mut loaded_runtime_objects = BTreeMap::new();
494        let mut by_value_shared_objects = BTreeSet::new();
495        let mut consensus_owner_objects = BTreeMap::new();
496        let mut gas_payment_location = None;
497        let gas = gas
498            .map(|(payment_location, m, mut g)| {
499                gas_payment_location = Some(payment_location);
500                let value_opt = g.local(0)?.move_if_valid()?;
501                let moved = value_opt.is_none();
502                assert_invariant!(
503                    moved == gas_coin_transfer.is_some(),
504                    "Gas coin moved requires gas coin transfer to be recorded, and vice versa"
505                );
506                Result::<_, ExecutionError>::Ok((m, value_opt))
507            })
508            .transpose()?;
509
510        let gas_id_opt = gas.as_ref().map(|(m, _)| m.id);
511        let object_inputs = object_input_metadata
512            .into_iter()
513            .enumerate()
514            .map(|(i, (_, m))| {
515                let v_opt = object_inputs.local(checked_as!(i, u16)?)?.move_if_valid()?;
516                Ok((m, v_opt))
517            })
518            .collect::<Result<Vec<_>, ExecutionError>>()?;
519        for (metadata, value_opt) in object_inputs.into_iter().chain(gas) {
520            let InputObjectMetadata {
521                newly_created,
522                id,
523                mutability,
524                owner,
525                version,
526                type_,
527            } = metadata;
528            match mutability {
529                ObjectMutability::Immutable => continue,
530                // It is illegal to mutate NonExclusiveWrites, but they are passed as &mut T,
531                // so we need to treat them as mutable here. After execution, we check if they
532                // have been mutated, and abort the tx if they have.
533                ObjectMutability::NonExclusiveWrite | ObjectMutability::Mutable => (),
534            }
535
536            if newly_created {
537                created_input_object_ids.insert(id);
538            } else {
539                loaded_runtime_objects.insert(
540                    id,
541                    LoadedRuntimeObject {
542                        version,
543                        is_modified: true,
544                    },
545                );
546            }
547            if let Some(object) = value_opt {
548                self.transfer_object_(
549                    owner,
550                    type_,
551                    CtxValue(object),
552                    /* end of transaction */ true,
553                )?;
554            } else if owner.is_shared() {
555                by_value_shared_objects.insert(id);
556            } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
557                consensus_owner_objects.insert(id, owner.clone());
558            }
559        }
560
561        let Self {
562            env,
563            native_extensions,
564            tx_context,
565            gas_charger,
566            user_events,
567            ..
568        } = self;
569        let ref_context: &RefCell<TxContext> = &tx_context;
570        let tx_context: &TxContext = &ref_context.borrow();
571        let tx_digest = ref_context.borrow().digest();
572
573        let object_runtime: ObjectRuntime = native_extensions
574            .try_borrow_mut()
575            .map_err(|_| {
576                make_invariant_violation!(
577                "Should be able to borrow object runtime native extension at the end of execution"
578            )
579            })?
580            .remove()
581            .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
582
583        let RuntimeResults {
584            mut writes,
585            user_events: remaining_events,
586            loaded_child_objects,
587            mut created_object_ids,
588            deleted_object_ids,
589            mut accumulator_events,
590            settlement_input_sui,
591            settlement_output_sui,
592        } = object_runtime.finish()?;
593        assert_invariant!(
594            loaded_runtime_objects
595                .keys()
596                .all(|id| !created_object_ids.contains(id)),
597            "Loaded input objects should not be in the created objects set"
598        );
599
600        assert_invariant!(
601            remaining_events.is_empty(),
602            "Events should be taken after every Move call"
603        );
604        // Refund unused gas to the coin, real or ephemeral
605        if let Some(gas_id) = gas_id_opt {
606            // deleted implies was moved and used in send_funds
607            assert_invariant!(
608                !deleted_object_ids.contains(&gas_id)
609                    || gas_coin_transfer.is_some_and(|destination| matches!(
610                        destination,
611                        GasCoinTransfer::SendFunds { .. }
612                    )),
613                "Gas coin should not be deleted"
614            );
615            let Some(gas_payment_location) = gas_payment_location else {
616                invariant_violation!("Gas payment should be specified if gas ID is present");
617            };
618            finish_gas_coin(
619                gas_charger,
620                &mut writes,
621                &mut created_object_ids,
622                &deleted_object_ids,
623                &mut accumulator_events,
624                gas_id,
625                gas_payment_location,
626                gas_coin_transfer,
627            )?;
628        }
629
630        loaded_runtime_objects.extend(loaded_child_objects);
631
632        let mut written_objects = BTreeMap::new();
633
634        let (writeout_vm, ty_linkage) =
635            Self::make_writeout_vm(env, writes.values().map(|(_, ty, _)| ty.clone()))?;
636
637        for (id, (recipient, ty, value)) in writes {
638            let (ty, layout) = Self::load_type_and_layout_from_struct_for_writeout(
639                env,
640                &writeout_vm,
641                &ty_linkage,
642                ty.clone().into(),
643            )?;
644            let abilities = ty.abilities();
645            let has_public_transfer = abilities.has_store();
646            let Some(bytes) = value.typed_serialize(&layout) else {
647                invariant_violation!("Failed to serialize already deserialized Move value");
648            };
649            // safe because has_public_transfer has been determined by the abilities
650            let move_object = unsafe {
651                create_written_object::<Mode>(
652                    env,
653                    &loaded_runtime_objects,
654                    id,
655                    ty,
656                    has_public_transfer,
657                    bytes,
658                )?
659            };
660            let object = Object::new_move(move_object, recipient, tx_digest);
661            written_objects.insert(id, object);
662        }
663
664        for package in self
665            .env
666            .linkable_store
667            .package_store
668            .to_new_packages()
669            .into_iter()
670        {
671            let package_obj = Object::new_from_package(package, tx_digest);
672            let id = package_obj.id();
673            created_object_ids.insert(id);
674            written_objects.insert(id, package_obj);
675        }
676
677        Ok(execution::context::finish(
678            env.protocol_config,
679            env.state_view,
680            gas_charger,
681            tx_context,
682            &by_value_shared_objects,
683            &consensus_owner_objects,
684            loaded_runtime_objects,
685            written_objects,
686            created_object_ids,
687            deleted_object_ids,
688            user_events,
689            accumulator_events,
690            settlement_input_sui,
691            settlement_output_sui,
692        )?)
693    }
694
695    pub fn take_user_events(
696        &mut self,
697        vm: &MoveVM<'_>,
698        version_mid: ModuleId,
699        function_def_idx: FunctionDefinitionIndex,
700        instr_length: u16,
701        linkage: &ExecutableLinkage,
702    ) -> Result<(), Mode::Error> {
703        let events = object_runtime_mut!(self)?.take_user_events();
704        let Some(num_events) = self.user_events.len().checked_add(events.len()) else {
705            invariant_violation!("usize overflow, too many events emitted")
706        };
707        let max_events = self.env.protocol_config.max_num_event_emit();
708        if num_events as u64 > max_events {
709            let err = max_event_error(max_events)
710                .at_code_offset(function_def_idx, instr_length)
711                .finish(Location::Module(version_mid.clone()));
712            return Err(self.env.convert_linked_vm_error(err, linkage));
713        }
714        let new_events = events
715            .into_iter()
716            .map(|(tag, value)| {
717                let type_tag = TypeTag::Struct(Box::new(tag));
718                let layout = vm
719                    .runtime_type_layout(&type_tag)
720                    .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
721                let Some(bytes) = value.typed_serialize(&layout) else {
722                    invariant_violation!("Failed to serialize Move event");
723                };
724                let TypeTag::Struct(tag) = type_tag else {
725                    unreachable!()
726                };
727                Ok((version_mid.clone(), *tag, bytes))
728            })
729            .collect::<Result<Vec<_>, Mode::Error>>()?;
730        self.user_events.extend(new_events);
731        Ok(())
732    }
733
734    //
735    // Final serialization of written objects
736    //
737
738    /// The writeout VM is used to serialize all written objects at the end of execution. This
739    /// needs access to all types that were written during the transaction [`writes`]. Importantly,
740    /// it needs to be able to create a VM over any newly published packages as the `init`
741    /// functions in those packages may have created objects of types defined in those packages.
742    fn make_writeout_vm<I>(
743        env: &Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
744        writes: I,
745    ) -> Result<(MoveVM<'extension>, ExecutableLinkage), Mode::Error>
746    where
747        I: IntoIterator<Item = MoveObjectType>,
748    {
749        let tys_addrs = writes
750            .into_iter()
751            .flat_map(|ty| StructTag::from(ty).all_addresses())
752            .map(ObjectID::from)
753            .collect::<BTreeSet<_>>();
754
755        let ty_linkage = ExecutableLinkage::type_linkage::<_, Mode::Error>(
756            env.linkage_analysis.config().clone(),
757            &tys_addrs,
758            env.linkable_store,
759        )?;
760        env.vm
761            .make_vm(
762                &env.linkable_store.package_store,
763                ty_linkage.linkage_context::<Mode::Error>()?,
764            )
765            .map_err(|e| env.convert_linked_vm_error(e, &ty_linkage))
766            .map(|vm| (vm, ty_linkage))
767    }
768
769    /// Load the type and layout for a struct tag.
770    /// It is important that this use the VM passed in, and not the `resolution_vm` in the `env` as
771    /// the types requested may have only been created during the execution of the transaction and
772    /// therefore will not be present in the `resolution_vm`.
773    fn load_type_and_layout_from_struct_for_writeout(
774        env: &Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
775        vm: &MoveVM,
776        linkage: &ExecutableLinkage,
777        tag: StructTag,
778    ) -> Result<(Type, move_core_types::runtime_value::MoveTypeLayout), Mode::Error> {
779        let type_tag = TypeTag::Struct(Box::new(tag));
780        let vm_type = vm
781            .load_type(&type_tag)
782            .map_err(|e| env.convert_linked_vm_error(e, linkage))?;
783        let layout = vm
784            .runtime_type_layout(&type_tag)
785            .map_err(|e| env.convert_vm_error(e))?;
786        env.adapter_type_from_vm_type(vm, &vm_type)
787            .map(|ty| (ty, layout))
788    }
789
790    //
791    // Arguments and Values
792    //
793
794    fn location(&mut self, usage: UsageKind, location: T::Location) -> Result<Value, Mode::Error> {
795        let resolved = self.locations.resolve(location)?;
796        let mut local = match resolved {
797            ResolvedLocation::Local(l) => l,
798            ResolvedLocation::Pure {
799                bytes,
800                metadata,
801                mut local,
802            } => {
803                if local.is_invalid()? {
804                    let v = load_pure_value(self.gas_charger, self.env, bytes, metadata)?;
805                    local.store(v)?;
806                }
807                local
808            }
809            ResolvedLocation::Receiving {
810                metadata,
811                mut local,
812            } => {
813                if local.is_invalid()? {
814                    let v = load_receiving_value(self.gas_charger, self.env, metadata)?;
815                    local.store(v)?;
816                }
817                local
818            }
819        };
820        Ok(match usage {
821            UsageKind::Move => {
822                let value = local.move_()?;
823                charge_gas_!(self.gas_charger, self.env, charge_move_loc, &value)?;
824                value
825            }
826            UsageKind::Copy => {
827                let value = local.copy()?;
828                charge_gas_!(self.gas_charger, self.env, charge_copy_loc, &value)?;
829                value
830            }
831            UsageKind::Borrow => {
832                charge_gas_!(
833                    self.gas_charger,
834                    self.env,
835                    charge_simple_instr(SimpleInstruction::MutBorrowLoc)
836                )?;
837                local.borrow()?
838            }
839        })
840    }
841
842    fn location_usage(&mut self, usage: T::Usage) -> Result<Value, Mode::Error> {
843        match usage {
844            T::Usage::Move(location) => self.location(UsageKind::Move, location),
845            T::Usage::Copy { location, .. } => self.location(UsageKind::Copy, location),
846        }
847    }
848
849    fn argument_value(&mut self, sp!(_, (arg_, _)): T::Argument) -> Result<Value, Mode::Error> {
850        match arg_ {
851            T::Argument__::Use(usage) => self.location_usage(usage),
852            // freeze is a no-op for references since the value does not track mutability
853            T::Argument__::Freeze(usage) => self.location_usage(usage),
854            T::Argument__::Borrow(_, location) => self.location(UsageKind::Borrow, location),
855            T::Argument__::Read(usage) => {
856                let reference = self.location_usage(usage)?;
857                charge_gas!(self, charge_read_ref, &reference)?;
858                Ok(reference.read_ref()?)
859            }
860        }
861    }
862
863    pub fn argument<V>(&mut self, arg: T::Argument) -> Result<V, Mode::Error>
864    where
865        VMValue: VMValueCast<V>,
866    {
867        let before_height = self.gas_charger.move_gas_status().stack_height_current();
868        let value = self.argument_value(arg)?;
869        let after_height = self.gas_charger.move_gas_status().stack_height_current();
870        debug_assert_eq!(before_height.saturating_add(1), after_height);
871        let value: V = value.cast()?;
872        Ok(value)
873    }
874
875    pub fn arguments<V>(&mut self, args: Vec<T::Argument>) -> Result<Vec<V>, Mode::Error>
876    where
877        VMValue: VMValueCast<V>,
878    {
879        args.into_iter().map(|arg| self.argument(arg)).collect()
880    }
881
882    pub fn result(&mut self, result: Vec<Option<CtxValue>>) -> Result<(), Mode::Error> {
883        self.locations
884            .results
885            .push(Locals::new(result.into_iter().map(|v| v.map(|v| v.0)))?);
886        Ok(())
887    }
888
889    pub fn charge_command(
890        &mut self,
891        is_move_call: bool,
892        num_args: usize,
893        num_return: usize,
894    ) -> Result<(), Mode::Error> {
895        let move_gas_status = self.gas_charger.move_gas_status_mut();
896        let before_size = move_gas_status.stack_size_current();
897        // Pop all of the arguments
898        // If the return values came from the Move VM directly (via a Move call), pop those
899        // as well
900        let num_popped = if is_move_call {
901            num_args.checked_add(num_return).ok_or_else(|| {
902                make_invariant_violation!("usize overflow when charging gas for command",)
903            })?
904        } else {
905            num_args
906        };
907        move_gas_status
908            .charge(1, 0, num_popped as u64, 0, /* unused */ 1)
909            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
910        let after_size = move_gas_status.stack_size_current();
911        assert_invariant!(
912            before_size == after_size,
913            "We assume currently that the stack size is not decremented. \
914            If this changes, we need to actually account for it here"
915        );
916        Ok(())
917    }
918
919    pub fn copy_value(&mut self, value: &CtxValue) -> Result<CtxValue, Mode::Error> {
920        Ok(CtxValue(copy_value(self.gas_charger, self.env, &value.0)?))
921    }
922
923    pub fn new_coin(&mut self, amount: u64) -> Result<CtxValue, Mode::Error> {
924        let id = self.tx_context.borrow_mut().fresh_id();
925        object_runtime_mut!(self)?
926            .new_id(id)
927            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
928        Ok(CtxValue(Value::coin(id, amount)))
929    }
930
931    pub fn destroy_coin(&mut self, coin: CtxValue) -> Result<u64, Mode::Error> {
932        let (id, amount) = coin.0.unpack_coin()?;
933        object_runtime_mut!(self)?
934            .delete_id(id)
935            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
936        Ok(amount)
937    }
938
939    pub fn new_upgrade_cap(&mut self, version_id: ObjectID) -> Result<CtxValue, Mode::Error> {
940        let id = self.tx_context.borrow_mut().fresh_id();
941        object_runtime_mut!(self)?
942            .new_id(id)
943            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
944        let cap = UpgradeCap::new(id, version_id);
945        Ok(CtxValue(Value::upgrade_cap(cap)))
946    }
947
948    pub fn upgrade_receipt(
949        &self,
950        upgrade_ticket: UpgradeTicket,
951        upgraded_package_id: ObjectID,
952    ) -> CtxValue {
953        let receipt = UpgradeReceipt::new(upgrade_ticket, upgraded_package_id);
954        CtxValue(Value::upgrade_receipt(receipt))
955    }
956
957    //
958    // Move calls
959    //
960
961    pub fn vm_move_call(
962        &mut self,
963        function: T::LoadedFunction,
964        args: Vec<CtxValue>,
965        trace_builder_opt: &mut Option<MoveTraceBuilder>,
966    ) -> Result<Vec<CtxValue>, Mode::Error> {
967        with_vm!(self, &function.linkage, Mode::Error, |vm: &mut MoveVM<
968            'env,
969        >| {
970            let ty_args = function
971                .type_arguments
972                .iter()
973                .map(|ty| {
974                    let tag: TypeTag = ty.clone().try_into().map_err(|e| {
975                        Mode::Error::new_with_source(ExecutionErrorKind::VMInvariantViolation, e)
976                    })?;
977                    vm.load_type(&tag)
978                        .map_err(|e| self.env.convert_linked_vm_error(e, &function.linkage))
979                })
980                .collect::<Result<Vec<_>, Mode::Error>>()?;
981            let result = self.execute_function_bypass_visibility_with_vm(
982                vm,
983                &function.original_mid,
984                &function.name,
985                ty_args,
986                args,
987                &function.linkage,
988                trace_builder_opt,
989            )?;
990            self.take_user_events(
991                vm,
992                function.version_mid,
993                function.definition_index,
994                function.instruction_length,
995                &function.linkage,
996            )?;
997            Ok::<Vec<CtxValue>, Mode::Error>(result)
998        })
999    }
1000
1001    fn execute_function_bypass_visibility_with_vm(
1002        &mut self,
1003        vm: &mut MoveVM<'env>,
1004        original_mid: &ModuleId,
1005        function_name: &IdentStr,
1006        ty_args: Vec<VMType>,
1007        args: Vec<CtxValue>,
1008        linkage: &ExecutableLinkage,
1009        tracer: &mut Option<MoveTraceBuilder>,
1010    ) -> Result<Vec<CtxValue>, Mode::Error> {
1011        let gas_status = self.gas_charger.move_gas_status_mut();
1012        let values = vm
1013            .execute_function_bypass_visibility(
1014                original_mid,
1015                function_name,
1016                ty_args,
1017                args.into_iter().map(|v| v.0.into()).collect(),
1018                &mut SuiGasMeter(gas_status),
1019                tracer.as_mut(),
1020            )
1021            .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
1022        Ok(values.into_iter().map(|v| CtxValue(v.into())).collect())
1023    }
1024
1025    //
1026    // Publish and Upgrade
1027    //
1028
1029    // is_upgrade is used for gas charging. Assumed to be a new publish if false.
1030    pub fn deserialize_modules(
1031        &mut self,
1032        module_bytes: &[Vec<u8>],
1033        is_upgrade: bool,
1034    ) -> Result<Vec<CompiledModule>, Mode::Error> {
1035        assert_invariant!(
1036            !module_bytes.is_empty(),
1037            "empty package is checked in transaction input checker"
1038        );
1039        let total_bytes = module_bytes.iter().map(|v| v.len()).sum();
1040        if is_upgrade {
1041            self.gas_charger.charge_upgrade_package(total_bytes)?
1042        } else {
1043            self.gas_charger.charge_publish_package(total_bytes)?
1044        }
1045
1046        let binary_config = self.env.protocol_config.binary_config(None);
1047        let modules = module_bytes
1048            .iter()
1049            .map(|b| {
1050                CompiledModule::deserialize_with_config(b, &binary_config)
1051                    .map_err(|e| e.finish(Location::Undefined))
1052            })
1053            .collect::<VMResult<Vec<CompiledModule>>>()
1054            .map_err(|e| self.env.convert_vm_error(e))?;
1055        Ok(modules)
1056    }
1057
1058    fn fetch_package(&mut self, dependency_id: &ObjectID) -> Result<Rc<MovePackage>, Mode::Error> {
1059        let [fetched_package] = self.fetch_packages(&[*dependency_id])?.try_into().map_err(
1060            |_| {
1061                make_invariant_violation!(
1062                    "We should always fetch a single package for each object or return a dependency error."
1063                )
1064            },
1065        )?;
1066        Ok(fetched_package)
1067    }
1068
1069    fn fetch_packages(
1070        &mut self,
1071        dependency_ids: &[ObjectID],
1072    ) -> Result<Vec<Rc<MovePackage>>, Mode::Error> {
1073        let mut fetched = vec![];
1074        let mut missing = vec![];
1075
1076        // Collect into a set to avoid duplicate fetches and preserve existing behavior
1077        let dependency_ids: BTreeSet<_> = dependency_ids.iter().collect();
1078
1079        for id in &dependency_ids {
1080            match self.env.linkable_store.get_move_package(id) {
1081                Err(e) => {
1082                    return Err(Mode::Error::new_with_source(
1083                        ExecutionErrorKind::PublishUpgradeMissingDependency,
1084                        e,
1085                    ));
1086                }
1087                Ok(Some(inner)) => {
1088                    fetched.push(inner);
1089                }
1090                Ok(None) => {
1091                    missing.push(*id);
1092                }
1093            }
1094        }
1095
1096        if missing.is_empty() {
1097            assert_invariant!(
1098                fetched.len() == dependency_ids.len(),
1099                "all dependencies should be fetched"
1100            );
1101            Ok(fetched)
1102        } else {
1103            let msg = format!(
1104                "Missing dependencies: {}",
1105                missing
1106                    .into_iter()
1107                    .map(|dep| format!("{}", dep))
1108                    .collect::<Vec<_>>()
1109                    .join(", ")
1110            );
1111            Err(Mode::Error::new_with_source(
1112                ExecutionErrorKind::PublishUpgradeMissingDependency,
1113                msg,
1114            ))
1115        }
1116    }
1117
1118    fn publish_and_verify_modules(
1119        &mut self,
1120        package_id: ObjectID,
1121        pkg: &MovePackage,
1122        modules: &[CompiledModule],
1123        linkage: &ExecutableLinkage,
1124    ) -> Result<(VerifiedPackage, MoveVM<'env>), Mode::Error> {
1125        let serialized_pkg = pkg.into_serialized_move_package().map_err(|e| {
1126            make_invariant_violation!("Failed to serialize package for verification: {}", e)
1127        })?;
1128        let data_store = &self.env.linkable_store.package_store;
1129        let vm = self
1130            .env
1131            .vm
1132            .validate_package(
1133                data_store,
1134                *package_id,
1135                serialized_pkg,
1136                &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
1137                self.native_extensions.clone(),
1138            )
1139            .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
1140
1141        // run the Sui verifier
1142        for module in modules {
1143            // Run Sui bytecode verifier, which runs some additional checks that assume the Move
1144            // bytecode verifier has passed.
1145            sui_verifier::verifier::sui_verify_module_unmetered(
1146                module,
1147                &BTreeMap::new(),
1148                &self
1149                    .env
1150                    .protocol_config
1151                    .verifier_config(/* signing_limits */ None),
1152            )?;
1153        }
1154
1155        Ok(vm)
1156    }
1157
1158    fn init_modules(
1159        &mut self,
1160        mut vm: MoveVM<'env>,
1161        package_id: ObjectID,
1162        modules: &[CompiledModule],
1163        linkage: &ExecutableLinkage,
1164        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1165    ) -> Result<(), Mode::Error> {
1166        for module in modules {
1167            let Some((fdef_idx, fdef)) = module.find_function_def_by_name(INIT_FN_NAME.as_str())
1168            else {
1169                continue;
1170            };
1171            let fhandle = module.function_handle_at(fdef.function);
1172            let fparameters = module.signature_at(fhandle.parameters);
1173            assert_invariant!(
1174                fparameters.0.len() <= 2,
1175                "init function should have at most 2 parameters"
1176            );
1177            let has_otw = fparameters.0.len() == 2;
1178            let tx_context = self
1179                .location(UsageKind::Borrow, T::Location::TxContext)
1180                .map_err(|e| {
1181                    make_invariant_violation!("Failed to get tx context for init function: {}", e)
1182                })?;
1183            // balance the stack after borrowing the tx context
1184            charge_gas!(self, charge_store_loc, &tx_context)?;
1185
1186            let args = if has_otw {
1187                vec![CtxValue(Value::one_time_witness()?), CtxValue(tx_context)]
1188            } else {
1189                vec![CtxValue(tx_context)]
1190            };
1191            debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
1192            trace_utils::trace_move_call_start(trace_builder_opt);
1193            let return_values = self.execute_function_bypass_visibility_with_vm(
1194                &mut vm,
1195                &module.self_id(),
1196                INIT_FN_NAME,
1197                vec![],
1198                args,
1199                linkage,
1200                trace_builder_opt,
1201            )?;
1202            trace_utils::trace_move_call_end(trace_builder_opt);
1203
1204            let version_mid = ModuleId::new(package_id.into(), module.self_id().name().to_owned());
1205            self.take_user_events(
1206                &vm,
1207                version_mid,
1208                fdef_idx,
1209                fdef.code
1210                    .as_ref()
1211                    .map(|c| checked_as!(c.code.len(), u16))
1212                    .transpose()?
1213                    .unwrap_or(0),
1214                linkage,
1215            )?;
1216            assert_invariant!(
1217                return_values.is_empty(),
1218                "init should not have return values"
1219            );
1220            debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
1221        }
1222
1223        Ok(())
1224    }
1225
1226    pub fn publish_and_init_package(
1227        &mut self,
1228        mut modules: Vec<CompiledModule>,
1229        dep_ids: &[ObjectID],
1230        linkage: ResolvedLinkage,
1231        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1232    ) -> Result<ObjectID, Mode::Error> {
1233        let original_id = if Mode::packages_are_predefined() {
1234            // do not calculate or substitute id for predefined packages
1235            (*modules.safe_get(0)?.self_id().address()).into()
1236        } else {
1237            // It should be fine that this does not go through the object runtime since it does not
1238            // need to know about new packages created, since Move objects and Move packages
1239            // cannot interact
1240            let id = self.tx_context.borrow_mut().fresh_id();
1241            adapter::substitute_package_id(&mut modules, id)?;
1242            id
1243        };
1244
1245        let dependencies = self.fetch_packages(dep_ids)?;
1246        let package = Rc::new(MovePackage::new_initial(
1247            &modules,
1248            self.env.protocol_config,
1249            dependencies.iter().map(|p| p.as_ref()),
1250        )?);
1251        let package_id = package.id();
1252
1253        let linkage = ResolvedLinkage::update_for_publication(package_id, original_id, linkage);
1254
1255        let (pkg, vm) =
1256            self.publish_and_verify_modules(original_id, &package, &modules, &linkage)?;
1257        // Here we optimistically push the package that is being published/upgraded
1258        // and if there is an error of any kind (verification or module init) we
1259        // remove it.
1260        // The call to `pop_last_package` later is fine because we cannot re-enter and
1261        // the last package we pushed is the one we are verifying and running the init from
1262        self.env
1263            .linkable_store
1264            .package_store
1265            .push_package(package_id, package.clone(), pkg)?;
1266
1267        match self.init_modules(vm, package_id, &modules, &linkage, trace_builder_opt) {
1268            Ok(()) => Ok(original_id),
1269            Err(e) => {
1270                self.env
1271                    .linkable_store
1272                    .package_store
1273                    .pop_package(package_id)?;
1274                Err(e)
1275            }
1276        }
1277    }
1278
1279    pub fn upgrade(
1280        &mut self,
1281        mut modules: Vec<CompiledModule>,
1282        dep_ids: &[ObjectID],
1283        current_package_id: ObjectID,
1284        upgrade_ticket_policy: u8,
1285        linkage: ResolvedLinkage,
1286    ) -> Result<ObjectID, Mode::Error> {
1287        // Check that this package ID points to a package and get the package we're upgrading.
1288        let current_move_package = self.fetch_package(&current_package_id)?;
1289
1290        let original_id = current_move_package.original_package_id();
1291        adapter::substitute_package_id(&mut modules, original_id)?;
1292
1293        // Upgraded packages share their predecessor's runtime ID but get a new storage ID.
1294        // It should be fine that this does not go through the object runtime since it does not
1295        // need to know about new packages created, since Move objects and Move packages
1296        // cannot interact
1297        let version_id = self.tx_context.borrow_mut().fresh_id();
1298
1299        let dependencies = self.fetch_packages(dep_ids)?;
1300        let package = current_move_package.new_upgraded(
1301            version_id,
1302            &modules,
1303            self.env.protocol_config,
1304            dependencies.iter().map(|p| p.as_ref()),
1305        )?;
1306
1307        let linkage = ResolvedLinkage::update_for_publication(version_id, original_id, linkage);
1308        let (verified_pkg, _) =
1309            self.publish_and_verify_modules(original_id, &package, &modules, &linkage)?;
1310
1311        check_compatibility(
1312            self.env.protocol_config,
1313            current_move_package.as_ref(),
1314            &modules,
1315            upgrade_ticket_policy,
1316        )?;
1317
1318        // find newly added modules to the package,
1319        // and error if they have init functions
1320        let current_module_names: BTreeSet<&str> = current_move_package
1321            .serialized_module_map()
1322            .keys()
1323            .map(|s| s.as_str())
1324            .collect();
1325        let upgrade_module_names: BTreeSet<&str> = package
1326            .serialized_module_map()
1327            .keys()
1328            .map(|s| s.as_str())
1329            .collect();
1330        let new_module_names = upgrade_module_names
1331            .difference(&current_module_names)
1332            .copied()
1333            .collect::<BTreeSet<&str>>();
1334        let new_modules = modules
1335            .iter()
1336            .filter(|m| {
1337                let name = m.identifier_at(m.self_handle().name).as_str();
1338                new_module_names.contains(name)
1339            })
1340            .collect::<Vec<&CompiledModule>>();
1341        let new_module_has_init = new_modules.iter().any(|module| {
1342            module.function_defs.iter().any(|fdef| {
1343                let fhandle = module.function_handle_at(fdef.function);
1344                let fname = module.identifier_at(fhandle.name);
1345                fname == INIT_FN_NAME
1346            })
1347        });
1348        if new_module_has_init {
1349            // TODO we cannot run 'init' on upgrade yet due to global type cache limitations
1350            return Err(Mode::Error::new_with_source(
1351                ExecutionErrorKind::FeatureNotYetSupported,
1352                "`init` in new modules on upgrade is not yet supported",
1353            ));
1354        }
1355
1356        self.env.linkable_store.package_store.push_package(
1357            version_id,
1358            Rc::new(package),
1359            verified_pkg,
1360        )?;
1361        Ok(version_id)
1362    }
1363
1364    //
1365    // Commands
1366    //
1367
1368    pub fn transfer_object(
1369        &mut self,
1370        recipient: Owner,
1371        ty: Type,
1372        object: CtxValue,
1373    ) -> Result<(), Mode::Error> {
1374        self.transfer_object_(recipient, ty, object, /* end of transaction */ false)
1375    }
1376
1377    fn transfer_object_(
1378        &mut self,
1379        recipient: Owner,
1380        ty: Type,
1381        object: CtxValue,
1382        end_of_transaction: bool,
1383    ) -> Result<(), Mode::Error> {
1384        let tag = TypeTag::try_from(ty)
1385            .map_err(|_| make_invariant_violation!("Unable to convert Type to TypeTag"))?;
1386        let TypeTag::Struct(tag) = tag else {
1387            invariant_violation!("Expected struct type tag");
1388        };
1389        let ty = MoveObjectType::from(*tag);
1390        object_runtime_mut!(self)?
1391            .transfer(recipient, ty, object.0.into(), end_of_transaction)
1392            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
1393        Ok(())
1394    }
1395
1396    //
1397    // Dev Inspect tracking
1398    //
1399
1400    #[allow(clippy::type_complexity)]
1401    pub fn argument_updates(
1402        &mut self,
1403        args: Vec<T::Argument>,
1404    ) -> Result<Vec<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, Mode::Error> {
1405        args.into_iter()
1406            .filter_map(|arg| self.argument_update(arg).transpose())
1407            .collect()
1408    }
1409
1410    #[allow(clippy::type_complexity)]
1411    fn argument_update(
1412        &mut self,
1413        sp!(_, (arg, ty)): T::Argument,
1414    ) -> Result<Option<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, Mode::Error> {
1415        use sui_types::transaction::Argument as TxArgument;
1416        let ty = match ty {
1417            Type::Reference(true, inner) => (*inner).clone(),
1418            ty => {
1419                debug_assert!(
1420                    false,
1421                    "Unexpected non reference type in location update: {ty:?}"
1422                );
1423                return Ok(None);
1424            }
1425        };
1426        let Ok(tag): Result<TypeTag, _> = ty.clone().try_into() else {
1427            invariant_violation!("unable to generate type tag from type")
1428        };
1429        let location = arg.location();
1430        let resolved = self.locations.resolve(location)?;
1431        let local = match resolved {
1432            ResolvedLocation::Local(local)
1433            | ResolvedLocation::Pure { local, .. }
1434            | ResolvedLocation::Receiving { local, .. } => local,
1435        };
1436        if local.is_invalid()? {
1437            return Ok(None);
1438        }
1439        // copy the value from the local
1440        let value = local.copy()?;
1441        let value = match arg {
1442            T::Argument__::Use(_) => {
1443                // dereference the reference
1444                value.read_ref()?
1445            }
1446            T::Argument__::Borrow(_, _) => {
1447                // value is not a reference, nothing to do
1448                value
1449            }
1450            T::Argument__::Freeze(_) => {
1451                invariant_violation!("freeze should not be used for a mutable reference")
1452            }
1453            T::Argument__::Read(_) => {
1454                invariant_violation!("read should not return a reference")
1455            }
1456        };
1457        let layout = self.env.runtime_layout(&ty)?;
1458        let Some(bytes) = value.typed_serialize(&layout) else {
1459            invariant_violation!("Failed to serialize Move value");
1460        };
1461        let arg = match location {
1462            T::Location::TxContext => return Ok(None),
1463            T::Location::GasCoin => TxArgument::GasCoin,
1464            T::Location::Result(i, j) => TxArgument::NestedResult(i, j),
1465            T::Location::ObjectInput(i) => TxArgument::Input(
1466                self.locations
1467                    .input_object_metadata
1468                    .safe_get(i as usize)?
1469                    .0
1470                    .0,
1471            ),
1472            T::Location::WithdrawalInput(i) => TxArgument::Input(
1473                self.locations
1474                    .input_withdrawal_metadata
1475                    .safe_get(i as usize)?
1476                    .original_input_index
1477                    .0,
1478            ),
1479            T::Location::PureInput(i) => TxArgument::Input(
1480                self.locations
1481                    .pure_input_metadata
1482                    .safe_get(i as usize)?
1483                    .original_input_index
1484                    .0,
1485            ),
1486            T::Location::ReceivingInput(i) => TxArgument::Input(
1487                self.locations
1488                    .receiving_input_metadata
1489                    .safe_get(i as usize)?
1490                    .original_input_index
1491                    .0,
1492            ),
1493        };
1494        Ok(Some((arg, bytes, tag)))
1495    }
1496
1497    pub fn tracked_results(
1498        &self,
1499        results: &[CtxValue],
1500        result_tys: &T::ResultType,
1501    ) -> Result<Vec<(Vec<u8>, TypeTag)>, Mode::Error> {
1502        assert_invariant!(
1503            results.len() == result_tys.len(),
1504            "results and result types should match"
1505        );
1506        results
1507            .iter()
1508            .zip_debug_eq(result_tys)
1509            .map(|(v, ty)| self.tracked_result(&v.0, ty.clone()))
1510            .collect()
1511    }
1512
1513    fn tracked_result(&self, result: &Value, ty: Type) -> Result<(Vec<u8>, TypeTag), Mode::Error> {
1514        let inner_value;
1515        let (v, ty) = match ty {
1516            Type::Reference(_, inner) => {
1517                inner_value = result.copy()?.read_ref()?;
1518                (&inner_value, (*inner).clone())
1519            }
1520            _ => (result, ty),
1521        };
1522        let layout = self.env.runtime_layout(&ty)?;
1523        let Some(bytes) = v.typed_serialize(&layout) else {
1524            invariant_violation!("Failed to serialize Move value");
1525        };
1526        let Ok(tag): Result<TypeTag, _> = ty.try_into() else {
1527            invariant_violation!("unable to generate type tag from type")
1528        };
1529        Ok((bytes, tag))
1530    }
1531}
1532
1533impl VMValueCast<CtxValue> for VMValue {
1534    fn cast(self) -> Result<CtxValue, PartialVMError> {
1535        Ok(CtxValue(self.into()))
1536    }
1537}
1538
1539impl CtxValue {
1540    pub fn vec_pack(ty: Type, values: Vec<CtxValue>) -> Result<CtxValue, ExecutionError> {
1541        Ok(CtxValue(Value::vec_pack(
1542            ty,
1543            values.into_iter().map(|v| v.0).collect(),
1544        )?))
1545    }
1546
1547    pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
1548        self.0.coin_ref_value()
1549    }
1550
1551    pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
1552        self.0.coin_ref_subtract_balance(amount)
1553    }
1554
1555    pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
1556        self.0.coin_ref_add_balance(amount)
1557    }
1558
1559    pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
1560        self.0.into_upgrade_ticket()
1561    }
1562
1563    pub fn to_address(&self) -> Result<AccountAddress, ExecutionError> {
1564        self.0.copy()?.cast()
1565    }
1566
1567    /// Used to get access the inner Value for tracing.
1568    pub(super) fn inner_for_tracing(&self) -> &Value {
1569        &self.0
1570    }
1571}
1572
1573fn load_object_arg<Mode: ExecutionMode>(
1574    meter: &mut GasCharger,
1575    env: &Env<Mode>,
1576    input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1577    input: T::ObjectInput,
1578) -> Result<(T::InputIndex, InputObjectMetadata, Value), Mode::Error> {
1579    let id = input.arg.id();
1580    let mutability = input.arg.mutability();
1581    let (metadata, value) =
1582        load_object_arg_impl(meter, env, input_object_map, id, mutability, input.ty)?;
1583    Ok((input.original_input_index, metadata, value))
1584}
1585
1586fn load_object_arg_impl<Mode: ExecutionMode>(
1587    meter: &mut GasCharger,
1588    env: &Env<Mode>,
1589    input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1590    id: ObjectID,
1591    mutability: ObjectMutability,
1592    ty: T::Type,
1593) -> Result<(InputObjectMetadata, Value), Mode::Error> {
1594    let obj = env.read_object(&id)?;
1595    let owner = obj.owner.clone();
1596    let version = obj.version();
1597    let object_metadata = InputObjectMetadata {
1598        newly_created: false,
1599        id,
1600        mutability,
1601        owner: owner.clone(),
1602        version,
1603        type_: ty.clone(),
1604    };
1605    let sui_types::object::ObjectInner {
1606        data: sui_types::object::Data::Move(move_obj),
1607        ..
1608    } = obj.as_inner()
1609    else {
1610        invariant_violation!("Expected a Move object");
1611    };
1612    assert_expected_move_object_type(&object_metadata.type_, move_obj.type_())?;
1613    let contained_uids = {
1614        let fully_annotated_layout = env.fully_annotated_layout(&ty)?;
1615        get_all_uids(&fully_annotated_layout, move_obj.contents()).map_err(|e| {
1616            make_invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1617        })?
1618    };
1619    input_object_map.insert(
1620        id,
1621        object_runtime::InputObject {
1622            contained_uids,
1623            version,
1624            owner,
1625        },
1626    );
1627
1628    let v = Value::deserialize(env, move_obj.contents(), ty)?;
1629    charge_gas_!(meter, env, charge_copy_loc, &v)?;
1630    charge_gas_!(meter, env, charge_store_loc, &v)?;
1631    Ok((object_metadata, v))
1632}
1633
1634fn load_withdrawal_arg<Mode: ExecutionMode>(
1635    meter: &mut GasCharger,
1636    env: &Env<Mode>,
1637    withdrawal: &T::WithdrawalInput,
1638) -> Result<Value, Mode::Error> {
1639    let T::WithdrawalInput {
1640        original_input_index: _,
1641        ty: _,
1642        owner,
1643        amount,
1644    } = withdrawal;
1645    let loaded = Value::funds_accumulator_withdrawal(*owner, *amount);
1646    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1647    charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1648    Ok(loaded)
1649}
1650
1651fn load_pure_value<Mode: ExecutionMode>(
1652    meter: &mut GasCharger,
1653    env: &Env<Mode>,
1654    bytes: &[u8],
1655    metadata: &T::PureInput,
1656) -> Result<Value, Mode::Error> {
1657    let loaded = Value::deserialize(env, bytes, metadata.ty.clone())?;
1658    // ByteValue::Receiving { id, version } => Value::receiving(*id, *version),
1659    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1660    charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1661    Ok(loaded)
1662}
1663
1664fn load_receiving_value<Mode: ExecutionMode>(
1665    meter: &mut GasCharger,
1666    env: &Env<Mode>,
1667    metadata: &T::ReceivingInput,
1668) -> Result<Value, Mode::Error> {
1669    let (id, version, _) = metadata.object_ref;
1670    let loaded = Value::receiving(id, version);
1671    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1672    charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1673    Ok(loaded)
1674}
1675
1676fn copy_value<Mode: ExecutionMode>(
1677    meter: &mut GasCharger,
1678    env: &Env<Mode>,
1679    value: &Value,
1680) -> Result<Value, Mode::Error> {
1681    charge_gas_!(meter, env, charge_copy_loc, value)?;
1682    charge_gas_!(meter, env, charge_pop, value)?;
1683    Ok(value.copy()?)
1684}
1685
1686/// The max budget was deducted from the gas coin at the beginning of the transaction,
1687/// now we return exactly that amount. Gas will be charged by the execution engine.
1688/// If the gas coin was transferred in any way, set the charge location.
1689fn refund_max_gas_budget<OType>(
1690    writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1691    accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1692    gas_charger: &mut GasCharger,
1693    gas_id: ObjectID,
1694    gas_coin_transfer: Option<&GasCoinTransfer>,
1695) -> Result<(), ExecutionError> {
1696    match gas_coin_transfer {
1697        Some(GasCoinTransfer::SendFunds { recipient, .. }) => {
1698            // if the gas coin was transferred to an address balance, send the budget to that
1699            // address balance
1700            assert_invariant!(
1701                !writes.contains_key(&gas_id),
1702                "Gas coin should not be in writes if it was used with send_funds"
1703            );
1704            balance_change_accumulator_event(
1705                accumulator_events,
1706                *recipient,
1707                checked_as!(gas_charger.gas_budget(), i64)?,
1708            )?;
1709        }
1710        Some(GasCoinTransfer::TransferObjects) | None => {
1711            let Some((_, _, value_ref)) = writes.get_mut(&gas_id) else {
1712                invariant_violation!("Gas object cannot be wrapped or destroyed")
1713            };
1714            // replace with dummy value
1715            let value = std::mem::replace(value_ref, VMValue::u8(0));
1716            let mut locals = Locals::new([Some(value.into())])?;
1717            let mut local = locals.local(0)?;
1718            let coin_value = local.borrow()?.coin_ref_value()?;
1719            let additional = gas_charger.gas_budget();
1720            if coin_value.checked_add(additional).is_none() {
1721                return Err(ExecutionError::new_with_source(
1722                    ExecutionErrorKind::CoinBalanceOverflow,
1723                    "Gas coin too large after returning the max gas budget",
1724                ));
1725            };
1726            local.borrow()?.coin_ref_add_balance(additional)?;
1727            // put the value back
1728            *value_ref = local.move_()?.into();
1729        }
1730    };
1731    Ok(())
1732}
1733
1734/// Refunds the gas budget, overrides the charge location if transferred, and for ephemeral
1735/// coins settles the net balance change back to the source address balance.
1736/// Writes are always updated for refunding the gas budget.
1737/// The ephemeral coin is removed from writes and created objects in the case that it was not
1738/// transferred (left in its memory location at the end of the transaction).
1739fn finish_gas_coin<OType>(
1740    gas_charger: &mut GasCharger,
1741    writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1742    created_object_ids: &mut IndexSet<ObjectID>,
1743    deleted_object_ids: &IndexSet<ObjectID>,
1744    accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1745    gas_id: ObjectID,
1746    gas_payment: GasPayment,
1747    gas_coin_transfer: Option<GasCoinTransfer>,
1748) -> Result<(), ExecutionError> {
1749    // return the max gas budget to the current gas location
1750    refund_max_gas_budget(
1751        writes,
1752        accumulator_events,
1753        gas_charger,
1754        gas_id,
1755        gas_coin_transfer.as_ref(),
1756    )?;
1757
1758    // Set the charge location if it was transferred
1759    // This might not actually "override" (that is the actual charge location might be the same
1760    // as it was at the beginning of the transaction), in the case where the coin was real and
1761    // was transferred, or in the case where the coin was ephemeral and transferred to the
1762    // an address balance recipient that matches the original address balance charge location
1763    match &gas_coin_transfer {
1764        Some(GasCoinTransfer::SendFunds { recipient, .. }) => {
1765            gas_charger.override_gas_charge_location(PaymentLocation::AddressBalance(
1766                (*recipient).into(),
1767            ))?;
1768        }
1769        Some(GasCoinTransfer::TransferObjects) => {
1770            gas_charger.override_gas_charge_location(PaymentLocation::Coin(gas_id))?;
1771        }
1772        None => (),
1773    }
1774
1775    // If the gas coin was not ephemeral, then we are done.
1776    let address = match gas_payment.location {
1777        PaymentLocation::Coin(_) => {
1778            // small sanity check
1779            assert_invariant!(
1780                !matches!(gas_coin_transfer, Some(GasCoinTransfer::SendFunds { .. }))
1781                    || deleted_object_ids.contains(&gas_id),
1782                "send_funds transfer implies the coin should be deleted"
1783            );
1784            return Ok(());
1785        }
1786        PaymentLocation::AddressBalance(address) => address,
1787    };
1788
1789    let net_balance_change = if let Some(gas_coin_transfer) = gas_coin_transfer {
1790        // sanity check storage changes
1791        match gas_coin_transfer {
1792            GasCoinTransfer::TransferObjects => {
1793                assert_invariant!(
1794                    created_object_ids.contains(&gas_id),
1795                    "ephemeral coin should be newly created"
1796                );
1797                assert_invariant!(
1798                    !deleted_object_ids.contains(&gas_id),
1799                    "ephemeral coin should not be deleted if transferred as an object"
1800                );
1801                assert_invariant!(
1802                    writes.contains_key(&gas_id),
1803                    "ephemeral coin should be in writes if transferred as an object"
1804                );
1805            }
1806            GasCoinTransfer::SendFunds { .. } => {
1807                assert_invariant!(
1808                    !created_object_ids.contains(&gas_id),
1809                    "ephemeral coin should not be newly created if transferred with send_funds"
1810                );
1811                assert_invariant!(
1812                    !deleted_object_ids.contains(&gas_id),
1813                    "ephemeral coin should not be deleted if transferred with send_funds"
1814                );
1815                assert_invariant!(
1816                    !writes.contains_key(&gas_id),
1817                    "ephemeral coin should not be in writes if transferred with send_funds"
1818                );
1819            }
1820        }
1821
1822        // If the gas coin was moved, it was transferred.
1823        // In such a case, the gas coin has a new location, so we fully withdraw the gas amount
1824        // and keep it in the coin object. The transferred location is now the source of payment
1825        // instead of the address balance.
1826        let Some(net_balance_change) = gas_payment
1827            .amount
1828            .try_into()
1829            .ok()
1830            .and_then(|i: i64| i.checked_neg())
1831        else {
1832            invariant_violation!("Gas payment amount cannot be represented as i64")
1833        };
1834        net_balance_change
1835    } else {
1836        // In this case the gas coin was not moved, so we want to return the remaining balance to
1837        // the address balance. To do so we need to destroy it and create an accumulator event for
1838        // the net balance change
1839        let was_created = created_object_ids.shift_remove(&gas_id);
1840        assert_invariant!(was_created, "ephemeral coin should be newly created");
1841        let Some((_owner, _ty, value)) = writes.shift_remove(&gas_id) else {
1842            invariant_violation!("checked above that the gas coin was present")
1843        };
1844        let (_id, remaining_balance) = Value::from(value).unpack_coin()?;
1845        // gas_payment.amount is the original value of the ephemeral coin.
1846        // If net_balance_change is negative, then balance was spent/withdrawn from the gas coin.
1847        // If the net_balance_change is positive, then balance was added/merged to the gas coin.
1848        let Some(net_balance_change): Option<i64> = (remaining_balance as i128)
1849            .checked_sub(gas_payment.amount as i128)
1850            .and_then(|i| i.try_into().ok())
1851        else {
1852            invariant_violation!("Remaining balance could not be represented as i64")
1853        };
1854        net_balance_change
1855    };
1856    balance_change_accumulator_event(accumulator_events, address.into(), net_balance_change)?;
1857    Ok(())
1858}
1859
1860fn balance_change_accumulator_event(
1861    accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1862    address: AccountAddress,
1863    balance_change: i64,
1864) -> Result<(), ExecutionError> {
1865    if balance_change == 0 {
1866        return Ok(());
1867    }
1868    let balance_type = Balance::type_tag(sui_types::gas_coin::GAS::type_tag());
1869    let Some(accumulator_id) =
1870        accumulator_root::AccumulatorValue::get_field_id(address.into(), &balance_type).ok()
1871    else {
1872        invariant_violation!("Failed to compute accumulator field id")
1873    };
1874    let (action, value) = if balance_change < 0 {
1875        (
1876            MoveAccumulatorAction::Split,
1877            MoveAccumulatorValue::U64(balance_change.unsigned_abs()),
1878        )
1879    } else {
1880        (
1881            MoveAccumulatorAction::Merge,
1882            MoveAccumulatorValue::U64(balance_change as u64),
1883        )
1884    };
1885    accumulator_events.push(MoveAccumulatorEvent {
1886        accumulator_id: *accumulator_id.inner(),
1887        action,
1888        target_addr: address,
1889        target_ty: balance_type,
1890        value,
1891    });
1892    Ok(())
1893}
1894
1895/// Generate an MoveObject given an updated/written object
1896/// # Safety
1897///
1898/// This function assumes proper generation of has_public_transfer, either from the abilities of
1899/// the StructTag, or from the runtime correctly propagating from the inputs
1900unsafe fn create_written_object<Mode: ExecutionMode>(
1901    env: &Env<Mode>,
1902    objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1903    id: ObjectID,
1904    type_: Type,
1905    has_public_transfer: bool,
1906    contents: Vec<u8>,
1907) -> Result<MoveObject, ExecutionError> {
1908    debug_assert_eq!(
1909        id,
1910        MoveObject::id_opt(&contents).expect("object contents should start with an id")
1911    );
1912    let old_obj_ver = objects_modified_at
1913        .get(&id)
1914        .map(|obj: &LoadedRuntimeObject| obj.version);
1915
1916    let Ok(type_tag): Result<TypeTag, _> = type_.try_into() else {
1917        invariant_violation!("unable to generate type tag from type")
1918    };
1919
1920    let struct_tag = match type_tag {
1921        TypeTag::Struct(inner) => *inner,
1922        _ => invariant_violation!("Non struct type for object"),
1923    };
1924    unsafe {
1925        MoveObject::new_from_execution(
1926            struct_tag.into(),
1927            has_public_transfer,
1928            old_obj_ver.unwrap_or_default(),
1929            contents,
1930            env.protocol_config,
1931            Mode::packages_are_predefined(),
1932        )
1933    }
1934}
1935
1936/// substitutes the type arguments into the parameter and return types
1937pub fn subst_signature(
1938    signature: LoadedFunctionInformation,
1939    type_arguments: &[VMType],
1940) -> VMResult<LoadedFunctionInformation> {
1941    let LoadedFunctionInformation {
1942        parameters,
1943        return_,
1944        is_entry,
1945        is_native,
1946        visibility,
1947        index,
1948        instruction_count,
1949    } = signature;
1950    let parameters = parameters
1951        .into_iter()
1952        .map(|ty| ty.subst(type_arguments))
1953        .collect::<PartialVMResult<Vec<_>>>()
1954        .map_err(|err| err.finish(Location::Undefined))?;
1955    let return_ = return_
1956        .into_iter()
1957        .map(|ty| ty.subst(type_arguments))
1958        .collect::<PartialVMResult<Vec<_>>>()
1959        .map_err(|err| err.finish(Location::Undefined))?;
1960    Ok(LoadedFunctionInformation {
1961        parameters,
1962        return_,
1963        is_entry,
1964        is_native,
1965        visibility,
1966        index,
1967        instruction_count,
1968    })
1969}
1970
1971pub enum EitherError<E: ExecutionErrorTrait = ExecutionError> {
1972    CommandArgument(CommandArgumentError),
1973    Execution(E),
1974}
1975
1976impl<E: ExecutionErrorTrait> From<ExecutionError> for EitherError<E> {
1977    fn from(e: ExecutionError) -> Self {
1978        EitherError::Execution(e.into())
1979    }
1980}
1981
1982impl<E: ExecutionErrorTrait> From<CommandArgumentError> for EitherError<E> {
1983    fn from(e: CommandArgumentError) -> Self {
1984        EitherError::CommandArgument(e)
1985    }
1986}
1987
1988impl<E: ExecutionErrorTrait> EitherError<E> {
1989    pub fn into_execution_error(self, command_index: usize) -> E {
1990        match self {
1991            EitherError::CommandArgument(e) => command_argument_error(e, command_index).into(),
1992            EitherError::Execution(e) => e,
1993        }
1994    }
1995}
1996
1997/***************************************************************************************************
1998 * Special serialization formats
1999 **************************************************************************************************/
2000
2001/// Special enum for values that need additional validation, in other words
2002/// There is validation to do on top of the BCS layout. Currently only needed for
2003/// strings
2004#[derive(Debug)]
2005pub enum PrimitiveArgumentLayout {
2006    /// An option
2007    Option(Box<PrimitiveArgumentLayout>),
2008    /// A vector
2009    Vector(Box<PrimitiveArgumentLayout>),
2010    /// An ASCII encoded string
2011    Ascii,
2012    /// A UTF8 encoded string
2013    UTF8,
2014    // needed for Option validation
2015    Bool,
2016    U8,
2017    U16,
2018    U32,
2019    U64,
2020    U128,
2021    U256,
2022    Address,
2023}
2024
2025impl PrimitiveArgumentLayout {
2026    /// returns true iff all BCS compatible bytes are actually values for this type.
2027    /// For example, this function returns false for Option and Strings since they need additional
2028    /// validation.
2029    pub fn bcs_only(&self) -> bool {
2030        match self {
2031            // have additional restrictions past BCS
2032            PrimitiveArgumentLayout::Option(_)
2033            | PrimitiveArgumentLayout::Ascii
2034            | PrimitiveArgumentLayout::UTF8 => false,
2035            // Move primitives are BCS compatible and do not need additional validation
2036            PrimitiveArgumentLayout::Bool
2037            | PrimitiveArgumentLayout::U8
2038            | PrimitiveArgumentLayout::U16
2039            | PrimitiveArgumentLayout::U32
2040            | PrimitiveArgumentLayout::U64
2041            | PrimitiveArgumentLayout::U128
2042            | PrimitiveArgumentLayout::U256
2043            | PrimitiveArgumentLayout::Address => true,
2044            // vector only needs validation if it's inner type does
2045            PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
2046        }
2047    }
2048}
2049
2050/// Checks the bytes against the `SpecialArgumentLayout` using `bcs`. It does not actually generate
2051/// the deserialized value, only walks the bytes. While not necessary if the layout does not contain
2052/// special arguments (e.g. Option or String) we check the BCS bytes for predictability
2053pub fn bcs_argument_validate(
2054    bytes: &[u8],
2055    idx: u16,
2056    layout: PrimitiveArgumentLayout,
2057) -> Result<(), ExecutionError> {
2058    bcs::from_bytes_seed(&layout, bytes).map_err(|_| {
2059        ExecutionError::new_with_source(
2060            ExecutionErrorKind::command_argument_error(CommandArgumentError::InvalidBCSBytes, idx),
2061            format!("Function expects {layout} but provided argument's value does not match",),
2062        )
2063    })
2064}
2065
2066impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout {
2067    type Value = ();
2068    fn deserialize<D: serde::de::Deserializer<'d>>(
2069        self,
2070        deserializer: D,
2071    ) -> Result<Self::Value, D::Error> {
2072        use serde::de::Error;
2073        match self {
2074            PrimitiveArgumentLayout::Ascii => {
2075                let s: &str = serde::Deserialize::deserialize(deserializer)?;
2076                if !s.is_ascii() {
2077                    Err(D::Error::custom("not an ascii string"))
2078                } else {
2079                    Ok(())
2080                }
2081            }
2082            PrimitiveArgumentLayout::UTF8 => {
2083                deserializer.deserialize_string(serde::de::IgnoredAny)?;
2084                Ok(())
2085            }
2086            PrimitiveArgumentLayout::Option(layout) => {
2087                deserializer.deserialize_option(OptionElementVisitor(layout))
2088            }
2089            PrimitiveArgumentLayout::Vector(layout) => {
2090                deserializer.deserialize_seq(VectorElementVisitor(layout))
2091            }
2092            // primitive move value cases, which are hit to make sure the correct number of bytes
2093            // are removed for elements of an option/vector
2094            PrimitiveArgumentLayout::Bool => {
2095                deserializer.deserialize_bool(serde::de::IgnoredAny)?;
2096                Ok(())
2097            }
2098            PrimitiveArgumentLayout::U8 => {
2099                deserializer.deserialize_u8(serde::de::IgnoredAny)?;
2100                Ok(())
2101            }
2102            PrimitiveArgumentLayout::U16 => {
2103                deserializer.deserialize_u16(serde::de::IgnoredAny)?;
2104                Ok(())
2105            }
2106            PrimitiveArgumentLayout::U32 => {
2107                deserializer.deserialize_u32(serde::de::IgnoredAny)?;
2108                Ok(())
2109            }
2110            PrimitiveArgumentLayout::U64 => {
2111                deserializer.deserialize_u64(serde::de::IgnoredAny)?;
2112                Ok(())
2113            }
2114            PrimitiveArgumentLayout::U128 => {
2115                deserializer.deserialize_u128(serde::de::IgnoredAny)?;
2116                Ok(())
2117            }
2118            PrimitiveArgumentLayout::U256 => {
2119                U256::deserialize(deserializer)?;
2120                Ok(())
2121            }
2122            PrimitiveArgumentLayout::Address => {
2123                SuiAddress::deserialize(deserializer)?;
2124                Ok(())
2125            }
2126        }
2127    }
2128}
2129
2130struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2131
2132impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> {
2133    type Value = ();
2134
2135    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2136        formatter.write_str("Vector")
2137    }
2138
2139    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
2140    where
2141        A: serde::de::SeqAccess<'d>,
2142    {
2143        while seq.next_element_seed(self.0)?.is_some() {}
2144        Ok(())
2145    }
2146}
2147
2148struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2149
2150impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> {
2151    type Value = ();
2152
2153    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2154        formatter.write_str("Option")
2155    }
2156
2157    fn visit_none<E>(self) -> Result<Self::Value, E>
2158    where
2159        E: serde::de::Error,
2160    {
2161        Ok(())
2162    }
2163
2164    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
2165    where
2166        D: serde::Deserializer<'d>,
2167    {
2168        self.0.deserialize(deserializer)
2169    }
2170}
2171
2172impl fmt::Display for PrimitiveArgumentLayout {
2173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2174        match self {
2175            PrimitiveArgumentLayout::Vector(inner) => {
2176                write!(f, "vector<{inner}>")
2177            }
2178            PrimitiveArgumentLayout::Option(inner) => {
2179                write!(f, "std::option::Option<{inner}>")
2180            }
2181            PrimitiveArgumentLayout::Ascii => {
2182                write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2)
2183            }
2184            PrimitiveArgumentLayout::UTF8 => {
2185                write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2)
2186            }
2187            PrimitiveArgumentLayout::Bool => write!(f, "bool"),
2188            PrimitiveArgumentLayout::U8 => write!(f, "u8"),
2189            PrimitiveArgumentLayout::U16 => write!(f, "u16"),
2190            PrimitiveArgumentLayout::U32 => write!(f, "u32"),
2191            PrimitiveArgumentLayout::U64 => write!(f, "u64"),
2192            PrimitiveArgumentLayout::U128 => write!(f, "u128"),
2193            PrimitiveArgumentLayout::U256 => write!(f, "u256"),
2194            PrimitiveArgumentLayout::Address => write!(f, "address"),
2195        }
2196    }
2197}
2198
2199pub fn finish(
2200    protocol_config: &ProtocolConfig,
2201    state_view: &dyn ExecutionState,
2202    gas_charger: &mut GasCharger,
2203    tx_context: &TxContext,
2204    by_value_shared_objects: &BTreeSet<ObjectID>,
2205    consensus_owner_objects: &BTreeMap<ObjectID, Owner>,
2206    loaded_runtime_objects: BTreeMap<ObjectID, LoadedRuntimeObject>,
2207    written_objects: BTreeMap<ObjectID, Object>,
2208    created_object_ids: IndexSet<ObjectID>,
2209    deleted_object_ids: IndexSet<ObjectID>,
2210    user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
2211    accumulator_events: Vec<MoveAccumulatorEvent>,
2212    settlement_input_sui: u64,
2213    settlement_output_sui: u64,
2214) -> Result<ExecutionResults, ExecutionError> {
2215    // Before finishing, ensure that any shared object taken by value by the transaction is either:
2216    // 1. Mutated (and still has a shared ownership); or
2217    // 2. Deleted.
2218    // Otherwise, the shared object operation is not allowed and we fail the transaction.
2219    for id in by_value_shared_objects {
2220        // If it's been written it must have been reshared so must still have an ownership
2221        // of `Shared`.
2222        if let Some(obj) = written_objects.get(id) {
2223            if !obj.is_shared() {
2224                return Err(ExecutionError::new(
2225                    ExecutionErrorKind::SharedObjectOperationNotAllowed,
2226                    Some(
2227                        format!(
2228                            "Shared object operation on {} not allowed: \
2229                                 cannot be frozen, transferred, or wrapped",
2230                            id
2231                        )
2232                        .into(),
2233                    ),
2234                ));
2235            }
2236        } else {
2237            // If it's not in the written objects, the object must have been deleted. Otherwise
2238            // it's an error.
2239            if !deleted_object_ids.contains(id) {
2240                return Err(ExecutionError::new(
2241                    ExecutionErrorKind::SharedObjectOperationNotAllowed,
2242                    Some(
2243                        format!(
2244                            "Shared object operation on {} not allowed: \
2245                             shared objects used by value must be re-shared if not deleted",
2246                            id
2247                        )
2248                        .into(),
2249                    ),
2250                ));
2251            }
2252        }
2253    }
2254
2255    // Before finishing, enforce auth restrictions on consensus objects.
2256    for (id, original_owner) in consensus_owner_objects {
2257        let Owner::ConsensusAddressOwner { owner, .. } = original_owner else {
2258            panic!(
2259                "verified before adding to `consensus_owner_objects` that these are ConsensusAddressOwner"
2260            );
2261        };
2262        // Already verified in pre-execution checks that tx sender is the object owner.
2263        // Owner is allowed to do anything with the object.
2264        if tx_context.sender() != *owner {
2265            debug_fatal!(
2266                "transaction with a singly owned input object where the tx sender is not the owner should never be executed"
2267            );
2268            return Err(ExecutionError::new(
2269                ExecutionErrorKind::SharedObjectOperationNotAllowed,
2270                Some(
2271                    format!(
2272                        "Shared object operation on {} not allowed: \
2273                         transaction with singly owned input object must be sent by the owner",
2274                        id
2275                    )
2276                    .into(),
2277                ),
2278            ));
2279        }
2280        // If an Owner type is implemented with support for more fine-grained authorization,
2281        // checks should be performed here. For example, transfers and wraps can be detected
2282        // by comparing `original_owner` with:
2283        // let new_owner = written_objects.get(&id).map(|obj| obj.owner);
2284        //
2285        // Deletions can be detected with:
2286        // let deleted = deleted_object_ids.contains(&id);
2287    }
2288
2289    let user_events: Vec<Event> = user_events
2290        .into_iter()
2291        .map(|(module_id, tag, contents)| {
2292            Event::new(
2293                module_id.address(),
2294                module_id.name(),
2295                tx_context.sender(),
2296                tag,
2297                contents,
2298            )
2299        })
2300        .collect();
2301
2302    let mut receiving_funds_type_and_owners = BTreeMap::new();
2303    let accumulator_events = accumulator_events
2304        .into_iter()
2305        .map(|accum_event| {
2306            if let Some(ty) = Balance::maybe_get_balance_type_param(&accum_event.target_ty) {
2307                receiving_funds_type_and_owners
2308                    .entry(ty)
2309                    .or_insert_with(BTreeSet::new)
2310                    .insert(accum_event.target_addr.into());
2311            }
2312            let value = match accum_event.value {
2313                MoveAccumulatorValue::U64(amount) => AccumulatorValue::Integer(amount),
2314                MoveAccumulatorValue::EventRef(event_idx) => {
2315                    let Some(event) = user_events.get(checked_as!(event_idx, usize)?) else {
2316                        invariant_violation!(
2317                            "Could not find authenticated event at index {}",
2318                            event_idx
2319                        );
2320                    };
2321                    let digest = event.digest();
2322                    AccumulatorValue::EventDigest(nonempty![(event_idx, digest)])
2323                }
2324            };
2325
2326            let address =
2327                AccumulatorAddress::new(accum_event.target_addr.into(), accum_event.target_ty);
2328
2329            let write = AccumulatorWriteV1 {
2330                address,
2331                operation: accum_event.action.into_sui_accumulator_action(),
2332                value,
2333            };
2334
2335            Ok(AccumulatorEvent::new(
2336                AccumulatorObjId::new_unchecked(accum_event.accumulator_id),
2337                write,
2338            ))
2339        })
2340        .collect::<Result<Vec<_>, ExecutionError>>()?;
2341
2342    // Deny-list v2 checks
2343    for object in written_objects.values() {
2344        let coin_type = object.type_().and_then(|ty| ty.coin_type_maybe());
2345        let owner = object.owner.get_owner_address();
2346        if let (Some(ty), Ok(owner)) = (coin_type, owner) {
2347            receiving_funds_type_and_owners
2348                .entry(ty)
2349                .or_insert_with(BTreeSet::new)
2350                .insert(owner);
2351        }
2352    }
2353    let DenyListResult {
2354        result,
2355        num_non_gas_coin_owners,
2356    } = state_view.check_coin_deny_list(receiving_funds_type_and_owners);
2357    gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
2358    result?;
2359
2360    let created_object_ids: BTreeSet<ObjectID> = created_object_ids.into_iter().collect();
2361    let deleted_object_ids: BTreeSet<ObjectID> = deleted_object_ids.into_iter().collect();
2362    let modified_objects: BTreeSet<ObjectID> = loaded_runtime_objects
2363        .into_iter()
2364        .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
2365        .collect();
2366
2367    assert_invariant!(
2368        created_object_ids.is_disjoint(&deleted_object_ids),
2369        "Created and deleted object sets should be disjoint"
2370    );
2371    assert_invariant!(
2372        modified_objects.is_disjoint(&created_object_ids),
2373        "Modified and created object sets should be disjoint"
2374    );
2375    assert_invariant!(
2376        written_objects
2377            .keys()
2378            .all(|id| !deleted_object_ids.contains(id)),
2379        "Written objects should not be deleted"
2380    );
2381    Ok(ExecutionResults::V2(ExecutionResultsV2 {
2382        written_objects,
2383        modified_objects,
2384        created_object_ids,
2385        deleted_object_ids,
2386        user_events,
2387        accumulator_events,
2388        settlement_input_sui,
2389        settlement_output_sui,
2390    }))
2391}
2392
2393pub fn fetch_package(
2394    state_view: &impl BackingPackageStore,
2395    package_id: &ObjectID,
2396) -> Result<PackageObject, ExecutionError> {
2397    let mut fetched_packages = fetch_packages(state_view, vec![package_id])?;
2398    assert_invariant!(
2399        fetched_packages.len() == 1,
2400        "Number of fetched packages must match the number of package object IDs if successful."
2401    );
2402    match fetched_packages.pop() {
2403        Some(pkg) => Ok(pkg),
2404        None => invariant_violation!(
2405            "We should always fetch a package for each object or return a dependency error."
2406        ),
2407    }
2408}
2409
2410pub fn fetch_packages<'ctx, 'state>(
2411    state_view: &'state impl BackingPackageStore,
2412    package_ids: impl IntoIterator<Item = &'ctx ObjectID>,
2413) -> Result<Vec<PackageObject>, ExecutionError> {
2414    let package_ids: BTreeSet<_> = package_ids.into_iter().collect();
2415    match get_package_objects(state_view, package_ids) {
2416        Err(e) => Err(ExecutionError::new_with_source(
2417            ExecutionErrorKind::PublishUpgradeMissingDependency,
2418            e,
2419        )),
2420        Ok(Err(missing_deps)) => {
2421            let msg = format!(
2422                "Missing dependencies: {}",
2423                missing_deps
2424                    .into_iter()
2425                    .map(|dep| format!("{}", dep))
2426                    .collect::<Vec<_>>()
2427                    .join(", ")
2428            );
2429            Err(ExecutionError::new_with_source(
2430                ExecutionErrorKind::PublishUpgradeMissingDependency,
2431                msg,
2432            ))
2433        }
2434        Ok(Ok(pkgs)) => Ok(pkgs),
2435    }
2436}
2437
2438pub fn check_compatibility(
2439    protocol_config: &ProtocolConfig,
2440    existing_package: &MovePackage,
2441    upgrading_modules: &[CompiledModule],
2442    policy: u8,
2443) -> Result<(), ExecutionError> {
2444    // Make sure this is a known upgrade policy.
2445    let Ok(policy) = UpgradePolicy::try_from(policy) else {
2446        return Err(ExecutionError::from_kind(
2447            ExecutionErrorKind::PackageUpgradeError {
2448                upgrade_error: PackageUpgradeError::UnknownUpgradePolicy { policy },
2449            },
2450        ));
2451    };
2452
2453    let pool = &mut normalized::RcPool::new();
2454    let binary_config = protocol_config.binary_config(None);
2455    let Ok(current_normalized) =
2456        existing_package.normalize(pool, &binary_config, /* include code */ true)
2457    else {
2458        invariant_violation!("Tried to normalize modules in existing package but failed")
2459    };
2460
2461    let existing_modules_len = current_normalized.len();
2462    let upgrading_modules_len = upgrading_modules.len();
2463    let disallow_new_modules = policy as u8 == UpgradePolicy::DEP_ONLY;
2464
2465    if disallow_new_modules && existing_modules_len != upgrading_modules_len {
2466        return Err(ExecutionError::new_with_source(
2467            ExecutionErrorKind::PackageUpgradeError {
2468                upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2469            },
2470            format!(
2471                "Existing package has {existing_modules_len} modules, but new package has \
2472                     {upgrading_modules_len}. Adding or removing a module to a deps only package is not allowed."
2473            ),
2474        ));
2475    }
2476
2477    let mut new_normalized = normalize_deserialized_modules(
2478        pool,
2479        upgrading_modules.iter(),
2480        /* include code */ true,
2481    );
2482    for (name, cur_module) in current_normalized {
2483        let Some(new_module) = new_normalized.remove(&name) else {
2484            return Err(ExecutionError::new_with_source(
2485                ExecutionErrorKind::PackageUpgradeError {
2486                    upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2487                },
2488                format!("Existing module {name} not found in next version of package"),
2489            ));
2490        };
2491
2492        check_module_compatibility(&policy, &cur_module, &new_module)?;
2493    }
2494
2495    // If we disallow new modules double check that there are no modules left in `new_normalized`.
2496    debug_assert!(!disallow_new_modules || new_normalized.is_empty());
2497
2498    Ok(())
2499}
2500
2501fn check_module_compatibility(
2502    policy: &UpgradePolicy,
2503    cur_module: &move_binary_format::compatibility::Module,
2504    new_module: &move_binary_format::compatibility::Module,
2505) -> Result<(), ExecutionError> {
2506    match policy {
2507        UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module),
2508        UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module),
2509        UpgradePolicy::Compatible => {
2510            let compatibility = Compatibility::upgrade_check();
2511
2512            compatibility.check(cur_module, new_module)
2513        }
2514    }
2515    .map_err(|e| {
2516        ExecutionError::new_with_source(
2517            ExecutionErrorKind::PackageUpgradeError {
2518                upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2519            },
2520            e,
2521        )
2522    })
2523}
2524
2525/// Assert the type inferred matches the object's type. This has already been done during loading,
2526/// but is checked again as an invariant. This may be removed safely at a later time if needed.
2527fn assert_expected_move_object_type(
2528    actual: &Type,
2529    expected: &MoveObjectType,
2530) -> Result<(), ExecutionError> {
2531    let Type::Datatype(actual) = actual else {
2532        invariant_violation!("Expected a datatype for a Move object");
2533    };
2534    let (a, m, n) = actual.qualified_ident();
2535    assert_invariant!(
2536        a == &expected.address(),
2537        "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
2538    );
2539    assert_invariant!(
2540        m == expected.module(),
2541        "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
2542    );
2543    assert_invariant!(
2544        n == expected.name(),
2545        "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
2546    );
2547    let actual_type_arguments = &actual.type_arguments;
2548    let expected_type_arguments = expected.type_params();
2549    assert_invariant!(
2550        actual_type_arguments.len() == expected_type_arguments.len(),
2551        "Actual type arg length does not match expected. \
2552       actual: {actual:?} vs expected: {expected:?}",
2553    );
2554    for (actual_ty, expected_ty) in actual_type_arguments
2555        .iter()
2556        .zip_debug_eq(&expected_type_arguments)
2557    {
2558        assert_expected_type(actual_ty, expected_ty)?;
2559    }
2560    Ok(())
2561}
2562
2563/// Assert the type inferred matches the expected type. This has already been done during typing,
2564/// but is checked again as an invariant. This may be removed safely at a later time if needed.
2565fn assert_expected_type(actual: &Type, expected: &TypeTag) -> Result<(), ExecutionError> {
2566    match (actual, expected) {
2567        (Type::Bool, TypeTag::Bool)
2568        | (Type::U8, TypeTag::U8)
2569        | (Type::U16, TypeTag::U16)
2570        | (Type::U32, TypeTag::U32)
2571        | (Type::U64, TypeTag::U64)
2572        | (Type::U128, TypeTag::U128)
2573        | (Type::U256, TypeTag::U256)
2574        | (Type::Address, TypeTag::Address)
2575        | (Type::Signer, TypeTag::Signer) => Ok(()),
2576        (Type::Vector(inner_actual), TypeTag::Vector(inner_expected)) => {
2577            assert_expected_type(&inner_actual.element_type, inner_expected)
2578        }
2579        (Type::Datatype(actual_dt), TypeTag::Struct(expected_st)) => {
2580            assert_expected_data_type(actual_dt, expected_st)
2581        }
2582        _ => invariant_violation!(
2583            "Type mismatch between actual: {actual:?} and expected: {expected:?}"
2584        ),
2585    }
2586}
2587/// Assert the type inferred matches the expected type. This has already been done during typing,
2588/// but is checked again as an invariant. This may be removed safely at a later time if needed.
2589fn assert_expected_data_type(
2590    actual: &Datatype,
2591    expected: &StructTag,
2592) -> Result<(), ExecutionError> {
2593    let (a, m, n) = actual.qualified_ident();
2594    assert_invariant!(
2595        a == &expected.address,
2596        "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
2597    );
2598    assert_invariant!(
2599        m == expected.module.as_ident_str(),
2600        "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
2601    );
2602    assert_invariant!(
2603        n == expected.name.as_ident_str(),
2604        "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
2605    );
2606    let actual_type_arguments = &actual.type_arguments;
2607    let expected_type_arguments = &expected.type_params;
2608    assert_invariant!(
2609        actual_type_arguments.len() == expected_type_arguments.len(),
2610        "Actual type arg length does not match expected. \
2611       actual: {actual:?} vs expected: {expected:?}",
2612    );
2613    for (actual_ty, expected_ty) in actual_type_arguments
2614        .iter()
2615        .zip_debug_eq(expected_type_arguments)
2616    {
2617        assert_expected_type(actual_ty, expected_ty)?;
2618    }
2619    Ok(())
2620}