Skip to main content

sui_adapter_latest/static_programmable_transactions/execution/
context.rs

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