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