sui_adapter_latest/static_programmable_transactions/execution/
context.rs

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