Skip to main content

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