sui_adapter_v0/programmable_transactions/
execution.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7mod checked {
8
9    use std::{
10        collections::{BTreeMap, BTreeSet},
11        fmt,
12        sync::Arc,
13    };
14
15    use crate::execution_value::{
16        CommandKind, ExecutionState, ObjectContents, ObjectValue, RawValueType, Value,
17    };
18    use crate::gas_charger::GasCharger;
19    use crate::{execution_mode::ExecutionMode, gas_meter::SuiGasMeter};
20    use move_binary_format::{
21        compatibility::{Compatibility, InclusionCheck},
22        errors::{Location, PartialVMResult, VMResult},
23        file_format::{AbilitySet, CodeOffset, FunctionDefinitionIndex, LocalIndex, Visibility},
24        file_format_common::VERSION_6,
25        normalized, CompiledModule,
26    };
27    use move_core_types::{
28        account_address::AccountAddress,
29        identifier::{IdentStr, Identifier},
30        language_storage::{ModuleId, StructTag, TypeTag},
31        u256::U256,
32    };
33    use move_vm_runtime::{
34        move_vm::MoveVM,
35        session::{LoadedFunctionInstantiation, SerializedReturnValues},
36    };
37    use move_vm_types::loaded_data::runtime_types::{CachedDatatype, Type};
38    use serde::{de::DeserializeSeed, Deserialize};
39    use sui_move_natives::object_runtime::ObjectRuntime;
40    use sui_protocol_config::ProtocolConfig;
41    use sui_types::execution_status::{CommandArgumentError, PackageUpgradeError};
42    use sui_types::storage::{get_package_objects, PackageObject};
43    use sui_types::{
44        base_types::{
45            MoveLegacyTxContext, MoveObjectType, ObjectID, SuiAddress, TxContext, TxContextKind,
46            RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR, TX_CONTEXT_MODULE_NAME,
47            TX_CONTEXT_STRUCT_NAME,
48        },
49        coin::Coin,
50        error::{command_argument_error, ExecutionError},
51        execution_status::ExecutionErrorKind,
52        id::RESOLVED_SUI_ID,
53        metrics::LimitsMetrics,
54        move_package::{
55            normalize_deserialized_modules, MovePackage, TypeOrigin, UpgradeCap, UpgradePolicy,
56            UpgradeReceipt, UpgradeTicket,
57        },
58        transaction::{Argument, Command, ProgrammableMoveCall, ProgrammableTransaction},
59        SUI_FRAMEWORK_ADDRESS,
60    };
61    use sui_verifier::{
62        private_generics::{EVENT_MODULE, PRIVATE_TRANSFER_FUNCTIONS, TRANSFER_MODULE},
63        INIT_FN_NAME,
64    };
65
66    use crate::adapter::substitute_package_id;
67    use crate::programmable_transactions::context::*;
68
69    pub fn execute<Mode: ExecutionMode>(
70        protocol_config: &ProtocolConfig,
71        metrics: Arc<LimitsMetrics>,
72        vm: &MoveVM,
73        state_view: &mut dyn ExecutionState,
74        tx_context: &mut TxContext,
75        gas_charger: &mut GasCharger,
76        pt: ProgrammableTransaction,
77    ) -> Result<Mode::ExecutionResults, ExecutionError> {
78        let ProgrammableTransaction { inputs, commands } = pt;
79        let mut context = ExecutionContext::new(
80            protocol_config,
81            metrics,
82            vm,
83            state_view,
84            tx_context,
85            gas_charger,
86            inputs,
87        )?;
88        // execute commands
89        let mut mode_results = Mode::empty_results();
90        for (idx, command) in commands.into_iter().enumerate() {
91            if let Err(err) = execute_command::<Mode>(&mut context, &mut mode_results, command) {
92                let object_runtime: &ObjectRuntime = context.session.get_native_extensions().get();
93                // We still need to record the loaded child objects for replay
94                let loaded_child_objects = object_runtime.loaded_child_objects();
95                drop(context);
96                state_view.save_loaded_runtime_objects(loaded_child_objects);
97                return Err(err.with_command_index(idx));
98            };
99        }
100
101        // Save loaded objects table in case we fail in post execution
102        let object_runtime: &ObjectRuntime = context.session.get_native_extensions().get();
103        // We still need to record the loaded child objects for replay
104        let loaded_child_objects = object_runtime.loaded_child_objects();
105
106        // apply changes
107        let finished = context.finish::<Mode>();
108        // Save loaded objects for debug. We dont want to lose the info
109        state_view.save_loaded_runtime_objects(loaded_child_objects);
110        state_view.record_execution_results(finished?)?;
111        Ok(mode_results)
112    }
113
114    /// Execute a single command
115    fn execute_command<Mode: ExecutionMode>(
116        context: &mut ExecutionContext<'_, '_, '_>,
117        mode_results: &mut Mode::ExecutionResults,
118        command: Command,
119    ) -> Result<(), ExecutionError> {
120        let mut argument_updates = Mode::empty_arguments();
121        let results = match command {
122            Command::MakeMoveVec(tag_opt, args) if args.is_empty() => {
123                let Some(tag) = tag_opt else {
124                    invariant_violation!(
125                        "input checker ensures if args are empty, there is a type specified"
126                    );
127                };
128
129                // SAFETY: Preserving existing behaviour for identifier deserialization within type
130                // tags and inputs.
131                let tag = unsafe { tag.into_type_tag_unchecked() };
132
133                let elem_ty = context
134                    .load_type(&tag)
135                    .map_err(|e| context.convert_vm_error(e))?;
136                let ty = Type::Vector(Box::new(elem_ty));
137                let abilities = context
138                    .session
139                    .get_type_abilities(&ty)
140                    .map_err(|e| context.convert_vm_error(e))?;
141                // BCS layout for any empty vector should be the same
142                let bytes = bcs::to_bytes::<Vec<u8>>(&vec![]).unwrap();
143                vec![Value::Raw(
144                    RawValueType::Loaded {
145                        ty,
146                        abilities,
147                        used_in_non_entry_move_call: false,
148                    },
149                    bytes,
150                )]
151            }
152            Command::MakeMoveVec(tag_opt, args) => {
153                let mut res = vec![];
154                leb128::write::unsigned(&mut res, args.len() as u64).unwrap();
155                let mut arg_iter = args.into_iter().enumerate();
156                let (mut used_in_non_entry_move_call, elem_ty) = match tag_opt {
157                    Some(tag) => {
158                        // SAFETY: Preserving existing behaviour for identifier deserialization within type
159                        // tags and inputs.
160                        let tag = unsafe { tag.into_type_tag_unchecked() };
161
162                        let elem_ty = context
163                            .load_type(&tag)
164                            .map_err(|e| context.convert_vm_error(e))?;
165                        (false, elem_ty)
166                    }
167                    // If no tag specified, it _must_ be an object
168                    None => {
169                        // empty args covered above
170                        let (idx, arg) = arg_iter.next().unwrap();
171                        let obj: ObjectValue =
172                            context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
173                        obj.write_bcs_bytes(&mut res);
174                        (obj.used_in_non_entry_move_call, obj.type_)
175                    }
176                };
177                for (idx, arg) in arg_iter {
178                    let value: Value = context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
179                    check_param_type::<Mode>(context, idx, &value, &elem_ty)?;
180                    used_in_non_entry_move_call =
181                        used_in_non_entry_move_call || value.was_used_in_non_entry_move_call();
182                    value.write_bcs_bytes(&mut res);
183                }
184                let ty = Type::Vector(Box::new(elem_ty));
185                let abilities = context
186                    .session
187                    .get_type_abilities(&ty)
188                    .map_err(|e| context.convert_vm_error(e))?;
189                vec![Value::Raw(
190                    RawValueType::Loaded {
191                        ty,
192                        abilities,
193                        used_in_non_entry_move_call,
194                    },
195                    res,
196                )]
197            }
198            Command::TransferObjects(objs, addr_arg) => {
199                let objs: Vec<ObjectValue> = objs
200                    .into_iter()
201                    .enumerate()
202                    .map(|(idx, arg)| context.by_value_arg(CommandKind::TransferObjects, idx, arg))
203                    .collect::<Result<_, _>>()?;
204                let addr: SuiAddress =
205                    context.by_value_arg(CommandKind::TransferObjects, objs.len(), addr_arg)?;
206                for obj in objs {
207                    obj.ensure_public_transfer_eligible()?;
208                    context.transfer_object(obj, addr)?;
209                }
210                vec![]
211            }
212            Command::SplitCoins(coin_arg, amount_args) => {
213                let mut obj: ObjectValue = context.borrow_arg_mut(0, coin_arg)?;
214                let ObjectContents::Coin(coin) = &mut obj.contents else {
215                    let e = ExecutionErrorKind::command_argument_error(
216                        CommandArgumentError::TypeMismatch,
217                        0,
218                    );
219                    let msg = "Expected a coin but got an non coin object".to_owned();
220                    return Err(ExecutionError::new_with_source(e, msg));
221                };
222                let split_coins = amount_args
223                    .into_iter()
224                    .map(|amount_arg| {
225                        let amount: u64 =
226                            context.by_value_arg(CommandKind::SplitCoins, 1, amount_arg)?;
227                        let new_coin_id = context.fresh_id()?;
228                        let new_coin = coin.split(amount, new_coin_id)?;
229                        let coin_type = obj.type_.clone();
230                        // safe because we are propagating the coin type, and relying on the internal
231                        // invariant that coin values have a coin type
232                        let new_coin = unsafe { ObjectValue::coin(coin_type, new_coin) };
233                        Ok(Value::Object(new_coin))
234                    })
235                    .collect::<Result<_, ExecutionError>>()?;
236                context.restore_arg::<Mode>(&mut argument_updates, coin_arg, Value::Object(obj))?;
237                split_coins
238            }
239            Command::MergeCoins(target_arg, coin_args) => {
240                let mut target: ObjectValue = context.borrow_arg_mut(0, target_arg)?;
241                let ObjectContents::Coin(target_coin) = &mut target.contents else {
242                    let e = ExecutionErrorKind::command_argument_error(
243                        CommandArgumentError::TypeMismatch,
244                        0,
245                    );
246                    let msg = "Expected a coin but got an non coin object".to_owned();
247                    return Err(ExecutionError::new_with_source(e, msg));
248                };
249                let coins: Vec<ObjectValue> = coin_args
250                    .into_iter()
251                    .enumerate()
252                    .map(|(idx, arg)| context.by_value_arg(CommandKind::MergeCoins, idx + 1, arg))
253                    .collect::<Result<_, _>>()?;
254                for (idx, coin) in coins.into_iter().enumerate() {
255                    if target.type_ != coin.type_ {
256                        let e = ExecutionErrorKind::command_argument_error(
257                            CommandArgumentError::TypeMismatch,
258                            (idx + 1) as u16,
259                        );
260                        let msg = "Coins do not have the same type".to_owned();
261                        return Err(ExecutionError::new_with_source(e, msg));
262                    }
263                    let ObjectContents::Coin(Coin { id, balance }) = coin.contents else {
264                        invariant_violation!(
265                            "Target coin was a coin, and we already checked for the same type. \
266                        This should be a coin"
267                        );
268                    };
269                    context.delete_id(*id.object_id())?;
270                    target_coin.add(balance)?;
271                }
272                context.restore_arg::<Mode>(
273                    &mut argument_updates,
274                    target_arg,
275                    Value::Object(target),
276                )?;
277                vec![]
278            }
279            Command::MoveCall(move_call) => {
280                let ProgrammableMoveCall {
281                    package,
282                    module,
283                    function,
284                    type_arguments,
285                    arguments,
286                } = *move_call;
287
288                // SAFETY: Preserving existing behaviour for identifier deserialization.
289                let module = unsafe { Identifier::new_unchecked(module) };
290                let function = unsafe { Identifier::new_unchecked(function) };
291
292                // Convert type arguments to `Type`s
293                let mut loaded_type_arguments = Vec::with_capacity(type_arguments.len());
294                for (ix, type_arg) in type_arguments.into_iter().enumerate() {
295                    // SAFETY: Preserving existing behaviour for identifier deserialization within type
296                    // tags and inputs.
297                    let type_arg = unsafe { type_arg.into_type_tag_unchecked() };
298
299                    let ty = context
300                        .load_type(&type_arg)
301                        .map_err(|e| context.convert_type_argument_error(ix, e))?;
302                    loaded_type_arguments.push(ty);
303                }
304
305                let original_address = context.set_link_context(package)?;
306                let runtime_id = ModuleId::new(original_address, module);
307                let return_values = execute_move_call::<Mode>(
308                    context,
309                    &mut argument_updates,
310                    &runtime_id,
311                    &function,
312                    loaded_type_arguments,
313                    arguments,
314                    /* is_init */ false,
315                );
316
317                context.reset_linkage();
318                return_values?
319            }
320            Command::Publish(modules, dep_ids) => {
321                execute_move_publish::<Mode>(context, &mut argument_updates, modules, dep_ids)?
322            }
323            Command::Upgrade(modules, dep_ids, current_package_id, upgrade_ticket) => {
324                execute_move_upgrade::<Mode>(
325                    context,
326                    modules,
327                    dep_ids,
328                    current_package_id,
329                    upgrade_ticket,
330                )?
331            }
332        };
333
334        Mode::finish_command(context, mode_results, argument_updates, &results)?;
335        context.push_command_results(results)?;
336        Ok(())
337    }
338
339    /// Execute a single Move call
340    fn execute_move_call<Mode: ExecutionMode>(
341        context: &mut ExecutionContext<'_, '_, '_>,
342        argument_updates: &mut Mode::ArgumentUpdates,
343        module_id: &ModuleId,
344        function: &IdentStr,
345        type_arguments: Vec<Type>,
346        arguments: Vec<Argument>,
347        is_init: bool,
348    ) -> Result<Vec<Value>, ExecutionError> {
349        // check that the function is either an entry function or a valid public function
350        let LoadedFunctionInfo {
351            kind,
352            signature,
353            return_value_kinds,
354            index,
355            last_instr,
356        } = check_visibility_and_signature::<Mode>(
357            context,
358            module_id,
359            function,
360            &type_arguments,
361            is_init,
362        )?;
363        // build the arguments, storing meta data about by-mut-ref args
364        let (tx_context_kind, by_mut_ref, serialized_arguments) =
365            build_move_args::<Mode>(context, module_id, function, kind, &signature, &arguments)?;
366        // invoke the VM
367        let SerializedReturnValues {
368            mutable_reference_outputs,
369            return_values,
370        } = vm_move_call(
371            context,
372            module_id,
373            function,
374            type_arguments,
375            tx_context_kind,
376            serialized_arguments,
377        )?;
378        assert_invariant!(
379            by_mut_ref.len() == mutable_reference_outputs.len(),
380            "lost mutable input"
381        );
382
383        context.take_user_events(module_id, index, last_instr)?;
384
385        // save the link context because calls to `make_value` below can set new ones, and we don't want
386        // it to be clobbered.
387        let saved_linkage = context.steal_linkage();
388        // write back mutable inputs. We also update if they were used in non entry Move calls
389        // though we do not care for immutable usages of objects or other values
390        let used_in_non_entry_move_call = kind == FunctionKind::NonEntry;
391        let res = write_back_results::<Mode>(
392            context,
393            argument_updates,
394            &arguments,
395            used_in_non_entry_move_call,
396            mutable_reference_outputs
397                .into_iter()
398                .map(|(i, bytes, _layout)| (i, bytes)),
399            by_mut_ref,
400            return_values.into_iter().map(|(bytes, _layout)| bytes),
401            return_value_kinds,
402        );
403
404        context.restore_linkage(saved_linkage)?;
405        res
406    }
407
408    fn write_back_results<Mode: ExecutionMode>(
409        context: &mut ExecutionContext<'_, '_, '_>,
410        argument_updates: &mut Mode::ArgumentUpdates,
411        arguments: &[Argument],
412        non_entry_move_call: bool,
413        mut_ref_values: impl IntoIterator<Item = (u8, Vec<u8>)>,
414        mut_ref_kinds: impl IntoIterator<Item = (u8, ValueKind)>,
415        return_values: impl IntoIterator<Item = Vec<u8>>,
416        return_value_kinds: impl IntoIterator<Item = ValueKind>,
417    ) -> Result<Vec<Value>, ExecutionError> {
418        for ((i, bytes), (j, kind)) in mut_ref_values.into_iter().zip(mut_ref_kinds) {
419            assert_invariant!(i == j, "lost mutable input");
420            let arg_idx = i as usize;
421            let value = make_value(context, kind, bytes, non_entry_move_call)?;
422            context.restore_arg::<Mode>(argument_updates, arguments[arg_idx], value)?;
423        }
424
425        return_values
426            .into_iter()
427            .zip(return_value_kinds)
428            .map(|(bytes, kind)| {
429                // only non entry functions have return values
430                make_value(
431                    context, kind, bytes, /* used_in_non_entry_move_call */ true,
432                )
433            })
434            .collect()
435    }
436
437    fn make_value(
438        context: &mut ExecutionContext<'_, '_, '_>,
439        value_info: ValueKind,
440        bytes: Vec<u8>,
441        used_in_non_entry_move_call: bool,
442    ) -> Result<Value, ExecutionError> {
443        Ok(match value_info {
444            ValueKind::Object {
445                type_,
446                has_public_transfer,
447            } => Value::Object(make_object_value(
448                context.vm,
449                &mut context.session,
450                type_,
451                has_public_transfer,
452                used_in_non_entry_move_call,
453                &bytes,
454            )?),
455            ValueKind::Raw(ty, abilities) => Value::Raw(
456                RawValueType::Loaded {
457                    ty,
458                    abilities,
459                    used_in_non_entry_move_call,
460                },
461                bytes,
462            ),
463        })
464    }
465
466    /// Publish Move modules and call the init functions.  Returns an `UpgradeCap` for the newly
467    /// published package on success.
468    fn execute_move_publish<Mode: ExecutionMode>(
469        context: &mut ExecutionContext<'_, '_, '_>,
470        argument_updates: &mut Mode::ArgumentUpdates,
471        module_bytes: Vec<Vec<u8>>,
472        dep_ids: Vec<ObjectID>,
473    ) -> Result<Vec<Value>, ExecutionError> {
474        assert_invariant!(
475            !module_bytes.is_empty(),
476            "empty package is checked in transaction input checker"
477        );
478        context
479            .gas_charger
480            .charge_publish_package(module_bytes.iter().map(|v| v.len()).sum())?;
481
482        let mut modules = deserialize_modules::<Mode>(context, &module_bytes)?;
483
484        // It should be fine that this does not go through ExecutionContext::fresh_id since the Move
485        // runtime does not to know about new packages created, since Move objects and Move packages
486        // cannot interact
487        let runtime_id = if Mode::packages_are_predefined() {
488            // do not calculate or substitute id for predefined packages
489            (*modules[0].self_id().address()).into()
490        } else {
491            let id = context.tx_context.fresh_id();
492            substitute_package_id(&mut modules, id)?;
493            id
494        };
495
496        // For newly published packages, runtime ID matches storage ID.
497        let storage_id = runtime_id;
498
499        // Preserve the old order of operations when package upgrades are not supported, because it
500        // affects the order in which error cases are checked.
501        let package_obj = if context.protocol_config.package_upgrades_supported() {
502            let dependencies = fetch_packages(context, &dep_ids)?;
503            let package_obj =
504                context.new_package(&modules, dependencies.iter().map(|p| p.move_package()))?;
505
506            let Some(package) = package_obj.data.try_as_package() else {
507                invariant_violation!("Newly created package object is not a package");
508            };
509
510            context.set_linkage(package)?;
511            let res = publish_and_verify_modules(context, runtime_id, &modules)
512                .and_then(|_| init_modules::<Mode>(context, argument_updates, &modules));
513            context.reset_linkage();
514            res?;
515
516            package_obj
517        } else {
518            // FOR THE LOVE OF ALL THAT IS GOOD DO NOT RE-ORDER THIS.  It looks redundant, but is
519            // required to maintain backwards compatibility.
520            publish_and_verify_modules(context, runtime_id, &modules)?;
521            let dependencies = fetch_packages(context, &dep_ids)?;
522            let package =
523                context.new_package(&modules, dependencies.iter().map(|p| p.move_package()))?;
524            init_modules::<Mode>(context, argument_updates, &modules)?;
525            package
526        };
527
528        context.write_package(package_obj)?;
529        let values = if Mode::packages_are_predefined() {
530            // no upgrade cap for genesis modules
531            vec![]
532        } else {
533            let cap = &UpgradeCap::new(context.fresh_id()?, storage_id);
534            vec![Value::Object(make_object_value(
535                context.vm,
536                &mut context.session,
537                UpgradeCap::type_().into(),
538                /* has_public_transfer */ true,
539                /* used_in_non_entry_move_call */ false,
540                &bcs::to_bytes(cap).unwrap(),
541            )?)]
542        };
543        Ok(values)
544    }
545
546    /// Upgrade a Move package.  Returns an `UpgradeReceipt` for the upgraded package on success.
547    fn execute_move_upgrade<Mode: ExecutionMode>(
548        context: &mut ExecutionContext<'_, '_, '_>,
549        module_bytes: Vec<Vec<u8>>,
550        dep_ids: Vec<ObjectID>,
551        current_package_id: ObjectID,
552        upgrade_ticket_arg: Argument,
553    ) -> Result<Vec<Value>, ExecutionError> {
554        // Check that package upgrades are supported.
555        context
556            .protocol_config
557            .check_package_upgrades_supported()
558            .map_err(|_| ExecutionError::from_kind(ExecutionErrorKind::FeatureNotYetSupported))?;
559
560        assert_invariant!(
561            !module_bytes.is_empty(),
562            "empty package is checked in transaction input checker"
563        );
564
565        let upgrade_ticket_type = context
566            .load_type(&TypeTag::Struct(Box::new(UpgradeTicket::type_())))
567            .map_err(|e| context.convert_vm_error(e))?;
568        let upgrade_receipt_type = context
569            .load_type(&TypeTag::Struct(Box::new(UpgradeReceipt::type_())))
570            .map_err(|e| context.convert_vm_error(e))?;
571
572        let upgrade_ticket: UpgradeTicket = {
573            let mut ticket_bytes = Vec::new();
574            let ticket_val: Value =
575                context.by_value_arg(CommandKind::Upgrade, 0, upgrade_ticket_arg)?;
576            check_param_type::<Mode>(context, 0, &ticket_val, &upgrade_ticket_type)?;
577            ticket_val.write_bcs_bytes(&mut ticket_bytes);
578            bcs::from_bytes(&ticket_bytes).map_err(|_| {
579                ExecutionError::from_kind(ExecutionErrorKind::CommandArgumentError {
580                    arg_idx: 0,
581                    kind: CommandArgumentError::InvalidBCSBytes,
582                })
583            })?
584        };
585
586        // Make sure the passed-in package ID matches the package ID in the `upgrade_ticket`.
587        if current_package_id != upgrade_ticket.package.bytes {
588            return Err(ExecutionError::from_kind(
589                ExecutionErrorKind::PackageUpgradeError {
590                    upgrade_error: PackageUpgradeError::PackageIDDoesNotMatch {
591                        package_id: current_package_id,
592                        ticket_id: upgrade_ticket.package.bytes,
593                    },
594                },
595            ));
596        }
597
598        // Check digest.
599        let computed_digest = MovePackage::compute_digest_for_modules_and_deps(
600            &module_bytes,
601            &dep_ids,
602            context.protocol_config.package_digest_hash_module(),
603        )
604        .to_vec();
605        if computed_digest != upgrade_ticket.digest {
606            return Err(ExecutionError::from_kind(
607                ExecutionErrorKind::PackageUpgradeError {
608                    upgrade_error: PackageUpgradeError::DigestDoesNotMatch {
609                        digest: computed_digest,
610                    },
611                },
612            ));
613        }
614
615        // Check that this package ID points to a package and get the package we're upgrading.
616        let current_package = fetch_package(context, &upgrade_ticket.package.bytes)?;
617
618        let mut modules = deserialize_modules::<Mode>(context, &module_bytes)?;
619        let runtime_id = current_package.move_package().original_package_id();
620        substitute_package_id(&mut modules, runtime_id)?;
621
622        // Upgraded packages share their predecessor's runtime ID but get a new storage ID.
623        let storage_id = context.tx_context.fresh_id();
624
625        let dependencies = fetch_packages(context, &dep_ids)?;
626        let package_obj = context.upgrade_package(
627            storage_id,
628            current_package.move_package(),
629            &modules,
630            dependencies.iter().map(|p| p.move_package()),
631        )?;
632
633        let Some(package) = package_obj.data.try_as_package() else {
634            invariant_violation!("Newly created package object is not a package");
635        };
636
637        // Populate loader with all previous types.
638        if !context
639            .protocol_config
640            .disallow_adding_abilities_on_upgrade()
641        {
642            for TypeOrigin {
643                module_name,
644                datatype_name,
645                package: origin,
646            } in package.type_origin_table()
647            {
648                if package.id() == *origin {
649                    continue;
650                }
651
652                let Ok(module) = Identifier::new(module_name.as_str()) else {
653                    continue;
654                };
655
656                let Ok(name) = Identifier::new(datatype_name.as_str()) else {
657                    continue;
658                };
659
660                let _ = context.load_type(&TypeTag::Struct(Box::new(StructTag {
661                    address: (*origin).into(),
662                    module,
663                    name,
664                    type_params: vec![],
665                })));
666            }
667        }
668
669        context.set_linkage(package)?;
670        let res = publish_and_verify_modules(context, runtime_id, &modules);
671        context.reset_linkage();
672        res?;
673
674        check_compatibility(
675            context,
676            current_package.move_package(),
677            &modules,
678            upgrade_ticket.policy,
679        )?;
680
681        context.write_package(package_obj)?;
682        Ok(vec![Value::Raw(
683            RawValueType::Loaded {
684                ty: upgrade_receipt_type,
685                abilities: AbilitySet::EMPTY,
686                used_in_non_entry_move_call: false,
687            },
688            bcs::to_bytes(&UpgradeReceipt::new(upgrade_ticket, storage_id)).unwrap(),
689        )])
690    }
691
692    fn check_compatibility<'a>(
693        context: &ExecutionContext,
694        existing_package: &MovePackage,
695        upgrading_modules: impl IntoIterator<Item = &'a CompiledModule>,
696        policy: u8,
697    ) -> Result<(), ExecutionError> {
698        // Make sure this is a known upgrade policy.
699        let Ok(policy) = UpgradePolicy::try_from(policy) else {
700            return Err(ExecutionError::from_kind(
701                ExecutionErrorKind::PackageUpgradeError {
702                    upgrade_error: PackageUpgradeError::UnknownUpgradePolicy { policy },
703                },
704            ));
705        };
706
707        let binary_config = context.protocol_config.binary_config(None);
708        let pool = &mut normalized::RcPool::new();
709        let Ok(current_normalized) =
710            existing_package.normalize(pool, &binary_config, /* include code */ true)
711        else {
712            invariant_violation!("Tried to normalize modules in existing package but failed")
713        };
714
715        let mut new_normalized = normalize_deserialized_modules(
716            pool,
717            upgrading_modules.into_iter(),
718            /* include code */ true,
719        );
720        for (name, cur_module) in current_normalized {
721            let Some(new_module) = new_normalized.remove(&name) else {
722                return Err(ExecutionError::new_with_source(
723                    ExecutionErrorKind::PackageUpgradeError {
724                        upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
725                    },
726                    format!("Existing module {name} not found in next version of package"),
727                ));
728            };
729
730            check_module_compatibility(&policy, context.protocol_config, &cur_module, &new_module)?;
731        }
732
733        Ok(())
734    }
735
736    fn check_module_compatibility(
737        policy: &UpgradePolicy,
738        protocol_config: &ProtocolConfig,
739        cur_module: &move_binary_format::compatibility::Module,
740        new_module: &move_binary_format::compatibility::Module,
741    ) -> Result<(), ExecutionError> {
742        match policy {
743            UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module),
744            UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module),
745            UpgradePolicy::Compatible => {
746                let disallowed_new_abilities =
747                    if protocol_config.disallow_adding_abilities_on_upgrade() {
748                        AbilitySet::ALL
749                    } else {
750                        AbilitySet::EMPTY
751                    };
752
753                let compatibility = Compatibility {
754                    check_datatype_layout: true,
755                    check_private_entry_linking: false,
756                    disallowed_new_abilities,
757                };
758
759                compatibility.check(cur_module, new_module)
760            }
761        }
762        .map_err(|e| {
763            ExecutionError::new_with_source(
764                ExecutionErrorKind::PackageUpgradeError {
765                    upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
766                },
767                e,
768            )
769        })
770    }
771
772    fn fetch_package(
773        context: &ExecutionContext<'_, '_, '_>,
774        package_id: &ObjectID,
775    ) -> Result<PackageObject, ExecutionError> {
776        let mut fetched_packages = fetch_packages(context, vec![package_id])?;
777        assert_invariant!(
778            fetched_packages.len() == 1,
779            "Number of fetched packages must match the number of package object IDs if successful."
780        );
781        match fetched_packages.pop() {
782            Some(pkg) => Ok(pkg),
783            None => invariant_violation!(
784                "We should always fetch a package for each object or return a dependency error."
785            ),
786        }
787    }
788
789    fn fetch_packages<'ctx, 'vm, 'state, 'a>(
790        context: &'ctx ExecutionContext<'vm, 'state, 'a>,
791        package_ids: impl IntoIterator<Item = &'ctx ObjectID>,
792    ) -> Result<Vec<PackageObject>, ExecutionError> {
793        let package_ids: BTreeSet<_> = package_ids.into_iter().collect();
794        match get_package_objects(&context.state_view, package_ids) {
795            Err(e) => Err(ExecutionError::new_with_source(
796                ExecutionErrorKind::PublishUpgradeMissingDependency,
797                e,
798            )),
799            Ok(Err(missing_deps)) => {
800                let msg = format!(
801                    "Missing dependencies: {}",
802                    missing_deps
803                        .into_iter()
804                        .map(|dep| format!("{}", dep))
805                        .collect::<Vec<_>>()
806                        .join(", ")
807                );
808                Err(ExecutionError::new_with_source(
809                    ExecutionErrorKind::PublishUpgradeMissingDependency,
810                    msg,
811                ))
812            }
813            Ok(Ok(pkgs)) => Ok(pkgs),
814        }
815    }
816
817    /***************************************************************************************************
818     * Move execution
819     **************************************************************************************************/
820
821    fn vm_move_call(
822        context: &mut ExecutionContext<'_, '_, '_>,
823        module_id: &ModuleId,
824        function: &IdentStr,
825        type_arguments: Vec<Type>,
826        tx_context_kind: TxContextKind,
827        mut serialized_arguments: Vec<Vec<u8>>,
828    ) -> Result<SerializedReturnValues, ExecutionError> {
829        match tx_context_kind {
830            TxContextKind::None => (),
831            TxContextKind::Mutable | TxContextKind::Immutable => {
832                serialized_arguments.push(context.tx_context.to_bcs_legacy_context());
833            }
834        }
835        // script visibility checked manually for entry points
836        let mut result = context
837            .session
838            .execute_function_bypass_visibility(
839                module_id,
840                function,
841                type_arguments,
842                serialized_arguments,
843                &mut SuiGasMeter(context.gas_charger.move_gas_status_mut()),
844            )
845            .map_err(|e| context.convert_vm_error(e))?;
846
847        // When this function is used during publishing, it
848        // may be executed several times, with objects being
849        // created in the Move VM in each Move call. In such
850        // case, we need to update TxContext value so that it
851        // reflects what happened each time we call into the
852        // Move VM (e.g. to account for the number of created
853        // objects).
854        if tx_context_kind == TxContextKind::Mutable {
855            let Some((_, ctx_bytes, _)) = result.mutable_reference_outputs.pop() else {
856                invariant_violation!("Missing TxContext in reference outputs");
857            };
858            let updated_ctx: MoveLegacyTxContext = bcs::from_bytes(&ctx_bytes).map_err(|e| {
859                ExecutionError::invariant_violation(format!(
860                    "Unable to deserialize TxContext bytes. {e}"
861                ))
862            })?;
863            context.tx_context.update_state(updated_ctx)?;
864        }
865        Ok(result)
866    }
867
868    #[allow(clippy::extra_unused_type_parameters)]
869    fn deserialize_modules<Mode: ExecutionMode>(
870        context: &mut ExecutionContext<'_, '_, '_>,
871        module_bytes: &[Vec<u8>],
872    ) -> Result<Vec<CompiledModule>, ExecutionError> {
873        let binary_config = context.protocol_config.binary_config(None);
874        let modules = module_bytes
875            .iter()
876            .map(|b| {
877                CompiledModule::deserialize_with_config(b, &binary_config)
878                    .map_err(|e| e.finish(Location::Undefined))
879            })
880            .collect::<VMResult<Vec<CompiledModule>>>()
881            .map_err(|e| context.convert_vm_error(e))?;
882
883        assert_invariant!(
884            !modules.is_empty(),
885            "input checker ensures package is not empty"
886        );
887
888        Ok(modules)
889    }
890
891    fn publish_and_verify_modules(
892        context: &mut ExecutionContext<'_, '_, '_>,
893        package_id: ObjectID,
894        modules: &[CompiledModule],
895    ) -> Result<(), ExecutionError> {
896        // 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>`
897        let new_module_bytes: Vec<_> = modules
898            .iter()
899            .map(|m| {
900                let mut bytes = Vec::new();
901                m.serialize_with_version(VERSION_6, &mut bytes).unwrap();
902                bytes
903            })
904            .collect();
905        context
906            .session
907            .publish_module_bundle(
908                new_module_bytes,
909                AccountAddress::from(package_id),
910                // TODO: publish_module_bundle() currently doesn't charge gas.
911                // Do we want to charge there?
912                &mut SuiGasMeter(context.gas_charger.move_gas_status_mut()),
913            )
914            .map_err(|e| context.convert_vm_error(e))?;
915
916        // run the Sui verifier
917        for module in modules {
918            // Run Sui bytecode verifier, which runs some additional checks that assume the Move
919            // bytecode verifier has passed.
920            sui_verifier::verifier::sui_verify_module_unmetered(
921                context.protocol_config,
922                module,
923                &BTreeMap::new(),
924            )?;
925        }
926
927        Ok(())
928    }
929
930    fn init_modules<Mode: ExecutionMode>(
931        context: &mut ExecutionContext<'_, '_, '_>,
932        argument_updates: &mut Mode::ArgumentUpdates,
933        modules: &[CompiledModule],
934    ) -> Result<(), ExecutionError> {
935        let modules_to_init = modules.iter().filter_map(|module| {
936            for fdef in &module.function_defs {
937                let fhandle = module.function_handle_at(fdef.function);
938                let fname = module.identifier_at(fhandle.name);
939                if fname == INIT_FN_NAME {
940                    return Some(module.self_id());
941                }
942            }
943            None
944        });
945
946        for module_id in modules_to_init {
947            let return_values = execute_move_call::<Mode>(
948                context,
949                argument_updates,
950                &module_id,
951                INIT_FN_NAME,
952                vec![],
953                vec![],
954                /* is_init */ true,
955            )?;
956
957            assert_invariant!(
958                return_values.is_empty(),
959                "init should not have return values"
960            )
961        }
962
963        Ok(())
964    }
965
966    /***************************************************************************************************
967     * Move signatures
968     **************************************************************************************************/
969
970    /// Helper marking what function we are invoking
971    #[derive(PartialEq, Eq, Clone, Copy)]
972    enum FunctionKind {
973        PrivateEntry,
974        PublicEntry,
975        NonEntry,
976        Init,
977    }
978
979    /// Used to remember type information about a type when resolving the signature
980    enum ValueKind {
981        Object {
982            type_: MoveObjectType,
983            has_public_transfer: bool,
984        },
985        Raw(Type, AbilitySet),
986    }
987
988    struct LoadedFunctionInfo {
989        /// The kind of the function, e.g. public or private or init
990        kind: FunctionKind,
991        /// The signature information of the function
992        signature: LoadedFunctionInstantiation,
993        /// Object or type information for the return values
994        return_value_kinds: Vec<ValueKind>,
995        /// Definition index of the function
996        index: FunctionDefinitionIndex,
997        /// The length of the function used for setting error information, or 0 if native
998        last_instr: CodeOffset,
999    }
1000
1001    /// Checks that the function to be called is either
1002    /// - an entry function
1003    /// - a public function that does not return references
1004    /// - module init (only internal usage)
1005    fn check_visibility_and_signature<Mode: ExecutionMode>(
1006        context: &mut ExecutionContext<'_, '_, '_>,
1007        module_id: &ModuleId,
1008        function: &IdentStr,
1009        type_arguments: &[Type],
1010        from_init: bool,
1011    ) -> Result<LoadedFunctionInfo, ExecutionError> {
1012        if from_init {
1013            // the session is weird and does not load the module on publishing. This is a temporary
1014            // work around, since loading the function through the session will cause the module
1015            // to be loaded through the sessions data store.
1016            let result = context
1017                .session
1018                .load_function(module_id, function, type_arguments);
1019            assert_invariant!(
1020                result.is_ok(),
1021                "The modules init should be able to be loaded"
1022            );
1023        }
1024        let module = context
1025            .vm
1026            .load_module(module_id, context.session.get_resolver())
1027            .map_err(|e| context.convert_vm_error(e))?;
1028        let Some((index, fdef)) = module
1029            .function_defs
1030            .iter()
1031            .enumerate()
1032            .find(|(_index, fdef)| {
1033                module.identifier_at(module.function_handle_at(fdef.function).name) == function
1034            })
1035        else {
1036            return Err(ExecutionError::new_with_source(
1037                ExecutionErrorKind::FunctionNotFound,
1038                format!(
1039                    "Could not resolve function '{}' in module {}",
1040                    function, &module_id,
1041                ),
1042            ));
1043        };
1044
1045        // entry on init is now banned, so ban invoking it
1046        if !from_init && function == INIT_FN_NAME && context.protocol_config.ban_entry_init() {
1047            return Err(ExecutionError::new_with_source(
1048                ExecutionErrorKind::NonEntryFunctionInvoked,
1049                "Cannot call 'init'",
1050            ));
1051        }
1052
1053        let last_instr: CodeOffset = fdef
1054            .code
1055            .as_ref()
1056            .map(|code| code.code.len() - 1)
1057            .unwrap_or(0) as CodeOffset;
1058        let function_kind = match (fdef.visibility, fdef.is_entry) {
1059            (Visibility::Private | Visibility::Friend, true) => FunctionKind::PrivateEntry,
1060            (Visibility::Public, true) => FunctionKind::PublicEntry,
1061            (Visibility::Public, false) => FunctionKind::NonEntry,
1062            (Visibility::Private, false) if from_init => {
1063                assert_invariant!(
1064                    function == INIT_FN_NAME,
1065                    "module init specified non-init function"
1066                );
1067                FunctionKind::Init
1068            }
1069            (Visibility::Private | Visibility::Friend, false)
1070                if Mode::allow_arbitrary_function_calls() =>
1071            {
1072                FunctionKind::NonEntry
1073            }
1074            (Visibility::Private | Visibility::Friend, false) => {
1075                return Err(ExecutionError::new_with_source(
1076                    ExecutionErrorKind::NonEntryFunctionInvoked,
1077                    "Can only call `entry` or `public` functions",
1078                ));
1079            }
1080        };
1081        let signature = context
1082            .session
1083            .load_function(module_id, function, type_arguments)
1084            .map_err(|e| context.convert_vm_error(e))?;
1085        let signature =
1086            subst_signature(signature, type_arguments).map_err(|e| context.convert_vm_error(e))?;
1087        let return_value_kinds = match function_kind {
1088            FunctionKind::Init => {
1089                assert_invariant!(
1090                    signature.return_.is_empty(),
1091                    "init functions must have no return values"
1092                );
1093                vec![]
1094            }
1095            FunctionKind::PrivateEntry | FunctionKind::PublicEntry | FunctionKind::NonEntry => {
1096                check_non_entry_signature::<Mode>(context, module_id, function, &signature)?
1097            }
1098        };
1099        check_private_generics(context, module_id, function, type_arguments)?;
1100        Ok(LoadedFunctionInfo {
1101            kind: function_kind,
1102            signature,
1103            return_value_kinds,
1104            index: FunctionDefinitionIndex(index as u16),
1105            last_instr,
1106        })
1107    }
1108
1109    /// substitutes the type arguments into the parameter and return types
1110    fn subst_signature(
1111        signature: LoadedFunctionInstantiation,
1112        type_arguments: &[Type],
1113    ) -> VMResult<LoadedFunctionInstantiation> {
1114        let LoadedFunctionInstantiation {
1115            parameters,
1116            return_,
1117        } = signature;
1118        let parameters = parameters
1119            .into_iter()
1120            .map(|ty| ty.subst(type_arguments))
1121            .collect::<PartialVMResult<Vec<_>>>()
1122            .map_err(|err| err.finish(Location::Undefined))?;
1123        let return_ = return_
1124            .into_iter()
1125            .map(|ty| ty.subst(type_arguments))
1126            .collect::<PartialVMResult<Vec<_>>>()
1127            .map_err(|err| err.finish(Location::Undefined))?;
1128        Ok(LoadedFunctionInstantiation {
1129            parameters,
1130            return_,
1131        })
1132    }
1133
1134    /// Checks that the non-entry function does not return references. And marks the return values
1135    /// as object or non-object return values
1136    fn check_non_entry_signature<Mode: ExecutionMode>(
1137        context: &mut ExecutionContext<'_, '_, '_>,
1138        _module_id: &ModuleId,
1139        _function: &IdentStr,
1140        signature: &LoadedFunctionInstantiation,
1141    ) -> Result<Vec<ValueKind>, ExecutionError> {
1142        signature
1143            .return_
1144            .iter()
1145            .enumerate()
1146            .map(|(idx, return_type)| {
1147                let return_type = match return_type {
1148                    // for dev-inspect, just dereference the value
1149                    Type::Reference(inner) | Type::MutableReference(inner)
1150                        if Mode::allow_arbitrary_values() =>
1151                    {
1152                        inner
1153                    }
1154                    Type::Reference(_) | Type::MutableReference(_) => {
1155                        return Err(ExecutionError::from_kind(
1156                            ExecutionErrorKind::InvalidPublicFunctionReturnType { idx: idx as u16 },
1157                        ))
1158                    }
1159                    t => t,
1160                };
1161                let abilities = context
1162                    .session
1163                    .get_type_abilities(return_type)
1164                    .map_err(|e| context.convert_vm_error(e))?;
1165                Ok(match return_type {
1166                    Type::MutableReference(_) | Type::Reference(_) => unreachable!(),
1167                    Type::TyParam(_) => {
1168                        invariant_violation!("TyParam should have been substituted")
1169                    }
1170                    Type::Datatype(_) | Type::DatatypeInstantiation(_) if abilities.has_key() => {
1171                        let type_tag = context
1172                            .session
1173                            .get_type_tag(return_type)
1174                            .map_err(|e| context.convert_vm_error(e))?;
1175                        let TypeTag::Struct(struct_tag) = type_tag else {
1176                            invariant_violation!("Struct type make a non struct type tag")
1177                        };
1178                        ValueKind::Object {
1179                            type_: MoveObjectType::from(*struct_tag),
1180                            has_public_transfer: abilities.has_store(),
1181                        }
1182                    }
1183                    Type::Datatype(_)
1184                    | Type::DatatypeInstantiation(_)
1185                    | Type::Bool
1186                    | Type::U8
1187                    | Type::U64
1188                    | Type::U128
1189                    | Type::Address
1190                    | Type::Signer
1191                    | Type::Vector(_)
1192                    | Type::U16
1193                    | Type::U32
1194                    | Type::U256 => ValueKind::Raw(return_type.clone(), abilities),
1195                })
1196            })
1197            .collect()
1198    }
1199
1200    fn check_private_generics(
1201        _context: &mut ExecutionContext,
1202        module_id: &ModuleId,
1203        function: &IdentStr,
1204        _type_arguments: &[Type],
1205    ) -> Result<(), ExecutionError> {
1206        let module_ident = (module_id.address(), module_id.name());
1207        if module_ident == (&SUI_FRAMEWORK_ADDRESS, EVENT_MODULE) {
1208            return Err(ExecutionError::new_with_source(
1209                ExecutionErrorKind::NonEntryFunctionInvoked,
1210                format!("Cannot directly call functions in sui::{}", EVENT_MODULE),
1211            ));
1212        }
1213
1214        if module_ident == (&SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE)
1215            && PRIVATE_TRANSFER_FUNCTIONS.contains(&function)
1216        {
1217            let msg = format!(
1218                "Cannot directly call sui::{m}::{f}. \
1219            Use the public variant instead, sui::{m}::public_{f}",
1220                m = TRANSFER_MODULE,
1221                f = function
1222            );
1223            return Err(ExecutionError::new_with_source(
1224                ExecutionErrorKind::NonEntryFunctionInvoked,
1225                msg,
1226            ));
1227        }
1228
1229        Ok(())
1230    }
1231
1232    type ArgInfo = (
1233        TxContextKind,
1234        /* mut ref */
1235        Vec<(LocalIndex, ValueKind)>,
1236        Vec<Vec<u8>>,
1237    );
1238
1239    /// Serializes the arguments into BCS values for Move. Performs the necessary type checking for
1240    /// each value
1241    fn build_move_args<Mode: ExecutionMode>(
1242        context: &mut ExecutionContext<'_, '_, '_>,
1243        module_id: &ModuleId,
1244        function: &IdentStr,
1245        function_kind: FunctionKind,
1246        signature: &LoadedFunctionInstantiation,
1247        args: &[Argument],
1248    ) -> Result<ArgInfo, ExecutionError> {
1249        // check the arity
1250        let parameters = &signature.parameters;
1251        let tx_ctx_kind = match parameters.last() {
1252            Some(t) => is_tx_context(context, t)?,
1253            None => TxContextKind::None,
1254        };
1255        // an init function can have one or two arguments, with the last one always being of type
1256        // &mut TxContext and the additional (first) one representing a one time witness type (see
1257        // one_time_witness verifier pass for additional explanation)
1258        let has_one_time_witness = function_kind == FunctionKind::Init && parameters.len() == 2;
1259        let has_tx_context = tx_ctx_kind != TxContextKind::None;
1260        let num_args = args.len() + (has_one_time_witness as usize) + (has_tx_context as usize);
1261        if num_args != parameters.len() {
1262            return Err(ExecutionError::new_with_source(
1263                ExecutionErrorKind::ArityMismatch,
1264                format!(
1265                    "Expected {:?} argument{} calling function '{}', but found {:?}",
1266                    parameters.len(),
1267                    if parameters.len() == 1 { "" } else { "s" },
1268                    function,
1269                    num_args
1270                ),
1271            ));
1272        }
1273
1274        // check the types and remember which are by mutable ref
1275        let mut by_mut_ref = vec![];
1276        let mut serialized_args = Vec::with_capacity(num_args);
1277        let command_kind = CommandKind::MoveCall {
1278            package: (*module_id.address()).into(),
1279            module: module_id.name(),
1280            function,
1281        };
1282        // an init function can have one or two arguments, with the last one always being of type
1283        // &mut TxContext and the additional (first) one representing a one time witness type (see
1284        // one_time_witness verifier pass for additional explanation)
1285        if has_one_time_witness {
1286            // one time witness type is a struct with a single bool filed which in bcs is encoded as
1287            // 0x01
1288            let bcs_true_value = bcs::to_bytes(&true).unwrap();
1289            serialized_args.push(bcs_true_value)
1290        }
1291        for ((idx, arg), param_ty) in args.iter().copied().enumerate().zip(parameters) {
1292            let (value, non_ref_param_ty): (Value, &Type) = match param_ty {
1293                Type::MutableReference(inner) => {
1294                    let value = context.borrow_arg_mut(idx, arg)?;
1295                    let object_info = if let Value::Object(ObjectValue {
1296                        type_,
1297                        has_public_transfer,
1298                        ..
1299                    }) = &value
1300                    {
1301                        let type_tag = context
1302                            .session
1303                            .get_type_tag(type_)
1304                            .map_err(|e| context.convert_vm_error(e))?;
1305                        let TypeTag::Struct(struct_tag) = type_tag else {
1306                            invariant_violation!("Struct type make a non struct type tag")
1307                        };
1308                        let type_ = (*struct_tag).into();
1309                        ValueKind::Object {
1310                            type_,
1311                            has_public_transfer: *has_public_transfer,
1312                        }
1313                    } else {
1314                        let abilities = context
1315                            .session
1316                            .get_type_abilities(inner)
1317                            .map_err(|e| context.convert_vm_error(e))?;
1318                        ValueKind::Raw((**inner).clone(), abilities)
1319                    };
1320                    by_mut_ref.push((idx as LocalIndex, object_info));
1321                    (value, inner)
1322                }
1323                Type::Reference(inner) => (context.borrow_arg(idx, arg)?, inner),
1324                t => {
1325                    let value = context.by_value_arg(command_kind, idx, arg)?;
1326                    (value, t)
1327                }
1328            };
1329            if matches!(
1330                function_kind,
1331                FunctionKind::PrivateEntry | FunctionKind::Init
1332            ) && value.was_used_in_non_entry_move_call()
1333            {
1334                return Err(command_argument_error(
1335                    CommandArgumentError::InvalidArgumentToPrivateEntryFunction,
1336                    idx,
1337                ));
1338            }
1339            check_param_type::<Mode>(context, idx, &value, non_ref_param_ty)?;
1340            let bytes = {
1341                let mut v = vec![];
1342                value.write_bcs_bytes(&mut v);
1343                v
1344            };
1345            serialized_args.push(bytes);
1346        }
1347        Ok((tx_ctx_kind, by_mut_ref, serialized_args))
1348    }
1349
1350    /// checks that the value is compatible with the specified type
1351    fn check_param_type<Mode: ExecutionMode>(
1352        context: &mut ExecutionContext<'_, '_, '_>,
1353        idx: usize,
1354        value: &Value,
1355        param_ty: &Type,
1356    ) -> Result<(), ExecutionError> {
1357        let ty = match value {
1358            // For dev-spect, allow any BCS bytes. This does mean internal invariants for types can
1359            // be violated (like for string or Option)
1360            Value::Raw(RawValueType::Any, _) if Mode::allow_arbitrary_values() => return Ok(()),
1361            // Any means this was just some bytes passed in as an argument (as opposed to being
1362            // generated from a Move function). Meaning we only allow "primitive" values
1363            // and might need to run validation in addition to the BCS layout
1364            Value::Raw(RawValueType::Any, bytes) => {
1365                let Some(layout) = primitive_serialization_layout(context, param_ty)? else {
1366                    let msg = format!(
1367                        "Non-primitive argument at index {}. If it is an object, it must be \
1368                    populated by an object",
1369                        idx,
1370                    );
1371                    return Err(ExecutionError::new_with_source(
1372                        ExecutionErrorKind::command_argument_error(
1373                            CommandArgumentError::InvalidUsageOfPureArg,
1374                            idx as u16,
1375                        ),
1376                        msg,
1377                    ));
1378                };
1379                bcs_argument_validate(bytes, idx as u16, layout)?;
1380                return Ok(());
1381            }
1382            Value::Raw(RawValueType::Loaded { ty, abilities, .. }, _) => {
1383                assert_invariant!(
1384                    Mode::allow_arbitrary_values() || !abilities.has_key(),
1385                    "Raw value should never be an object"
1386                );
1387                ty
1388            }
1389            Value::Object(obj) => &obj.type_,
1390            Value::Receiving(_, _, _) => {
1391                unreachable!("Receiving value should never occur in v0 execution")
1392            }
1393        };
1394        if ty != param_ty {
1395            Err(command_argument_error(
1396                CommandArgumentError::TypeMismatch,
1397                idx,
1398            ))
1399        } else {
1400            Ok(())
1401        }
1402    }
1403
1404    fn get_datatype_ident(s: &CachedDatatype) -> (&AccountAddress, &IdentStr, &IdentStr) {
1405        let module_id = &s.defining_id;
1406        let struct_name = &s.name;
1407        (
1408            module_id.address(),
1409            module_id.name(),
1410            struct_name.as_ident_str(),
1411        )
1412    }
1413
1414    // Returns Some(kind) if the type is a reference to the TxnContext. kind being Mutable with
1415    // a MutableReference, and Immutable otherwise.
1416    // Returns None for all other types
1417    pub fn is_tx_context(
1418        context: &mut ExecutionContext<'_, '_, '_>,
1419        t: &Type,
1420    ) -> Result<TxContextKind, ExecutionError> {
1421        let (is_mut, inner) = match t {
1422            Type::MutableReference(inner) => (true, inner),
1423            Type::Reference(inner) => (false, inner),
1424            _ => return Ok(TxContextKind::None),
1425        };
1426        let Type::Datatype(idx) = &**inner else {
1427            return Ok(TxContextKind::None);
1428        };
1429        let Some(s) = context.session.get_struct_type(*idx) else {
1430            invariant_violation!("Loaded struct not found")
1431        };
1432        let (module_addr, module_name, struct_name) = get_datatype_ident(&s);
1433        let is_tx_context_type = module_addr == &SUI_FRAMEWORK_ADDRESS
1434            && module_name == TX_CONTEXT_MODULE_NAME
1435            && struct_name == TX_CONTEXT_STRUCT_NAME;
1436        Ok(if is_tx_context_type {
1437            if is_mut {
1438                TxContextKind::Mutable
1439            } else {
1440                TxContextKind::Immutable
1441            }
1442        } else {
1443            TxContextKind::None
1444        })
1445    }
1446
1447    /// Returns Some(layout) iff it is a primitive, an ID, a String, or an option/vector of a valid type
1448    fn primitive_serialization_layout(
1449        context: &mut ExecutionContext<'_, '_, '_>,
1450        param_ty: &Type,
1451    ) -> Result<Option<PrimitiveArgumentLayout>, ExecutionError> {
1452        Ok(match param_ty {
1453            Type::Signer => return Ok(None),
1454            Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => {
1455                invariant_violation!("references and type parameters should be checked elsewhere")
1456            }
1457            Type::Bool => Some(PrimitiveArgumentLayout::Bool),
1458            Type::U8 => Some(PrimitiveArgumentLayout::U8),
1459            Type::U16 => Some(PrimitiveArgumentLayout::U16),
1460            Type::U32 => Some(PrimitiveArgumentLayout::U32),
1461            Type::U64 => Some(PrimitiveArgumentLayout::U64),
1462            Type::U128 => Some(PrimitiveArgumentLayout::U128),
1463            Type::U256 => Some(PrimitiveArgumentLayout::U256),
1464            Type::Address => Some(PrimitiveArgumentLayout::Address),
1465
1466            Type::Vector(inner) => {
1467                let info_opt = primitive_serialization_layout(context, inner)?;
1468                info_opt.map(|layout| PrimitiveArgumentLayout::Vector(Box::new(layout)))
1469            }
1470            Type::DatatypeInstantiation(inst) => {
1471                let (idx, targs) = &**inst;
1472                let Some(s) = context.session.get_struct_type(*idx) else {
1473                    invariant_violation!("Loaded struct not found")
1474                };
1475                let resolved_struct = get_datatype_ident(&s);
1476                // is option of a string
1477                if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
1478                    let info_opt = primitive_serialization_layout(context, &targs[0])?;
1479                    info_opt.map(|layout| PrimitiveArgumentLayout::Option(Box::new(layout)))
1480                } else {
1481                    None
1482                }
1483            }
1484            Type::Datatype(idx) => {
1485                let Some(s) = context.session.get_struct_type(*idx) else {
1486                    invariant_violation!("Loaded struct not found")
1487                };
1488                let resolved_struct = get_datatype_ident(&s);
1489                if resolved_struct == RESOLVED_SUI_ID {
1490                    Some(PrimitiveArgumentLayout::Address)
1491                } else if resolved_struct == RESOLVED_ASCII_STR {
1492                    Some(PrimitiveArgumentLayout::Ascii)
1493                } else if resolved_struct == RESOLVED_UTF8_STR {
1494                    Some(PrimitiveArgumentLayout::UTF8)
1495                } else {
1496                    None
1497                }
1498            }
1499        })
1500    }
1501
1502    /***************************************************************************************************
1503     * Special serialization formats
1504     **************************************************************************************************/
1505
1506    /// Special enum for values that need additional validation, in other words
1507    /// There is validation to do on top of the BCS layout. Currently only needed for
1508    /// strings
1509    #[derive(Debug)]
1510    pub enum PrimitiveArgumentLayout {
1511        /// An option
1512        Option(Box<PrimitiveArgumentLayout>),
1513        /// A vector
1514        Vector(Box<PrimitiveArgumentLayout>),
1515        /// An ASCII encoded string
1516        Ascii,
1517        /// A UTF8 encoded string
1518        UTF8,
1519        // needed for Option validation
1520        Bool,
1521        U8,
1522        U16,
1523        U32,
1524        U64,
1525        U128,
1526        U256,
1527        Address,
1528    }
1529
1530    impl PrimitiveArgumentLayout {
1531        /// returns true iff all BCS compatible bytes are actually values for this type.
1532        /// For example, this function returns false for Option and Strings since they need additional
1533        /// validation.
1534        pub fn bcs_only(&self) -> bool {
1535            match self {
1536                // have additional restrictions past BCS
1537                PrimitiveArgumentLayout::Option(_)
1538                | PrimitiveArgumentLayout::Ascii
1539                | PrimitiveArgumentLayout::UTF8 => false,
1540                // Move primitives are BCS compatible and do not need additional validation
1541                PrimitiveArgumentLayout::Bool
1542                | PrimitiveArgumentLayout::U8
1543                | PrimitiveArgumentLayout::U16
1544                | PrimitiveArgumentLayout::U32
1545                | PrimitiveArgumentLayout::U64
1546                | PrimitiveArgumentLayout::U128
1547                | PrimitiveArgumentLayout::U256
1548                | PrimitiveArgumentLayout::Address => true,
1549                // vector only needs validation if it's inner type does
1550                PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
1551            }
1552        }
1553    }
1554
1555    /// Checks the bytes against the `SpecialArgumentLayout` using `bcs`. It does not actually generate
1556    /// the deserialized value, only walks the bytes. While not necessary if the layout does not contain
1557    /// special arguments (e.g. Option or String) we check the BCS bytes for predictability
1558    pub fn bcs_argument_validate(
1559        bytes: &[u8],
1560        idx: u16,
1561        layout: PrimitiveArgumentLayout,
1562    ) -> Result<(), ExecutionError> {
1563        bcs::from_bytes_seed(&layout, bytes).map_err(|_| {
1564            ExecutionError::new_with_source(
1565                ExecutionErrorKind::command_argument_error(
1566                    CommandArgumentError::InvalidBCSBytes,
1567                    idx,
1568                ),
1569                format!("Function expects {layout} but provided argument's value does not match",),
1570            )
1571        })
1572    }
1573
1574    impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout {
1575        type Value = ();
1576        fn deserialize<D: serde::de::Deserializer<'d>>(
1577            self,
1578            deserializer: D,
1579        ) -> Result<Self::Value, D::Error> {
1580            use serde::de::Error;
1581            match self {
1582                PrimitiveArgumentLayout::Ascii => {
1583                    let s: &str = serde::Deserialize::deserialize(deserializer)?;
1584                    if !s.is_ascii() {
1585                        Err(D::Error::custom("not an ascii string"))
1586                    } else {
1587                        Ok(())
1588                    }
1589                }
1590                PrimitiveArgumentLayout::UTF8 => {
1591                    deserializer.deserialize_string(serde::de::IgnoredAny)?;
1592                    Ok(())
1593                }
1594                PrimitiveArgumentLayout::Option(layout) => {
1595                    deserializer.deserialize_option(OptionElementVisitor(layout))
1596                }
1597                PrimitiveArgumentLayout::Vector(layout) => {
1598                    deserializer.deserialize_seq(VectorElementVisitor(layout))
1599                }
1600                // primitive move value cases, which are hit to make sure the correct number of bytes
1601                // are removed for elements of an option/vector
1602                PrimitiveArgumentLayout::Bool => {
1603                    deserializer.deserialize_bool(serde::de::IgnoredAny)?;
1604                    Ok(())
1605                }
1606                PrimitiveArgumentLayout::U8 => {
1607                    deserializer.deserialize_u8(serde::de::IgnoredAny)?;
1608                    Ok(())
1609                }
1610                PrimitiveArgumentLayout::U16 => {
1611                    deserializer.deserialize_u16(serde::de::IgnoredAny)?;
1612                    Ok(())
1613                }
1614                PrimitiveArgumentLayout::U32 => {
1615                    deserializer.deserialize_u32(serde::de::IgnoredAny)?;
1616                    Ok(())
1617                }
1618                PrimitiveArgumentLayout::U64 => {
1619                    deserializer.deserialize_u64(serde::de::IgnoredAny)?;
1620                    Ok(())
1621                }
1622                PrimitiveArgumentLayout::U128 => {
1623                    deserializer.deserialize_u128(serde::de::IgnoredAny)?;
1624                    Ok(())
1625                }
1626                PrimitiveArgumentLayout::U256 => {
1627                    U256::deserialize(deserializer)?;
1628                    Ok(())
1629                }
1630                PrimitiveArgumentLayout::Address => {
1631                    SuiAddress::deserialize(deserializer)?;
1632                    Ok(())
1633                }
1634            }
1635        }
1636    }
1637
1638    struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout);
1639
1640    impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> {
1641        type Value = ();
1642
1643        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1644            formatter.write_str("Vector")
1645        }
1646
1647        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1648        where
1649            A: serde::de::SeqAccess<'d>,
1650        {
1651            while seq.next_element_seed(self.0)?.is_some() {}
1652            Ok(())
1653        }
1654    }
1655
1656    struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout);
1657
1658    impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> {
1659        type Value = ();
1660
1661        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1662            formatter.write_str("Option")
1663        }
1664
1665        fn visit_none<E>(self) -> Result<Self::Value, E>
1666        where
1667            E: serde::de::Error,
1668        {
1669            Ok(())
1670        }
1671
1672        fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1673        where
1674            D: serde::Deserializer<'d>,
1675        {
1676            self.0.deserialize(deserializer)
1677        }
1678    }
1679
1680    impl fmt::Display for PrimitiveArgumentLayout {
1681        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1682            match self {
1683                PrimitiveArgumentLayout::Vector(inner) => {
1684                    write!(f, "vector<{inner}>")
1685                }
1686                PrimitiveArgumentLayout::Option(inner) => {
1687                    write!(f, "std::option::Option<{inner}>")
1688                }
1689                PrimitiveArgumentLayout::Ascii => {
1690                    write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2)
1691                }
1692                PrimitiveArgumentLayout::UTF8 => {
1693                    write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2)
1694                }
1695                PrimitiveArgumentLayout::Bool => write!(f, "bool"),
1696                PrimitiveArgumentLayout::U8 => write!(f, "u8"),
1697                PrimitiveArgumentLayout::U16 => write!(f, "u16"),
1698                PrimitiveArgumentLayout::U32 => write!(f, "u32"),
1699                PrimitiveArgumentLayout::U64 => write!(f, "u64"),
1700                PrimitiveArgumentLayout::U128 => write!(f, "u128"),
1701                PrimitiveArgumentLayout::U256 => write!(f, "u256"),
1702                PrimitiveArgumentLayout::Address => write!(f, "address"),
1703            }
1704        }
1705    }
1706}