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