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