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    data_store::linked_data_store::LinkedDataStore,
7    execution_mode::ExecutionMode,
8    gas_charger::GasCharger,
9    gas_meter::SuiGasMeter,
10    programmable_transactions::{self as legacy_ptb},
11    sp,
12    static_programmable_transactions::{
13        env::Env,
14        execution::values::{Local, Locals, Value},
15        linkage::resolved_linkage::{ResolvedLinkage, RootedLinkage},
16        loading::ast::ObjectMutability,
17        typing::ast::{self as T, Type},
18    },
19};
20use indexmap::{IndexMap, IndexSet};
21use move_binary_format::{
22    CompiledModule,
23    errors::{Location, PartialVMError, VMResult},
24    file_format::FunctionDefinitionIndex,
25    file_format_common::VERSION_6,
26};
27use move_core_types::{
28    account_address::AccountAddress,
29    identifier::IdentStr,
30    language_storage::{ModuleId, StructTag},
31};
32use move_trace_format::format::MoveTraceBuilder;
33use move_vm_runtime::native_extensions::NativeContextExtensions;
34use move_vm_types::{
35    gas::GasMeter,
36    values::{VMValueCast, Value as VMValue},
37};
38use std::{
39    cell::RefCell,
40    collections::{BTreeMap, BTreeSet},
41    rc::Rc,
42    sync::Arc,
43};
44use sui_move_natives::object_runtime::{
45    self, LoadedRuntimeObject, ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
46};
47use sui_types::{
48    TypeTag,
49    base_types::{MoveObjectType, ObjectID, SequenceNumber, TxContext},
50    error::{ExecutionError, ExecutionErrorKind},
51    execution::ExecutionResults,
52    metrics::LimitsMetrics,
53    move_package::{MovePackage, UpgradeCap, UpgradeReceipt, UpgradeTicket},
54    object::{MoveObject, Object, Owner},
55};
56use sui_verifier::INIT_FN_NAME;
57use tracing::instrument;
58
59macro_rules! unwrap {
60    ($e:expr, $($args:expr),* $(,)?) => {
61        match $e {
62            Some(v) => v,
63            None => {
64                invariant_violation!("Unexpected none: {}", format!($($args),*))
65            }
66        }
67
68    };
69}
70
71macro_rules! object_runtime_mut {
72    ($context:ident) => {{
73        $context
74            .native_extensions
75            .get_mut::<ObjectRuntime>()
76            .map_err(|e| $context.env.convert_vm_error(e.finish(Location::Undefined)))
77    }};
78}
79
80macro_rules! charge_gas_ {
81    ($gas_charger:expr, $env:expr, $case:ident, $value_view:expr) => {{
82        SuiGasMeter($gas_charger.move_gas_status_mut())
83            .$case($value_view)
84            .map_err(|e| $env.convert_vm_error(e.finish(Location::Undefined)))
85    }};
86}
87
88macro_rules! charge_gas {
89    ($context:ident, $case:ident, $value_view:expr) => {{ charge_gas_!($context.gas_charger, $context.env, $case, $value_view) }};
90}
91
92/// Type wrapper around Value to ensure safe usage
93#[derive(Debug)]
94pub struct CtxValue(Value);
95
96#[derive(Clone, Debug)]
97pub struct InputObjectMetadata {
98    pub id: ObjectID,
99    pub mutability: ObjectMutability,
100    pub owner: Owner,
101    pub version: SequenceNumber,
102    pub type_: Type,
103}
104
105#[derive(Copy, Clone)]
106enum UsageKind {
107    Move,
108    Copy,
109    Borrow,
110}
111
112// Locals and metadata for all `Location`s. Separated from `Context` for lifetime reasons.
113struct Locations {
114    // A single local for holding the TxContext
115    tx_context_value: Locals,
116    /// The runtime value for the Gas coin, None if no gas coin is provided
117    gas: Option<(InputObjectMetadata, Locals)>,
118    /// The runtime value for the input objects args
119    input_object_metadata: Vec<(T::InputIndex, InputObjectMetadata)>,
120    object_inputs: Locals,
121    pure_input_bytes: IndexSet<Vec<u8>>,
122    pure_input_metadata: Vec<T::PureInput>,
123    pure_inputs: Locals,
124    receiving_input_metadata: Vec<T::ReceivingInput>,
125    receiving_inputs: Locals,
126    /// The results of a given command. For most commands, the inner vector will have length 1.
127    /// It will only not be 1 for Move calls with multiple return values.
128    /// Inner values are None if taken/moved by-value
129    results: Vec<Locals>,
130}
131
132enum ResolvedLocation<'a> {
133    Local(Local<'a>),
134    Pure {
135        bytes: &'a [u8],
136        metadata: &'a T::PureInput,
137        local: Local<'a>,
138    },
139    Receiving {
140        metadata: &'a T::ReceivingInput,
141        local: Local<'a>,
142    },
143}
144
145/// Maintains all runtime state specific to programmable transactions
146pub struct Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas> {
147    pub env: &'env Env<'pc, 'vm, 'state, 'linkage>,
148    /// Metrics for reporting exceeded limits
149    pub metrics: Arc<LimitsMetrics>,
150    pub native_extensions: NativeContextExtensions<'env>,
151    /// A shared transaction context, contains transaction digest information and manages the
152    /// creation of new object IDs
153    pub tx_context: Rc<RefCell<TxContext>>,
154    /// The gas charger used for metering
155    pub gas_charger: &'gas mut GasCharger,
156    /// User events are claimed after each Move call
157    user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
158    // runtime data
159    locations: Locations,
160}
161
162impl Locations {
163    /// NOTE! This does not charge gas and should not be used directly. It is exposed for
164    /// dev-inspect
165    fn resolve(&mut self, location: T::Location) -> Result<ResolvedLocation<'_>, ExecutionError> {
166        Ok(match location {
167            T::Location::TxContext => ResolvedLocation::Local(self.tx_context_value.local(0)?),
168            T::Location::GasCoin => {
169                let (_, gas_locals) = unwrap!(self.gas.as_mut(), "Gas coin not provided");
170                ResolvedLocation::Local(gas_locals.local(0)?)
171            }
172            T::Location::ObjectInput(i) => ResolvedLocation::Local(self.object_inputs.local(i)?),
173            T::Location::Result(i, j) => {
174                let result = unwrap!(self.results.get_mut(i as usize), "bounds already verified");
175                ResolvedLocation::Local(result.local(j)?)
176            }
177            T::Location::PureInput(i) => {
178                let local = self.pure_inputs.local(i)?;
179                let metadata = &self.pure_input_metadata[i as usize];
180                let bytes = self
181                    .pure_input_bytes
182                    .get_index(metadata.byte_index)
183                    .ok_or_else(|| {
184                        make_invariant_violation!(
185                            "Pure input {} bytes out of bounds at index {}",
186                            metadata.original_input_index.0,
187                            metadata.byte_index,
188                        )
189                    })?;
190                ResolvedLocation::Pure {
191                    bytes,
192                    metadata,
193                    local,
194                }
195            }
196            T::Location::ReceivingInput(i) => ResolvedLocation::Receiving {
197                metadata: &self.receiving_input_metadata[i as usize],
198                local: self.receiving_inputs.local(i)?,
199            },
200        })
201    }
202}
203
204impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas> {
205    #[instrument(name = "Context::new", level = "trace", skip_all)]
206    pub fn new(
207        env: &'env Env<'pc, 'vm, 'state, 'linkage>,
208        metrics: Arc<LimitsMetrics>,
209        tx_context: Rc<RefCell<TxContext>>,
210        gas_charger: &'gas mut GasCharger,
211        pure_input_bytes: IndexSet<Vec<u8>>,
212        object_inputs: Vec<T::ObjectInput>,
213        pure_input_metadata: Vec<T::PureInput>,
214        receiving_input_metadata: Vec<T::ReceivingInput>,
215    ) -> Result<Self, ExecutionError>
216    where
217        'pc: 'state,
218    {
219        let mut input_object_map = BTreeMap::new();
220        let mut input_object_metadata = Vec::with_capacity(object_inputs.len());
221        let mut object_values = Vec::with_capacity(object_inputs.len());
222        for object_input in object_inputs {
223            let (i, m, v) = load_object_arg(gas_charger, env, &mut input_object_map, object_input)?;
224            input_object_metadata.push((i, m));
225            object_values.push(Some(v));
226        }
227        let object_inputs = Locals::new(object_values)?;
228        let pure_inputs = Locals::new_invalid(pure_input_metadata.len())?;
229        let receiving_inputs = Locals::new_invalid(receiving_input_metadata.len())?;
230        let gas = match gas_charger.gas_coin() {
231            Some(gas_coin) => {
232                let ty = env.gas_coin_type()?;
233                let (gas_metadata, gas_value) = load_object_arg_impl(
234                    gas_charger,
235                    env,
236                    &mut input_object_map,
237                    gas_coin,
238                    ObjectMutability::Mutable,
239                    ty,
240                )?;
241                let mut gas_locals = Locals::new([Some(gas_value)])?;
242                let gas_local = gas_locals.local(0)?;
243                let gas_ref = gas_local.borrow()?;
244                // We have already checked that the gas balance is enough to cover the gas budget
245                let max_gas_in_balance = gas_charger.gas_budget();
246                gas_ref.coin_ref_subtract_balance(max_gas_in_balance)?;
247                Some((gas_metadata, gas_locals))
248            }
249            None => None,
250        };
251        let native_extensions = adapter::new_native_extensions(
252            env.state_view.as_child_resolver(),
253            input_object_map,
254            !gas_charger.is_unmetered(),
255            env.protocol_config,
256            metrics.clone(),
257            tx_context.clone(),
258        );
259
260        let tx_context_value = Locals::new(vec![Some(Value::new_tx_context(
261            tx_context.borrow().digest(),
262        )?)])?;
263        Ok(Self {
264            env,
265            metrics,
266            native_extensions,
267            tx_context,
268            gas_charger,
269            user_events: vec![],
270            locations: Locations {
271                tx_context_value,
272                gas,
273                input_object_metadata,
274                object_inputs,
275                pure_input_bytes,
276                pure_input_metadata,
277                pure_inputs,
278                receiving_input_metadata,
279                receiving_inputs,
280                results: vec![],
281            },
282        })
283    }
284
285    pub fn finish<Mode: ExecutionMode>(mut self) -> Result<ExecutionResults, ExecutionError> {
286        assert_invariant!(
287            !self.locations.tx_context_value.local(0)?.is_invalid()?,
288            "tx context value should be present"
289        );
290        let gas = std::mem::take(&mut self.locations.gas);
291        let object_input_metadata = std::mem::take(&mut self.locations.input_object_metadata);
292        let mut object_inputs =
293            std::mem::replace(&mut self.locations.object_inputs, Locals::new_invalid(0)?);
294        let mut loaded_runtime_objects = BTreeMap::new();
295        let mut by_value_shared_objects = BTreeSet::new();
296        let mut consensus_owner_objects = BTreeMap::new();
297        let gas = gas
298            .map(|(m, mut g)| Result::<_, ExecutionError>::Ok((m, g.local(0)?.move_if_valid()?)))
299            .transpose()?;
300        let gas_id_opt = gas.as_ref().map(|(m, _)| m.id);
301        let object_inputs = object_input_metadata
302            .into_iter()
303            .enumerate()
304            .map(|(i, (_, m))| {
305                let v_opt = object_inputs.local(i as u16)?.move_if_valid()?;
306                Ok((m, v_opt))
307            })
308            .collect::<Result<Vec<_>, ExecutionError>>()?;
309        for (metadata, value_opt) in object_inputs.into_iter().chain(gas) {
310            let InputObjectMetadata {
311                id,
312                mutability,
313                owner,
314                version,
315                type_,
316            } = metadata;
317            match mutability {
318                ObjectMutability::Immutable => continue,
319                // It is illegal to mutate NonExclusiveWrites, but they are passed as &mut T,
320                // so we need to treat them as mutable here. After execution, we check if they
321                // have been mutated, and abort the tx if they have.
322                ObjectMutability::NonExclusiveWrite | ObjectMutability::Mutable => (),
323            }
324            loaded_runtime_objects.insert(
325                id,
326                LoadedRuntimeObject {
327                    version,
328                    is_modified: true,
329                },
330            );
331            if let Some(object) = value_opt {
332                self.transfer_object_(
333                    owner,
334                    type_,
335                    CtxValue(object),
336                    /* end of transaction */ true,
337                )?;
338            } else if owner.is_shared() {
339                by_value_shared_objects.insert(id);
340            } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
341                consensus_owner_objects.insert(id, owner.clone());
342            }
343        }
344
345        let Self {
346            env,
347            mut native_extensions,
348            tx_context,
349            gas_charger,
350            user_events,
351            ..
352        } = self;
353        let ref_context: &RefCell<TxContext> = &tx_context;
354        let tx_context: &TxContext = &ref_context.borrow();
355        let tx_digest = ref_context.borrow().digest();
356
357        let object_runtime: ObjectRuntime = native_extensions
358            .remove()
359            .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
360
361        let RuntimeResults {
362            mut writes,
363            user_events: remaining_events,
364            loaded_child_objects,
365            mut created_object_ids,
366            deleted_object_ids,
367            accumulator_events,
368            settlement_input_sui,
369            settlement_output_sui,
370        } = object_runtime.finish()?;
371        assert_invariant!(
372            remaining_events.is_empty(),
373            "Events should be taken after every Move call"
374        );
375        // Refund unused gas
376        if let Some(gas_id) = gas_id_opt {
377            refund_max_gas_budget(&mut writes, gas_charger, gas_id)?;
378        }
379
380        loaded_runtime_objects.extend(loaded_child_objects);
381
382        let mut written_objects = BTreeMap::new();
383
384        for (id, (recipient, ty, value)) in writes {
385            let ty: Type = env.load_type_from_struct(&ty.clone().into())?;
386            let abilities = ty.abilities();
387            let has_public_transfer = abilities.has_store();
388            let Some(bytes) = value.serialize() else {
389                invariant_violation!("Failed to serialize already deserialized Move value");
390            };
391            // safe because has_public_transfer has been determined by the abilities
392            let move_object = unsafe {
393                create_written_object::<Mode>(
394                    env,
395                    &loaded_runtime_objects,
396                    id,
397                    ty,
398                    has_public_transfer,
399                    bytes,
400                )?
401            };
402            let object = Object::new_move(move_object, recipient, tx_digest);
403            written_objects.insert(id, object);
404        }
405
406        for package in self.env.linkable_store.to_new_packages().into_iter() {
407            let package_obj = Object::new_from_package(package, tx_digest);
408            let id = package_obj.id();
409            created_object_ids.insert(id);
410            written_objects.insert(id, package_obj);
411        }
412
413        legacy_ptb::context::finish(
414            env.protocol_config,
415            env.state_view,
416            gas_charger,
417            tx_context,
418            &by_value_shared_objects,
419            &consensus_owner_objects,
420            loaded_runtime_objects,
421            written_objects,
422            created_object_ids,
423            deleted_object_ids,
424            user_events,
425            accumulator_events,
426            settlement_input_sui,
427            settlement_output_sui,
428        )
429    }
430
431    pub fn object_runtime(&self) -> Result<&ObjectRuntime<'_>, ExecutionError> {
432        self.native_extensions
433            .get::<ObjectRuntime>()
434            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))
435    }
436
437    pub fn take_user_events(
438        &mut self,
439        storage_id: ModuleId,
440        function_def_idx: FunctionDefinitionIndex,
441        instr_length: u16,
442        linkage: &RootedLinkage,
443    ) -> Result<(), ExecutionError> {
444        let events = object_runtime_mut!(self)?.take_user_events();
445        let num_events = self.user_events.len() + events.len();
446        let max_events = self.env.protocol_config.max_num_event_emit();
447        if num_events as u64 > max_events {
448            let err = max_event_error(max_events)
449                .at_code_offset(function_def_idx, instr_length)
450                .finish(Location::Module(storage_id.clone()));
451            return Err(self.env.convert_linked_vm_error(err, linkage));
452        }
453        let new_events = events
454            .into_iter()
455            .map(|(tag, value)| {
456                let Some(bytes) = value.serialize() else {
457                    invariant_violation!("Failed to serialize Move event");
458                };
459                Ok((storage_id.clone(), tag, bytes))
460            })
461            .collect::<Result<Vec<_>, ExecutionError>>()?;
462        self.user_events.extend(new_events);
463        Ok(())
464    }
465
466    //
467    // Arguments and Values
468    //
469
470    fn location(
471        &mut self,
472        usage: UsageKind,
473        location: T::Location,
474    ) -> Result<Value, ExecutionError> {
475        let resolved = self.locations.resolve(location)?;
476        let mut local = match resolved {
477            ResolvedLocation::Local(l) => l,
478            ResolvedLocation::Pure {
479                bytes,
480                metadata,
481                mut local,
482            } => {
483                if local.is_invalid()? {
484                    let v = load_pure_value(self.gas_charger, self.env, bytes, metadata)?;
485                    local.store(v)?;
486                }
487                local
488            }
489            ResolvedLocation::Receiving {
490                metadata,
491                mut local,
492            } => {
493                if local.is_invalid()? {
494                    let v = load_receiving_value(self.gas_charger, self.env, metadata)?;
495                    local.store(v)?;
496                }
497                local
498            }
499        };
500        Ok(match usage {
501            UsageKind::Move => local.move_()?,
502            UsageKind::Copy => {
503                let value = local.copy()?;
504                charge_gas_!(self.gas_charger, self.env, charge_copy_loc, &value)?;
505                value
506            }
507            UsageKind::Borrow => local.borrow()?,
508        })
509    }
510
511    fn location_usage(&mut self, usage: T::Usage) -> Result<Value, ExecutionError> {
512        match usage {
513            T::Usage::Move(location) => self.location(UsageKind::Move, location),
514            T::Usage::Copy { location, .. } => self.location(UsageKind::Copy, location),
515        }
516    }
517
518    fn argument_value(&mut self, sp!(_, (arg_, _)): T::Argument) -> Result<Value, ExecutionError> {
519        match arg_ {
520            T::Argument__::Use(usage) => self.location_usage(usage),
521            // freeze is a no-op for references since the value does not track mutability
522            T::Argument__::Freeze(usage) => self.location_usage(usage),
523            T::Argument__::Borrow(_, location) => self.location(UsageKind::Borrow, location),
524            T::Argument__::Read(usage) => {
525                let reference = self.location_usage(usage)?;
526                charge_gas!(self, charge_read_ref, &reference)?;
527                reference.read_ref()
528            }
529        }
530    }
531
532    pub fn argument<V>(&mut self, arg: T::Argument) -> Result<V, ExecutionError>
533    where
534        VMValue: VMValueCast<V>,
535    {
536        let value = self.argument_value(arg)?;
537        let value: V = value.cast()?;
538        Ok(value)
539    }
540
541    pub fn arguments<V>(&mut self, args: Vec<T::Argument>) -> Result<Vec<V>, ExecutionError>
542    where
543        VMValue: VMValueCast<V>,
544    {
545        args.into_iter().map(|arg| self.argument(arg)).collect()
546    }
547
548    pub fn result(&mut self, result: Vec<Option<CtxValue>>) -> Result<(), ExecutionError> {
549        self.locations
550            .results
551            .push(Locals::new(result.into_iter().map(|v| v.map(|v| v.0)))?);
552        Ok(())
553    }
554
555    pub fn copy_value(&mut self, value: &CtxValue) -> Result<CtxValue, ExecutionError> {
556        Ok(CtxValue(copy_value(self.gas_charger, self.env, &value.0)?))
557    }
558
559    pub fn new_coin(&mut self, amount: u64) -> Result<CtxValue, ExecutionError> {
560        let id = self.tx_context.borrow_mut().fresh_id();
561        object_runtime_mut!(self)?
562            .new_id(id)
563            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
564        Ok(CtxValue(Value::coin(id, amount)))
565    }
566
567    pub fn destroy_coin(&mut self, coin: CtxValue) -> Result<u64, ExecutionError> {
568        let (id, amount) = coin.0.unpack_coin()?;
569        object_runtime_mut!(self)?
570            .delete_id(id)
571            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
572        Ok(amount)
573    }
574
575    pub fn new_upgrade_cap(&mut self, storage_id: ObjectID) -> Result<CtxValue, ExecutionError> {
576        let id = self.tx_context.borrow_mut().fresh_id();
577        object_runtime_mut!(self)?
578            .new_id(id)
579            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
580        let cap = UpgradeCap::new(id, storage_id);
581        Ok(CtxValue(Value::upgrade_cap(cap)))
582    }
583
584    pub fn upgrade_receipt(
585        &self,
586        upgrade_ticket: UpgradeTicket,
587        upgraded_package_id: ObjectID,
588    ) -> CtxValue {
589        let receipt = UpgradeReceipt::new(upgrade_ticket, upgraded_package_id);
590        CtxValue(Value::upgrade_receipt(receipt))
591    }
592
593    //
594    // Move calls
595    //
596
597    pub fn vm_move_call(
598        &mut self,
599        function: T::LoadedFunction,
600        args: Vec<CtxValue>,
601        trace_builder_opt: Option<&mut MoveTraceBuilder>,
602    ) -> Result<Vec<CtxValue>, ExecutionError> {
603        let result = self.execute_function_bypass_visibility(
604            &function.runtime_id,
605            &function.name,
606            &function.type_arguments,
607            args,
608            &function.linkage,
609            trace_builder_opt,
610        )?;
611        self.take_user_events(
612            function.storage_id,
613            function.definition_index,
614            function.instruction_length,
615            &function.linkage,
616        )?;
617        Ok(result)
618    }
619
620    pub fn execute_function_bypass_visibility(
621        &mut self,
622        runtime_id: &ModuleId,
623        function_name: &IdentStr,
624        ty_args: &[Type],
625        args: Vec<CtxValue>,
626        linkage: &RootedLinkage,
627        tracer: Option<&mut MoveTraceBuilder>,
628    ) -> Result<Vec<CtxValue>, ExecutionError> {
629        let ty_args = ty_args
630            .iter()
631            .enumerate()
632            .map(|(idx, ty)| self.env.load_vm_type_argument_from_adapter_type(idx, ty))
633            .collect::<Result<_, _>>()?;
634        let gas_status = self.gas_charger.move_gas_status_mut();
635        let mut data_store = LinkedDataStore::new(linkage, self.env.linkable_store);
636        let values = self
637            .env
638            .vm
639            .get_runtime()
640            .execute_function_with_values_bypass_visibility(
641                runtime_id,
642                function_name,
643                ty_args,
644                args.into_iter().map(|v| v.0.into()).collect(),
645                &mut data_store,
646                &mut SuiGasMeter(gas_status),
647                &mut self.native_extensions,
648                tracer,
649            )
650            .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
651        Ok(values.into_iter().map(|v| CtxValue(v.into())).collect())
652    }
653
654    //
655    // Publish and Upgrade
656    //
657
658    // is_upgrade is used for gas charging. Assumed to be a new publish if false.
659    pub fn deserialize_modules(
660        &mut self,
661        module_bytes: &[Vec<u8>],
662        is_upgrade: bool,
663    ) -> Result<Vec<CompiledModule>, ExecutionError> {
664        assert_invariant!(
665            !module_bytes.is_empty(),
666            "empty package is checked in transaction input checker"
667        );
668        let total_bytes = module_bytes.iter().map(|v| v.len()).sum();
669        if is_upgrade {
670            self.gas_charger.charge_upgrade_package(total_bytes)?
671        } else {
672            self.gas_charger.charge_publish_package(total_bytes)?
673        }
674
675        let binary_config = self.env.protocol_config.binary_config(None);
676        let modules = module_bytes
677            .iter()
678            .map(|b| {
679                CompiledModule::deserialize_with_config(b, &binary_config)
680                    .map_err(|e| e.finish(Location::Undefined))
681            })
682            .collect::<VMResult<Vec<CompiledModule>>>()
683            .map_err(|e| self.env.convert_vm_error(e))?;
684        Ok(modules)
685    }
686
687    fn fetch_package(
688        &mut self,
689        dependency_id: &ObjectID,
690    ) -> Result<Rc<MovePackage>, ExecutionError> {
691        let [fetched_package] = self.fetch_packages(&[*dependency_id])?.try_into().map_err(
692            |_| {
693                make_invariant_violation!(
694                    "We should always fetch a single package for each object or return a dependency error."
695                )
696            },
697        )?;
698        Ok(fetched_package)
699    }
700
701    fn fetch_packages(
702        &mut self,
703        dependency_ids: &[ObjectID],
704    ) -> Result<Vec<Rc<MovePackage>>, ExecutionError> {
705        let mut fetched = vec![];
706        let mut missing = vec![];
707
708        for id in dependency_ids {
709            match self.env.linkable_store.get_package(id) {
710                Err(e) => {
711                    return Err(ExecutionError::new_with_source(
712                        ExecutionErrorKind::PublishUpgradeMissingDependency,
713                        e,
714                    ));
715                }
716                Ok(Some(inner)) => {
717                    fetched.push(inner);
718                }
719                Ok(None) => {
720                    missing.push(*id);
721                }
722            }
723        }
724
725        if missing.is_empty() {
726            assert_invariant!(
727                fetched.len() == dependency_ids.len(),
728                "all dependencies should be fetched"
729            );
730            Ok(fetched)
731        } else {
732            let msg = format!(
733                "Missing dependencies: {}",
734                missing
735                    .into_iter()
736                    .map(|dep| format!("{}", dep))
737                    .collect::<Vec<_>>()
738                    .join(", ")
739            );
740            Err(ExecutionError::new_with_source(
741                ExecutionErrorKind::PublishUpgradeMissingDependency,
742                msg,
743            ))
744        }
745    }
746
747    fn publish_and_verify_modules(
748        &mut self,
749        package_id: ObjectID,
750        modules: &[CompiledModule],
751        linkage: &RootedLinkage,
752    ) -> Result<(), ExecutionError> {
753        // TODO(https://github.com/MystenLabs/sui/issues/69): avoid this redundant serialization by exposing VM API that allows us to run the linker directly on `Vec<CompiledModule>`
754        let binary_version = self.env.protocol_config.move_binary_format_version();
755        let new_module_bytes: Vec<_> = modules
756            .iter()
757            .map(|m| {
758                let mut bytes = Vec::new();
759                let version = if binary_version > VERSION_6 {
760                    m.version
761                } else {
762                    VERSION_6
763                };
764                m.serialize_with_version(version, &mut bytes).unwrap();
765                bytes
766            })
767            .collect();
768        let mut data_store = LinkedDataStore::new(linkage, self.env.linkable_store);
769        self.env
770            .vm
771            .get_runtime()
772            .publish_module_bundle(
773                new_module_bytes,
774                AccountAddress::from(package_id),
775                &mut data_store,
776                &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
777            )
778            .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
779
780        // run the Sui verifier
781        for module in modules {
782            // Run Sui bytecode verifier, which runs some additional checks that assume the Move
783            // bytecode verifier has passed.
784            sui_verifier::verifier::sui_verify_module_unmetered(
785                module,
786                &BTreeMap::new(),
787                &self
788                    .env
789                    .protocol_config
790                    .verifier_config(/* signing_limits */ None),
791            )?;
792        }
793
794        Ok(())
795    }
796
797    fn init_modules(
798        &mut self,
799        package_id: ObjectID,
800        modules: &[CompiledModule],
801        linkage: &RootedLinkage,
802        mut trace_builder_opt: Option<&mut MoveTraceBuilder>,
803    ) -> Result<(), ExecutionError> {
804        for module in modules {
805            let Some((fdef_idx, fdef)) = module.find_function_def_by_name(INIT_FN_NAME.as_str())
806            else {
807                continue;
808            };
809            let fhandle = module.function_handle_at(fdef.function);
810            let fparameters = module.signature_at(fhandle.parameters);
811            assert_invariant!(
812                fparameters.0.len() <= 2,
813                "init function should have at most 2 parameters"
814            );
815            let has_otw = fparameters.0.len() == 2;
816            let tx_context = self
817                .location(UsageKind::Borrow, T::Location::TxContext)
818                .map_err(|e| {
819                    make_invariant_violation!("Failed to get tx context for init function: {}", e)
820                })?;
821            let args = if has_otw {
822                vec![CtxValue(Value::one_time_witness()?), CtxValue(tx_context)]
823            } else {
824                vec![CtxValue(tx_context)]
825            };
826            let return_values = self.execute_function_bypass_visibility(
827                &module.self_id(),
828                INIT_FN_NAME,
829                &[],
830                args,
831                linkage,
832                trace_builder_opt.as_deref_mut(),
833            )?;
834
835            let storage_id = ModuleId::new(package_id.into(), module.self_id().name().to_owned());
836            self.take_user_events(
837                storage_id,
838                fdef_idx,
839                fdef.code.as_ref().map(|c| c.code.len() as u16).unwrap_or(0),
840                linkage,
841            )?;
842            assert_invariant!(
843                return_values.is_empty(),
844                "init should not have return values"
845            )
846        }
847
848        Ok(())
849    }
850
851    pub fn publish_and_init_package<Mode: ExecutionMode>(
852        &mut self,
853        mut modules: Vec<CompiledModule>,
854        dep_ids: &[ObjectID],
855        linkage: ResolvedLinkage,
856        trace_builder_opt: Option<&mut MoveTraceBuilder>,
857    ) -> Result<ObjectID, ExecutionError> {
858        let runtime_id = if <Mode>::packages_are_predefined() {
859            // do not calculate or substitute id for predefined packages
860            (*modules[0].self_id().address()).into()
861        } else {
862            // It should be fine that this does not go through the object runtime since it does not
863            // need to know about new packages created, since Move objects and Move packages
864            // cannot interact
865            let id = self.tx_context.borrow_mut().fresh_id();
866            adapter::substitute_package_id(&mut modules, id)?;
867            id
868        };
869
870        let dependencies = self.fetch_packages(dep_ids)?;
871        let package = Rc::new(MovePackage::new_initial(
872            &modules,
873            self.env.protocol_config,
874            dependencies.iter().map(|p| p.as_ref()),
875        )?);
876        let package_id = package.id();
877
878        // Here we optimistically push the package that is being published/upgraded
879        // and if there is an error of any kind (verification or module init) we
880        // remove it.
881        // The call to `pop_last_package` later is fine because we cannot re-enter and
882        // the last package we pushed is the one we are verifying and running the init from
883        let linkage = RootedLinkage::new_for_publication(package_id, runtime_id, linkage);
884
885        self.env.linkable_store.push_package(package_id, package)?;
886        let res = self
887            .publish_and_verify_modules(runtime_id, &modules, &linkage)
888            .and_then(|_| self.init_modules(package_id, &modules, &linkage, trace_builder_opt));
889        match res {
890            Ok(()) => Ok(runtime_id),
891            Err(e) => {
892                self.env.linkable_store.pop_package(package_id)?;
893                Err(e)
894            }
895        }
896    }
897
898    pub fn upgrade(
899        &mut self,
900        mut modules: Vec<CompiledModule>,
901        dep_ids: &[ObjectID],
902        current_package_id: ObjectID,
903        upgrade_ticket_policy: u8,
904        linkage: ResolvedLinkage,
905    ) -> Result<ObjectID, ExecutionError> {
906        // Check that this package ID points to a package and get the package we're upgrading.
907        let current_move_package = self.fetch_package(&current_package_id)?;
908
909        let runtime_id = current_move_package.original_package_id();
910        adapter::substitute_package_id(&mut modules, runtime_id)?;
911
912        // Upgraded packages share their predecessor's runtime ID but get a new storage ID.
913        // It should be fine that this does not go through the object runtime since it does not
914        // need to know about new packages created, since Move objects and Move packages
915        // cannot interact
916        let storage_id = self.tx_context.borrow_mut().fresh_id();
917
918        let dependencies = self.fetch_packages(dep_ids)?;
919        let package = current_move_package.new_upgraded(
920            storage_id,
921            &modules,
922            self.env.protocol_config,
923            dependencies.iter().map(|p| p.as_ref()),
924        )?;
925
926        let linkage = RootedLinkage::new_for_publication(storage_id, runtime_id, linkage);
927        self.publish_and_verify_modules(runtime_id, &modules, &linkage)?;
928
929        legacy_ptb::execution::check_compatibility(
930            self.env.protocol_config,
931            current_move_package.as_ref(),
932            &modules,
933            upgrade_ticket_policy,
934        )?;
935
936        if self.env.protocol_config.check_for_init_during_upgrade() {
937            // find newly added modules to the package,
938            // and error if they have init functions
939            let current_module_names: BTreeSet<&str> = current_move_package
940                .serialized_module_map()
941                .keys()
942                .map(|s| s.as_str())
943                .collect();
944            let upgrade_module_names: BTreeSet<&str> = package
945                .serialized_module_map()
946                .keys()
947                .map(|s| s.as_str())
948                .collect();
949            let new_module_names = upgrade_module_names
950                .difference(&current_module_names)
951                .copied()
952                .collect::<BTreeSet<&str>>();
953            let new_modules = modules
954                .iter()
955                .filter(|m| {
956                    let name = m.identifier_at(m.self_handle().name).as_str();
957                    new_module_names.contains(name)
958                })
959                .collect::<Vec<&CompiledModule>>();
960            let new_module_has_init = new_modules.iter().any(|module| {
961                module.function_defs.iter().any(|fdef| {
962                    let fhandle = module.function_handle_at(fdef.function);
963                    let fname = module.identifier_at(fhandle.name);
964                    fname == INIT_FN_NAME
965                })
966            });
967            if new_module_has_init {
968                // TODO we cannot run 'init' on upgrade yet due to global type cache limitations
969                return Err(ExecutionError::new_with_source(
970                    ExecutionErrorKind::FeatureNotYetSupported,
971                    "`init` in new modules on upgrade is not yet supported",
972                ));
973            }
974        }
975
976        self.env
977            .linkable_store
978            .push_package(storage_id, Rc::new(package))?;
979        Ok(storage_id)
980    }
981
982    //
983    // Commands
984    //
985
986    pub fn transfer_object(
987        &mut self,
988        recipient: Owner,
989        ty: Type,
990        object: CtxValue,
991    ) -> Result<(), ExecutionError> {
992        self.transfer_object_(recipient, ty, object, /* end of transaction */ false)
993    }
994
995    fn transfer_object_(
996        &mut self,
997        recipient: Owner,
998        ty: Type,
999        object: CtxValue,
1000        end_of_transaction: bool,
1001    ) -> Result<(), ExecutionError> {
1002        let tag = TypeTag::try_from(ty)
1003            .map_err(|_| make_invariant_violation!("Unable to convert Type to TypeTag"))?;
1004        let TypeTag::Struct(tag) = tag else {
1005            invariant_violation!("Expected struct type tag");
1006        };
1007        let ty = MoveObjectType::from(*tag);
1008        object_runtime_mut!(self)?
1009            .transfer(recipient, ty, object.0.into(), end_of_transaction)
1010            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
1011        Ok(())
1012    }
1013
1014    //
1015    // Dev Inspect tracking
1016    //
1017
1018    pub fn argument_updates(
1019        &mut self,
1020        args: Vec<T::Argument>,
1021    ) -> Result<Vec<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, ExecutionError> {
1022        args.into_iter()
1023            .filter_map(|arg| self.argument_update(arg).transpose())
1024            .collect()
1025    }
1026
1027    fn argument_update(
1028        &mut self,
1029        sp!(_, (arg, ty)): T::Argument,
1030    ) -> Result<Option<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, ExecutionError> {
1031        use sui_types::transaction::Argument as TxArgument;
1032        let ty = match ty {
1033            Type::Reference(true, inner) => (*inner).clone(),
1034            ty => {
1035                debug_assert!(
1036                    false,
1037                    "Unexpected non reference type in location update: {ty:?}"
1038                );
1039                return Ok(None);
1040            }
1041        };
1042        let Ok(tag): Result<TypeTag, _> = ty.clone().try_into() else {
1043            invariant_violation!("unable to generate type tag from type")
1044        };
1045        let location = arg.location();
1046        let resolved = self.locations.resolve(location)?;
1047        let local = match resolved {
1048            ResolvedLocation::Local(local)
1049            | ResolvedLocation::Pure { local, .. }
1050            | ResolvedLocation::Receiving { local, .. } => local,
1051        };
1052        if local.is_invalid()? {
1053            return Ok(None);
1054        }
1055        // copy the value from the local
1056        let value = local.copy()?;
1057        let value = match arg {
1058            T::Argument__::Use(_) => {
1059                // dereference the reference
1060                value.read_ref()?
1061            }
1062            T::Argument__::Borrow(_, _) => {
1063                // value is not a reference, nothing to do
1064                value
1065            }
1066            T::Argument__::Freeze(_) => {
1067                invariant_violation!("freeze should not be used for a mutable reference")
1068            }
1069            T::Argument__::Read(_) => {
1070                invariant_violation!("read should not return a reference")
1071            }
1072        };
1073        let Some(bytes) = value.serialize() else {
1074            invariant_violation!("Failed to serialize Move value");
1075        };
1076        let arg = match location {
1077            T::Location::TxContext => return Ok(None),
1078            T::Location::GasCoin => TxArgument::GasCoin,
1079            T::Location::Result(i, j) => TxArgument::NestedResult(i, j),
1080            T::Location::ObjectInput(i) => {
1081                TxArgument::Input(self.locations.input_object_metadata[i as usize].0.0)
1082            }
1083            T::Location::PureInput(i) => TxArgument::Input(
1084                self.locations.pure_input_metadata[i as usize]
1085                    .original_input_index
1086                    .0,
1087            ),
1088            T::Location::ReceivingInput(i) => TxArgument::Input(
1089                self.locations.receiving_input_metadata[i as usize]
1090                    .original_input_index
1091                    .0,
1092            ),
1093        };
1094        Ok(Some((arg, bytes, tag)))
1095    }
1096
1097    pub fn tracked_results(
1098        &self,
1099        results: &[CtxValue],
1100        result_tys: &T::ResultType,
1101    ) -> Result<Vec<(Vec<u8>, TypeTag)>, ExecutionError> {
1102        assert_invariant!(
1103            results.len() == result_tys.len(),
1104            "results and result types should match"
1105        );
1106        results
1107            .iter()
1108            .zip(result_tys)
1109            .map(|(v, ty)| self.tracked_result(&v.0, ty.clone()))
1110            .collect()
1111    }
1112
1113    fn tracked_result(
1114        &self,
1115        result: &Value,
1116        ty: Type,
1117    ) -> Result<(Vec<u8>, TypeTag), ExecutionError> {
1118        let inner_value;
1119        let (v, ty) = match ty {
1120            Type::Reference(_, inner) => {
1121                inner_value = result.copy()?.read_ref()?;
1122                (&inner_value, (*inner).clone())
1123            }
1124            _ => (result, ty),
1125        };
1126        let Some(bytes) = v.serialize() else {
1127            invariant_violation!("Failed to serialize Move value");
1128        };
1129        let Ok(tag): Result<TypeTag, _> = ty.try_into() else {
1130            invariant_violation!("unable to generate type tag from type")
1131        };
1132        Ok((bytes, tag))
1133    }
1134}
1135
1136impl VMValueCast<CtxValue> for VMValue {
1137    fn cast(self) -> Result<CtxValue, PartialVMError> {
1138        Ok(CtxValue(self.into()))
1139    }
1140}
1141
1142impl CtxValue {
1143    pub fn vec_pack(ty: Type, values: Vec<CtxValue>) -> Result<CtxValue, ExecutionError> {
1144        Ok(CtxValue(Value::vec_pack(
1145            ty,
1146            values.into_iter().map(|v| v.0).collect(),
1147        )?))
1148    }
1149
1150    pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
1151        self.0.coin_ref_value()
1152    }
1153
1154    pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
1155        self.0.coin_ref_subtract_balance(amount)
1156    }
1157
1158    pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
1159        self.0.coin_ref_add_balance(amount)
1160    }
1161
1162    pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
1163        self.0.into_upgrade_ticket()
1164    }
1165}
1166
1167fn load_object_arg(
1168    meter: &mut GasCharger,
1169    env: &Env,
1170    input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1171    input: T::ObjectInput,
1172) -> Result<(T::InputIndex, InputObjectMetadata, Value), ExecutionError> {
1173    let id = input.arg.id();
1174    let mutability = input.arg.mutability();
1175    let (metadata, value) =
1176        load_object_arg_impl(meter, env, input_object_map, id, mutability, input.ty)?;
1177    Ok((input.original_input_index, metadata, value))
1178}
1179
1180fn load_object_arg_impl(
1181    meter: &mut GasCharger,
1182    env: &Env,
1183    input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1184    id: ObjectID,
1185    mutability: ObjectMutability,
1186    ty: T::Type,
1187) -> Result<(InputObjectMetadata, Value), ExecutionError> {
1188    let obj = env.read_object(&id)?;
1189    let owner = obj.owner.clone();
1190    let version = obj.version();
1191    let object_metadata = InputObjectMetadata {
1192        id,
1193        mutability,
1194        owner: owner.clone(),
1195        version,
1196        type_: ty.clone(),
1197    };
1198    let sui_types::object::ObjectInner {
1199        data: sui_types::object::Data::Move(move_obj),
1200        ..
1201    } = obj.as_inner()
1202    else {
1203        invariant_violation!("Expected a Move object");
1204    };
1205    let contained_uids = {
1206        let fully_annotated_layout = env.fully_annotated_layout(&ty)?;
1207        get_all_uids(&fully_annotated_layout, move_obj.contents()).map_err(|e| {
1208            make_invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1209        })?
1210    };
1211    input_object_map.insert(
1212        id,
1213        object_runtime::InputObject {
1214            contained_uids,
1215            version,
1216            owner,
1217        },
1218    );
1219
1220    let v = Value::deserialize(env, move_obj.contents(), ty)?;
1221    charge_gas_!(meter, env, charge_copy_loc, &v)?;
1222    Ok((object_metadata, v))
1223}
1224
1225fn load_pure_value(
1226    meter: &mut GasCharger,
1227    env: &Env,
1228    bytes: &[u8],
1229    metadata: &T::PureInput,
1230) -> Result<Value, ExecutionError> {
1231    let loaded = Value::deserialize(env, bytes, metadata.ty.clone())?;
1232    // ByteValue::Receiving { id, version } => Value::receiving(*id, *version),
1233    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1234    Ok(loaded)
1235}
1236
1237fn load_receiving_value(
1238    meter: &mut GasCharger,
1239    env: &Env,
1240    metadata: &T::ReceivingInput,
1241) -> Result<Value, ExecutionError> {
1242    let (id, version, _) = metadata.object_ref;
1243    let loaded = Value::receiving(id, version);
1244    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1245    Ok(loaded)
1246}
1247
1248fn copy_value(meter: &mut GasCharger, env: &Env, value: &Value) -> Result<Value, ExecutionError> {
1249    charge_gas_!(meter, env, charge_copy_loc, value)?;
1250    value.copy()
1251}
1252
1253/// The max budget was deducted from the gas coin at the beginning of the transaction,
1254/// now we return exactly that amount. Gas will be charged by the execution engine
1255fn refund_max_gas_budget<OType>(
1256    writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1257    gas_charger: &mut GasCharger,
1258    gas_id: ObjectID,
1259) -> Result<(), ExecutionError> {
1260    let Some((_, _, value_ref)) = writes.get_mut(&gas_id) else {
1261        invariant_violation!("Gas object cannot be wrapped or destroyed")
1262    };
1263    // replace with dummy value
1264    let value = std::mem::replace(value_ref, VMValue::u8(0));
1265    let mut locals = Locals::new([Some(value.into())])?;
1266    let mut local = locals.local(0)?;
1267    let coin_value = local.borrow()?.coin_ref_value()?;
1268    let additional = gas_charger.gas_budget();
1269    if coin_value.checked_add(additional).is_none() {
1270        return Err(ExecutionError::new_with_source(
1271            ExecutionErrorKind::CoinBalanceOverflow,
1272            "Gas coin too large after returning the max gas budget",
1273        ));
1274    };
1275    local.borrow()?.coin_ref_add_balance(additional)?;
1276    // put the value back
1277    *value_ref = local.move_()?.into();
1278    Ok(())
1279}
1280
1281/// Generate an MoveObject given an updated/written object
1282/// # Safety
1283///
1284/// This function assumes proper generation of has_public_transfer, either from the abilities of
1285/// the StructTag, or from the runtime correctly propagating from the inputs
1286unsafe fn create_written_object<Mode: ExecutionMode>(
1287    env: &Env,
1288    objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1289    id: ObjectID,
1290    type_: Type,
1291    has_public_transfer: bool,
1292    contents: Vec<u8>,
1293) -> Result<MoveObject, ExecutionError> {
1294    debug_assert_eq!(
1295        id,
1296        MoveObject::id_opt(&contents).expect("object contents should start with an id")
1297    );
1298    let old_obj_ver = objects_modified_at
1299        .get(&id)
1300        .map(|obj: &LoadedRuntimeObject| obj.version);
1301
1302    let Ok(type_tag): Result<TypeTag, _> = type_.try_into() else {
1303        invariant_violation!("unable to generate type tag from type")
1304    };
1305
1306    let struct_tag = match type_tag {
1307        TypeTag::Struct(inner) => *inner,
1308        _ => invariant_violation!("Non struct type for object"),
1309    };
1310    unsafe {
1311        MoveObject::new_from_execution(
1312            struct_tag.into(),
1313            has_public_transfer,
1314            old_obj_ver.unwrap_or_default(),
1315            contents,
1316            env.protocol_config,
1317            Mode::packages_are_predefined(),
1318        )
1319    }
1320}