sui_adapter_latest/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    use crate::{
9        adapter::substitute_package_id,
10        data_store::{PackageStore, legacy::sui_data_store::SuiDataStore},
11        execution_mode::ExecutionMode,
12        execution_value::{
13            CommandKind, ExecutionState, ObjectContents, ObjectValue, RawValueType, Value,
14            ensure_serialized_size,
15        },
16        gas_charger::GasCharger,
17        programmable_transactions::{context::*, trace_utils},
18        static_programmable_transactions,
19        type_resolver::TypeTagResolver,
20    };
21    use move_binary_format::file_format::AbilitySet;
22    use move_binary_format::{
23        CompiledModule,
24        compatibility::{Compatibility, InclusionCheck},
25        errors::{Location, PartialVMResult, VMResult},
26        file_format::{CodeOffset, FunctionDefinitionIndex, LocalIndex, Visibility},
27        file_format_common::VERSION_6,
28        normalized,
29    };
30    use move_core_types::{
31        account_address::AccountAddress,
32        identifier::{IdentStr, Identifier},
33        language_storage::{ModuleId, StructTag, TypeTag},
34        u256::U256,
35    };
36    use move_trace_format::format::MoveTraceBuilder;
37    use move_vm_runtime::{
38        move_vm::MoveVM,
39        session::{LoadedFunctionInstantiation, SerializedReturnValues},
40    };
41    use move_vm_types::loaded_data::runtime_types::{CachedDatatype, Type};
42    use serde::{Deserialize, de::DeserializeSeed};
43    use std::{
44        cell::{OnceCell, RefCell},
45        collections::{BTreeMap, BTreeSet},
46        fmt,
47        rc::Rc,
48        sync::Arc,
49        time::Instant,
50    };
51    use sui_move_natives::object_runtime::ObjectRuntime;
52    use sui_protocol_config::ProtocolConfig;
53    use sui_types::{
54        SUI_FRAMEWORK_ADDRESS,
55        base_types::{
56            MoveLegacyTxContext, MoveObjectType, ObjectID, RESOLVED_ASCII_STR, RESOLVED_STD_OPTION,
57            RESOLVED_UTF8_STR, SuiAddress, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME,
58            TxContext, TxContextKind,
59        },
60        coin::Coin,
61        error::{ExecutionError, ExecutionErrorKind, command_argument_error},
62        execution::{ExecutionTiming, ResultWithTimings},
63        execution_status::{CommandArgumentError, PackageUpgradeError, TypeArgumentError},
64        id::RESOLVED_SUI_ID,
65        metrics::LimitsMetrics,
66        move_package::{
67            MovePackage, UpgradeCap, UpgradePolicy, UpgradeReceipt, UpgradeTicket,
68            normalize_deserialized_modules,
69        },
70        storage::{BackingPackageStore, PackageObject, get_package_objects},
71        transaction::{Command, ProgrammableMoveCall, ProgrammableTransaction},
72        transfer::RESOLVED_RECEIVING_STRUCT,
73        type_input::{StructInput, TypeInput},
74    };
75    use sui_verifier::{
76        INIT_FN_NAME,
77        private_generics::{EVENT_MODULE, PRIVATE_TRANSFER_FUNCTIONS, TRANSFER_MODULE},
78        private_generics_verifier_v2,
79    };
80    use tracing::instrument;
81
82    pub fn execute<Mode: ExecutionMode>(
83        protocol_config: &ProtocolConfig,
84        metrics: Arc<LimitsMetrics>,
85        vm: &MoveVM,
86        state_view: &mut dyn ExecutionState,
87        package_store: &dyn BackingPackageStore,
88        tx_context: Rc<RefCell<TxContext>>,
89        gas_charger: &mut GasCharger,
90        withdrawal_compatibility_inputs: Option<Vec<bool>>,
91        pt: ProgrammableTransaction,
92        trace_builder_opt: &mut Option<MoveTraceBuilder>,
93    ) -> ResultWithTimings<Mode::ExecutionResults, ExecutionError> {
94        if protocol_config.enable_ptb_execution_v2() {
95            return static_programmable_transactions::execute::<Mode>(
96                protocol_config,
97                metrics,
98                vm,
99                state_view,
100                package_store,
101                tx_context,
102                gas_charger,
103                withdrawal_compatibility_inputs,
104                pt,
105                trace_builder_opt,
106            );
107        }
108
109        let mut timings = vec![];
110        let result = execute_inner::<Mode>(
111            &mut timings,
112            protocol_config,
113            metrics,
114            vm,
115            state_view,
116            tx_context,
117            gas_charger,
118            pt,
119            trace_builder_opt,
120        );
121
122        match result {
123            Ok(result) => Ok((result, timings)),
124            Err(e) => {
125                trace_utils::trace_execution_error(trace_builder_opt, e.to_string());
126
127                Err((e, timings))
128            }
129        }
130    }
131
132    pub fn execute_inner<Mode: ExecutionMode>(
133        timings: &mut Vec<ExecutionTiming>,
134        protocol_config: &ProtocolConfig,
135        metrics: Arc<LimitsMetrics>,
136        vm: &MoveVM,
137        state_view: &mut dyn ExecutionState,
138        tx_context: Rc<RefCell<TxContext>>,
139        gas_charger: &mut GasCharger,
140        pt: ProgrammableTransaction,
141        trace_builder_opt: &mut Option<MoveTraceBuilder>,
142    ) -> Result<Mode::ExecutionResults, ExecutionError> {
143        let ProgrammableTransaction { inputs, commands } = pt;
144        let mut context = ExecutionContext::new(
145            protocol_config,
146            metrics,
147            vm,
148            state_view,
149            tx_context,
150            gas_charger,
151            inputs,
152        )?;
153
154        trace_utils::trace_ptb_summary::<Mode>(&mut context, trace_builder_opt, &commands)?;
155
156        // execute commands
157        let mut mode_results = Mode::empty_results();
158        for (idx, command) in commands.into_iter().enumerate() {
159            let start = Instant::now();
160            if let Err(err) =
161                execute_command::<Mode>(&mut context, &mut mode_results, command, trace_builder_opt)
162            {
163                let object_runtime: &ObjectRuntime = context.object_runtime()?;
164                // We still need to record the loaded child objects for replay
165                let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
166                // we do not save the wrapped objects since on error, they should not be modified
167                drop(context);
168                state_view.save_loaded_runtime_objects(loaded_runtime_objects);
169                timings.push(ExecutionTiming::Abort(start.elapsed()));
170                return Err(err.with_command_index(idx));
171            };
172            timings.push(ExecutionTiming::Success(start.elapsed()));
173        }
174
175        // Save loaded objects table in case we fail in post execution
176        let object_runtime: &ObjectRuntime = context.object_runtime()?;
177        // We still need to record the loaded child objects for replay
178        // Record the objects loaded at runtime (dynamic fields + received) for
179        // storage rebate calculation.
180        let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
181        // We record what objects were contained in at the start of the transaction
182        // for expensive invariant checks
183        let wrapped_object_containers = object_runtime.wrapped_object_containers();
184        // We record the generated object IDs for expensive invariant checks
185        let generated_object_ids = object_runtime.generated_object_ids();
186
187        // apply changes
188        let finished = context.finish::<Mode>();
189        // Save loaded objects for debug. We dont want to lose the info
190        state_view.save_loaded_runtime_objects(loaded_runtime_objects);
191        state_view.save_wrapped_object_containers(wrapped_object_containers);
192        state_view.record_execution_results(finished?)?;
193        state_view.record_generated_object_ids(generated_object_ids);
194        Ok(mode_results)
195    }
196
197    /// Execute a single command
198    #[instrument(level = "trace", skip_all)]
199    fn execute_command<Mode: ExecutionMode>(
200        context: &mut ExecutionContext<'_, '_, '_>,
201        mode_results: &mut Mode::ExecutionResults,
202        command: Command,
203        trace_builder_opt: &mut Option<MoveTraceBuilder>,
204    ) -> Result<(), ExecutionError> {
205        let mut argument_updates = Mode::empty_arguments();
206
207        let kind = match &command {
208            Command::MakeMoveVec(_, _) => CommandKind::MakeMoveVec,
209            Command::TransferObjects(_, _) => CommandKind::TransferObjects,
210            Command::SplitCoins(_, _) => CommandKind::SplitCoins,
211            Command::MergeCoins(_, _) => CommandKind::MergeCoins,
212            Command::MoveCall(_) => CommandKind::MoveCall,
213            Command::Publish(_, _) => CommandKind::Publish,
214            Command::Upgrade(_, _, _, _) => CommandKind::Upgrade,
215        };
216        let results = match command {
217            Command::MakeMoveVec(tag_opt, args) if args.is_empty() => {
218                let Some(tag) = tag_opt else {
219                    invariant_violation!(
220                        "input checker ensures if args are empty, there is a type specified"
221                    );
222                };
223
224                let tag = to_type_tag(context, tag, 0)?;
225
226                let elem_ty = context.load_type(&tag).map_err(|e| {
227                    if context.protocol_config.convert_type_argument_error() {
228                        context.convert_type_argument_error(0, e)
229                    } else {
230                        context.convert_vm_error(e)
231                    }
232                })?;
233
234                let ty = Type::Vector(Box::new(elem_ty));
235                let abilities = context.get_type_abilities(&ty)?;
236                // BCS layout for any empty vector should be the same
237                let bytes = bcs::to_bytes::<Vec<u8>>(&vec![]).unwrap();
238
239                trace_utils::trace_make_move_vec(context, trace_builder_opt, vec![], &ty)?;
240
241                vec![Value::Raw(
242                    RawValueType::Loaded {
243                        ty,
244                        abilities,
245                        used_in_non_entry_move_call: false,
246                    },
247                    bytes,
248                )]
249            }
250            Command::MakeMoveVec(tag_opt, args) => {
251                let args = context.splat_args(0, args)?;
252                let elem_abilities = OnceCell::<AbilitySet>::new();
253                let mut res = vec![];
254                leb128::write::unsigned(&mut res, args.len() as u64).unwrap();
255                let mut arg_iter = args.into_iter().enumerate();
256                let mut move_values = vec![];
257                let (mut used_in_non_entry_move_call, elem_ty) = match tag_opt {
258                    Some(tag) => {
259                        let tag = to_type_tag(context, tag, 0)?;
260                        let elem_ty = context.load_type(&tag).map_err(|e| {
261                            if context.protocol_config.convert_type_argument_error() {
262                                context.convert_type_argument_error(0, e)
263                            } else {
264                                context.convert_vm_error(e)
265                            }
266                        })?;
267                        (false, elem_ty)
268                    }
269                    // If no tag specified, it _must_ be an object
270                    None => {
271                        // empty args covered above
272                        let (idx, arg) = arg_iter.next().unwrap();
273                        let obj: ObjectValue =
274                            context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
275                        trace_utils::add_move_value_info_from_obj_value(
276                            context,
277                            trace_builder_opt,
278                            &mut move_values,
279                            &obj,
280                        )?;
281                        let bound =
282                            amplification_bound::<Mode>(context, &obj.type_, &elem_abilities)?;
283                        obj.write_bcs_bytes(
284                            &mut res,
285                            bound.map(|b| context.size_bound_vector_elem(b)),
286                        )?;
287                        (obj.used_in_non_entry_move_call, obj.type_)
288                    }
289                };
290                for (idx, arg) in arg_iter {
291                    let value: Value = context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
292                    trace_utils::add_move_value_info_from_value(
293                        context,
294                        trace_builder_opt,
295                        &mut move_values,
296                        &elem_ty,
297                        &value,
298                    )?;
299                    check_param_type::<Mode>(context, idx, &value, &elem_ty)?;
300                    used_in_non_entry_move_call =
301                        used_in_non_entry_move_call || value.was_used_in_non_entry_move_call();
302                    let bound = amplification_bound::<Mode>(context, &elem_ty, &elem_abilities)?;
303                    value.write_bcs_bytes(
304                        &mut res,
305                        bound.map(|b| context.size_bound_vector_elem(b)),
306                    )?;
307                }
308                let ty = Type::Vector(Box::new(elem_ty));
309                let abilities = context.get_type_abilities(&ty)?;
310
311                trace_utils::trace_make_move_vec(context, trace_builder_opt, move_values, &ty)?;
312
313                vec![Value::Raw(
314                    RawValueType::Loaded {
315                        ty,
316                        abilities,
317                        used_in_non_entry_move_call,
318                    },
319                    res,
320                )]
321            }
322            Command::TransferObjects(objs, addr_arg) => {
323                let unsplat_objs_len = objs.len();
324                let objs = context.splat_args(0, objs)?;
325                let addr_arg = context.one_arg(unsplat_objs_len, addr_arg)?;
326                let objs: Vec<ObjectValue> = objs
327                    .into_iter()
328                    .enumerate()
329                    .map(|(idx, arg)| context.by_value_arg(CommandKind::TransferObjects, idx, arg))
330                    .collect::<Result<_, _>>()?;
331                let addr: SuiAddress =
332                    context.by_value_arg(CommandKind::TransferObjects, objs.len(), addr_arg)?;
333
334                trace_utils::trace_transfer(context, trace_builder_opt, &objs)?;
335
336                for obj in objs {
337                    obj.ensure_public_transfer_eligible()?;
338                    context.transfer_object(obj, addr)?;
339                }
340                vec![]
341            }
342            Command::SplitCoins(coin_arg, amount_args) => {
343                let coin_arg = context.one_arg(0, coin_arg)?;
344                let amount_args = context.splat_args(1, amount_args)?;
345                let mut obj: ObjectValue = context.borrow_arg_mut(0, coin_arg)?;
346                let ObjectContents::Coin(coin) = &mut obj.contents else {
347                    let e = ExecutionErrorKind::command_argument_error(
348                        CommandArgumentError::TypeMismatch,
349                        0,
350                    );
351                    let msg = "Expected a coin but got an non coin object".to_owned();
352                    return Err(ExecutionError::new_with_source(e, msg));
353                };
354                let split_coins: Vec<Value> = amount_args
355                    .into_iter()
356                    .map(|amount_arg| {
357                        let amount: u64 =
358                            context.by_value_arg(CommandKind::SplitCoins, 1, amount_arg)?;
359                        let new_coin_id = context.fresh_id()?;
360                        let new_coin = coin.split(amount, new_coin_id)?;
361                        let coin_type = obj.type_.clone();
362                        // safe because we are propagating the coin type, and relying on the internal
363                        // invariant that coin values have a coin type
364                        let new_coin = unsafe { ObjectValue::coin(coin_type, new_coin) };
365                        Ok(Value::Object(new_coin))
366                    })
367                    .collect::<Result<_, ExecutionError>>()?;
368
369                trace_utils::trace_split_coins(
370                    context,
371                    trace_builder_opt,
372                    &obj.type_,
373                    coin,
374                    &split_coins,
375                )?;
376
377                context.restore_arg::<Mode>(&mut argument_updates, coin_arg, Value::Object(obj))?;
378                split_coins
379            }
380            Command::MergeCoins(target_arg, coin_args) => {
381                let target_arg = context.one_arg(0, target_arg)?;
382                let coin_args = context.splat_args(1, coin_args)?;
383                let mut target: ObjectValue = context.borrow_arg_mut(0, target_arg)?;
384                let ObjectContents::Coin(target_coin) = &mut target.contents else {
385                    let e = ExecutionErrorKind::command_argument_error(
386                        CommandArgumentError::TypeMismatch,
387                        0,
388                    );
389                    let msg = "Expected a coin but got an non coin object".to_owned();
390                    return Err(ExecutionError::new_with_source(e, msg));
391                };
392                let coins: Vec<ObjectValue> = coin_args
393                    .into_iter()
394                    .enumerate()
395                    .map(|(idx, arg)| context.by_value_arg(CommandKind::MergeCoins, idx + 1, arg))
396                    .collect::<Result<_, _>>()?;
397                let mut input_infos = vec![];
398                for (idx, coin) in coins.into_iter().enumerate() {
399                    if target.type_ != coin.type_ {
400                        let e = ExecutionErrorKind::command_argument_error(
401                            CommandArgumentError::TypeMismatch,
402                            (idx + 1) as u16,
403                        );
404                        let msg = "Coins do not have the same type".to_owned();
405                        return Err(ExecutionError::new_with_source(e, msg));
406                    }
407                    let ObjectContents::Coin(Coin { id, balance }) = coin.contents else {
408                        invariant_violation!(
409                            "Target coin was a coin, and we already checked for the same type. \
410                            This should be a coin"
411                        );
412                    };
413                    trace_utils::add_coin_obj_info(
414                        trace_builder_opt,
415                        &mut input_infos,
416                        balance.value(),
417                        *id.object_id(),
418                    );
419                    context.delete_id(*id.object_id())?;
420                    target_coin.add(balance)?;
421                }
422
423                trace_utils::trace_merge_coins(
424                    context,
425                    trace_builder_opt,
426                    &target.type_,
427                    &input_infos,
428                    target_coin,
429                )?;
430
431                context.restore_arg::<Mode>(
432                    &mut argument_updates,
433                    target_arg,
434                    Value::Object(target),
435                )?;
436                vec![]
437            }
438            Command::MoveCall(move_call) => {
439                let ProgrammableMoveCall {
440                    package,
441                    module,
442                    function,
443                    type_arguments,
444                    arguments,
445                } = *move_call;
446                trace_utils::trace_move_call_start(trace_builder_opt);
447
448                let arguments = context.splat_args(0, arguments)?;
449
450                let module = to_identifier(context, module)?;
451                let function = to_identifier(context, function)?;
452
453                // Convert type arguments to `Type`s
454                let mut loaded_type_arguments = Vec::with_capacity(type_arguments.len());
455                for (ix, type_arg) in type_arguments.into_iter().enumerate() {
456                    let type_arg = to_type_tag(context, type_arg, ix)?;
457                    let ty = context
458                        .load_type(&type_arg)
459                        .map_err(|e| context.convert_type_argument_error(ix, e))?;
460                    loaded_type_arguments.push(ty);
461                }
462
463                let original_address = context.set_link_context(package)?;
464                let storage_id = ModuleId::new(*package, module.clone());
465                let runtime_id = ModuleId::new(original_address, module);
466                let return_values = execute_move_call::<Mode>(
467                    context,
468                    &mut argument_updates,
469                    &storage_id,
470                    &runtime_id,
471                    &function,
472                    loaded_type_arguments,
473                    arguments,
474                    /* is_init */ false,
475                    trace_builder_opt,
476                );
477
478                trace_utils::trace_move_call_end(trace_builder_opt);
479
480                context.linkage_view.reset_linkage()?;
481                return_values?
482            }
483            Command::Publish(modules, dep_ids) => {
484                trace_utils::trace_publish_event(trace_builder_opt)?;
485
486                execute_move_publish::<Mode>(
487                    context,
488                    &mut argument_updates,
489                    modules,
490                    dep_ids,
491                    trace_builder_opt,
492                )?
493            }
494            Command::Upgrade(modules, dep_ids, current_package_id, upgrade_ticket) => {
495                trace_utils::trace_upgrade_event(trace_builder_opt)?;
496
497                let upgrade_ticket = context.one_arg(0, upgrade_ticket)?;
498                execute_move_upgrade::<Mode>(
499                    context,
500                    modules,
501                    dep_ids,
502                    current_package_id,
503                    upgrade_ticket,
504                )?
505            }
506        };
507
508        Mode::finish_command(context, mode_results, argument_updates, &results)?;
509        context.push_command_results(kind, results)?;
510        Ok(())
511    }
512
513    /// Execute a single Move call
514    fn execute_move_call<Mode: ExecutionMode>(
515        context: &mut ExecutionContext<'_, '_, '_>,
516        argument_updates: &mut Mode::ArgumentUpdates,
517        storage_id: &ModuleId,
518        runtime_id: &ModuleId,
519        function: &IdentStr,
520        type_arguments: Vec<Type>,
521        arguments: Vec<Arg>,
522        is_init: bool,
523        trace_builder_opt: &mut Option<MoveTraceBuilder>,
524    ) -> Result<Vec<Value>, ExecutionError> {
525        // check that the function is either an entry function or a valid public function
526        let LoadedFunctionInfo {
527            kind,
528            signature,
529            return_value_kinds,
530            index,
531            last_instr,
532        } = check_visibility_and_signature::<Mode>(
533            context,
534            runtime_id,
535            function,
536            &type_arguments,
537            is_init,
538        )?;
539        // build the arguments, storing meta data about by-mut-ref args
540        let (tx_context_kind, by_mut_ref, serialized_arguments) =
541            build_move_args::<Mode>(context, runtime_id, function, kind, &signature, &arguments)?;
542        // invoke the VM
543        let SerializedReturnValues {
544            mutable_reference_outputs,
545            return_values,
546        } = vm_move_call(
547            context,
548            runtime_id,
549            function,
550            type_arguments,
551            tx_context_kind,
552            serialized_arguments,
553            trace_builder_opt,
554        )?;
555        assert_invariant!(
556            by_mut_ref.len() == mutable_reference_outputs.len(),
557            "lost mutable input"
558        );
559
560        if context.protocol_config.relocate_event_module() {
561            context.take_user_events(storage_id, index, last_instr)?;
562        } else {
563            context.take_user_events(runtime_id, index, last_instr)?;
564        }
565
566        // save the link context because calls to `make_value` below can set new ones, and we don't want
567        // it to be clobbered.
568        let saved_linkage = context.linkage_view.steal_linkage();
569        // write back mutable inputs. We also update if they were used in non entry Move calls
570        // though we do not care for immutable usages of objects or other values
571        let used_in_non_entry_move_call = kind == FunctionKind::NonEntry;
572        let res = write_back_results::<Mode>(
573            context,
574            argument_updates,
575            &arguments,
576            used_in_non_entry_move_call,
577            mutable_reference_outputs
578                .into_iter()
579                .map(|(i, bytes, _layout)| (i, bytes)),
580            by_mut_ref,
581            return_values.into_iter().map(|(bytes, _layout)| bytes),
582            return_value_kinds,
583        );
584
585        context.linkage_view.restore_linkage(saved_linkage)?;
586        res
587    }
588
589    fn write_back_results<Mode: ExecutionMode>(
590        context: &mut ExecutionContext<'_, '_, '_>,
591        argument_updates: &mut Mode::ArgumentUpdates,
592        arguments: &[Arg],
593        non_entry_move_call: bool,
594        mut_ref_values: impl IntoIterator<Item = (u8, Vec<u8>)>,
595        mut_ref_kinds: impl IntoIterator<Item = (u8, ValueKind)>,
596        return_values: impl IntoIterator<Item = Vec<u8>>,
597        return_value_kinds: impl IntoIterator<Item = ValueKind>,
598    ) -> Result<Vec<Value>, ExecutionError> {
599        for ((i, bytes), (j, kind)) in mut_ref_values.into_iter().zip(mut_ref_kinds) {
600            assert_invariant!(i == j, "lost mutable input");
601            let arg_idx = i as usize;
602            let value = make_value(context, kind, bytes, non_entry_move_call)?;
603            context.restore_arg::<Mode>(argument_updates, arguments[arg_idx], value)?;
604        }
605
606        return_values
607            .into_iter()
608            .zip(return_value_kinds)
609            .map(|(bytes, kind)| {
610                // only non entry functions have return values
611                make_value(
612                    context, kind, bytes, /* used_in_non_entry_move_call */ true,
613                )
614            })
615            .collect()
616    }
617
618    fn make_value(
619        context: &mut ExecutionContext<'_, '_, '_>,
620        value_info: ValueKind,
621        bytes: Vec<u8>,
622        used_in_non_entry_move_call: bool,
623    ) -> Result<Value, ExecutionError> {
624        Ok(match value_info {
625            ValueKind::Object {
626                type_,
627                has_public_transfer,
628            } => Value::Object(context.make_object_value(
629                type_,
630                has_public_transfer,
631                used_in_non_entry_move_call,
632                &bytes,
633            )?),
634            ValueKind::Raw(ty, abilities) => Value::Raw(
635                RawValueType::Loaded {
636                    ty,
637                    abilities,
638                    used_in_non_entry_move_call,
639                },
640                bytes,
641            ),
642        })
643    }
644
645    /// Publish Move modules and call the init functions.  Returns an `UpgradeCap` for the newly
646    /// published package on success.
647    fn execute_move_publish<Mode: ExecutionMode>(
648        context: &mut ExecutionContext<'_, '_, '_>,
649        argument_updates: &mut Mode::ArgumentUpdates,
650        module_bytes: Vec<Vec<u8>>,
651        dep_ids: Vec<ObjectID>,
652        trace_builder_opt: &mut Option<MoveTraceBuilder>,
653    ) -> Result<Vec<Value>, ExecutionError> {
654        assert_invariant!(
655            !module_bytes.is_empty(),
656            "empty package is checked in transaction input checker"
657        );
658        context
659            .gas_charger
660            .charge_publish_package(module_bytes.iter().map(|v| v.len()).sum())?;
661
662        let mut modules = context.deserialize_modules(&module_bytes)?;
663
664        // It should be fine that this does not go through ExecutionContext::fresh_id since the Move
665        // runtime does not to know about new packages created, since Move objects and Move packages
666        // cannot interact
667        let runtime_id = if Mode::packages_are_predefined() {
668            // do not calculate or substitute id for predefined packages
669            (*modules[0].self_id().address()).into()
670        } else {
671            let id = context.tx_context.borrow_mut().fresh_id();
672            substitute_package_id(&mut modules, id)?;
673            id
674        };
675
676        // For newly published packages, runtime ID matches storage ID.
677        let storage_id = runtime_id;
678        let dependencies = fetch_packages(&context.state_view, &dep_ids)?;
679        let package =
680            context.new_package(&modules, dependencies.iter().map(|p| p.move_package()))?;
681
682        // Here we optimistically push the package that is being published/upgraded
683        // and if there is an error of any kind (verification or module init) we
684        // remove it.
685        // The call to `pop_last_package` later is fine because we cannot re-enter and
686        // the last package we pushed is the one we are verifying and running the init from
687        context.linkage_view.set_linkage(&package)?;
688        context.write_package(package);
689        let res = publish_and_verify_modules(context, runtime_id, &modules).and_then(|_| {
690            init_modules::<Mode>(context, argument_updates, &modules, trace_builder_opt)
691        });
692        context.linkage_view.reset_linkage()?;
693        if res.is_err() {
694            context.pop_package();
695        }
696        res?;
697
698        let values = if Mode::packages_are_predefined() {
699            // no upgrade cap for genesis modules
700            vec![]
701        } else {
702            let cap = &UpgradeCap::new(context.fresh_id()?, storage_id);
703            vec![Value::Object(context.make_object_value(
704                UpgradeCap::type_().into(),
705                /* has_public_transfer */ true,
706                /* used_in_non_entry_move_call */ false,
707                &bcs::to_bytes(cap).unwrap(),
708            )?)]
709        };
710        Ok(values)
711    }
712
713    /// Upgrade a Move package.  Returns an `UpgradeReceipt` for the upgraded package on success.
714    fn execute_move_upgrade<Mode: ExecutionMode>(
715        context: &mut ExecutionContext<'_, '_, '_>,
716        module_bytes: Vec<Vec<u8>>,
717        dep_ids: Vec<ObjectID>,
718        current_package_id: ObjectID,
719        upgrade_ticket_arg: Arg,
720    ) -> Result<Vec<Value>, ExecutionError> {
721        assert_invariant!(
722            !module_bytes.is_empty(),
723            "empty package is checked in transaction input checker"
724        );
725        context
726            .gas_charger
727            .charge_upgrade_package(module_bytes.iter().map(|v| v.len()).sum())?;
728
729        let upgrade_ticket_type = context
730            .load_type_from_struct(&UpgradeTicket::type_())
731            .map_err(|e| context.convert_vm_error(e))?;
732        let upgrade_receipt_type = context
733            .load_type_from_struct(&UpgradeReceipt::type_())
734            .map_err(|e| context.convert_vm_error(e))?;
735
736        let upgrade_ticket: UpgradeTicket = {
737            let mut ticket_bytes = Vec::new();
738            let ticket_val: Value =
739                context.by_value_arg(CommandKind::Upgrade, 0, upgrade_ticket_arg)?;
740            check_param_type::<Mode>(context, 0, &ticket_val, &upgrade_ticket_type)?;
741            let bound =
742                amplification_bound::<Mode>(context, &upgrade_ticket_type, &OnceCell::new())?;
743            ticket_val
744                .write_bcs_bytes(&mut ticket_bytes, bound.map(|b| context.size_bound_raw(b)))?;
745            bcs::from_bytes(&ticket_bytes).map_err(|_| {
746                ExecutionError::from_kind(ExecutionErrorKind::CommandArgumentError {
747                    arg_idx: 0,
748                    kind: CommandArgumentError::InvalidBCSBytes,
749                })
750            })?
751        };
752
753        // Make sure the passed-in package ID matches the package ID in the `upgrade_ticket`.
754        if current_package_id != upgrade_ticket.package.bytes {
755            return Err(ExecutionError::from_kind(
756                ExecutionErrorKind::PackageUpgradeError {
757                    upgrade_error: PackageUpgradeError::PackageIDDoesNotMatch {
758                        package_id: current_package_id,
759                        ticket_id: upgrade_ticket.package.bytes,
760                    },
761                },
762            ));
763        }
764
765        // Check digest.
766        let hash_modules = true;
767        let computed_digest =
768            MovePackage::compute_digest_for_modules_and_deps(&module_bytes, &dep_ids, hash_modules)
769                .to_vec();
770        if computed_digest != upgrade_ticket.digest {
771            return Err(ExecutionError::from_kind(
772                ExecutionErrorKind::PackageUpgradeError {
773                    upgrade_error: PackageUpgradeError::DigestDoesNotMatch {
774                        digest: computed_digest,
775                    },
776                },
777            ));
778        }
779
780        // Check that this package ID points to a package and get the package we're upgrading.
781        let current_package = fetch_package(&context.state_view, &upgrade_ticket.package.bytes)?;
782
783        let mut modules = context.deserialize_modules(&module_bytes)?;
784        let runtime_id = current_package.move_package().original_package_id();
785        substitute_package_id(&mut modules, runtime_id)?;
786
787        // Upgraded packages share their predecessor's runtime ID but get a new storage ID.
788        let storage_id = context.tx_context.borrow_mut().fresh_id();
789
790        let dependencies = fetch_packages(&context.state_view, &dep_ids)?;
791        let package = context.upgrade_package(
792            storage_id,
793            current_package.move_package(),
794            &modules,
795            dependencies.iter().map(|p| p.move_package()),
796        )?;
797
798        context.linkage_view.set_linkage(&package)?;
799        let res = publish_and_verify_modules(context, runtime_id, &modules);
800        context.linkage_view.reset_linkage()?;
801        res?;
802
803        check_compatibility(
804            context.protocol_config,
805            current_package.move_package(),
806            &modules,
807            upgrade_ticket.policy,
808        )?;
809        if context.protocol_config.check_for_init_during_upgrade() {
810            // find newly added modules to the package,
811            // and error if they have init functions
812            let current_module_names: BTreeSet<&str> = current_package
813                .move_package()
814                .serialized_module_map()
815                .keys()
816                .map(|s| s.as_str())
817                .collect();
818            let upgrade_module_names: BTreeSet<&str> = package
819                .serialized_module_map()
820                .keys()
821                .map(|s| s.as_str())
822                .collect();
823            let new_module_names = upgrade_module_names
824                .difference(&current_module_names)
825                .copied()
826                .collect::<BTreeSet<&str>>();
827            let new_modules = modules
828                .iter()
829                .filter(|m| {
830                    let name = m.identifier_at(m.self_handle().name).as_str();
831                    new_module_names.contains(name)
832                })
833                .collect::<Vec<&CompiledModule>>();
834            let new_module_has_init = new_modules.iter().any(|module| {
835                module.function_defs.iter().any(|fdef| {
836                    let fhandle = module.function_handle_at(fdef.function);
837                    let fname = module.identifier_at(fhandle.name);
838                    fname == INIT_FN_NAME
839                })
840            });
841            if new_module_has_init {
842                // TODO we cannot run 'init' on upgrade yet due to global type cache limitations
843                return Err(ExecutionError::new_with_source(
844                    ExecutionErrorKind::FeatureNotYetSupported,
845                    "`init` in new modules on upgrade is not yet supported",
846                ));
847            }
848        }
849
850        context.write_package(package);
851        Ok(vec![Value::Raw(
852            RawValueType::Loaded {
853                ty: upgrade_receipt_type,
854                abilities: AbilitySet::EMPTY,
855                used_in_non_entry_move_call: false,
856            },
857            bcs::to_bytes(&UpgradeReceipt::new(upgrade_ticket, storage_id)).unwrap(),
858        )])
859    }
860
861    pub fn check_compatibility(
862        protocol_config: &ProtocolConfig,
863        existing_package: &MovePackage,
864        upgrading_modules: &[CompiledModule],
865        policy: u8,
866    ) -> Result<(), ExecutionError> {
867        // Make sure this is a known upgrade policy.
868        let Ok(policy) = UpgradePolicy::try_from(policy) else {
869            return Err(ExecutionError::from_kind(
870                ExecutionErrorKind::PackageUpgradeError {
871                    upgrade_error: PackageUpgradeError::UnknownUpgradePolicy { policy },
872                },
873            ));
874        };
875
876        let pool = &mut normalized::RcPool::new();
877        let binary_config = protocol_config.binary_config(None);
878        let Ok(current_normalized) =
879            existing_package.normalize(pool, &binary_config, /* include code */ true)
880        else {
881            invariant_violation!("Tried to normalize modules in existing package but failed")
882        };
883
884        let existing_modules_len = current_normalized.len();
885        let upgrading_modules_len = upgrading_modules.len();
886        let disallow_new_modules = protocol_config.disallow_new_modules_in_deps_only_packages()
887            && policy as u8 == UpgradePolicy::DEP_ONLY;
888
889        if disallow_new_modules && existing_modules_len != upgrading_modules_len {
890            return Err(ExecutionError::new_with_source(
891                ExecutionErrorKind::PackageUpgradeError {
892                    upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
893                },
894                format!(
895                    "Existing package has {existing_modules_len} modules, but new package has \
896                     {upgrading_modules_len}. Adding or removing a module to a deps only package is not allowed."
897                ),
898            ));
899        }
900
901        let mut new_normalized = normalize_deserialized_modules(
902            pool,
903            upgrading_modules.iter(),
904            /* include code */ true,
905        );
906        for (name, cur_module) in current_normalized {
907            let Some(new_module) = new_normalized.remove(&name) else {
908                return Err(ExecutionError::new_with_source(
909                    ExecutionErrorKind::PackageUpgradeError {
910                        upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
911                    },
912                    format!("Existing module {name} not found in next version of package"),
913                ));
914            };
915
916            check_module_compatibility(&policy, &cur_module, &new_module)?;
917        }
918
919        // If we disallow new modules double check that there are no modules left in `new_normalized`.
920        debug_assert!(!disallow_new_modules || new_normalized.is_empty());
921
922        Ok(())
923    }
924
925    fn check_module_compatibility(
926        policy: &UpgradePolicy,
927        cur_module: &move_binary_format::compatibility::Module,
928        new_module: &move_binary_format::compatibility::Module,
929    ) -> Result<(), ExecutionError> {
930        match policy {
931            UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module),
932            UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module),
933            UpgradePolicy::Compatible => {
934                let compatibility = Compatibility::upgrade_check();
935
936                compatibility.check(cur_module, new_module)
937            }
938        }
939        .map_err(|e| {
940            ExecutionError::new_with_source(
941                ExecutionErrorKind::PackageUpgradeError {
942                    upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
943                },
944                e,
945            )
946        })
947    }
948
949    pub fn fetch_package(
950        state_view: &impl BackingPackageStore,
951        package_id: &ObjectID,
952    ) -> Result<PackageObject, ExecutionError> {
953        let mut fetched_packages = fetch_packages(state_view, vec![package_id])?;
954        assert_invariant!(
955            fetched_packages.len() == 1,
956            "Number of fetched packages must match the number of package object IDs if successful."
957        );
958        match fetched_packages.pop() {
959            Some(pkg) => Ok(pkg),
960            None => invariant_violation!(
961                "We should always fetch a package for each object or return a dependency error."
962            ),
963        }
964    }
965
966    pub fn fetch_packages<'ctx, 'state>(
967        state_view: &'state impl BackingPackageStore,
968        package_ids: impl IntoIterator<Item = &'ctx ObjectID>,
969    ) -> Result<Vec<PackageObject>, ExecutionError> {
970        let package_ids: BTreeSet<_> = package_ids.into_iter().collect();
971        match get_package_objects(state_view, package_ids) {
972            Err(e) => Err(ExecutionError::new_with_source(
973                ExecutionErrorKind::PublishUpgradeMissingDependency,
974                e,
975            )),
976            Ok(Err(missing_deps)) => {
977                let msg = format!(
978                    "Missing dependencies: {}",
979                    missing_deps
980                        .into_iter()
981                        .map(|dep| format!("{}", dep))
982                        .collect::<Vec<_>>()
983                        .join(", ")
984                );
985                Err(ExecutionError::new_with_source(
986                    ExecutionErrorKind::PublishUpgradeMissingDependency,
987                    msg,
988                ))
989            }
990            Ok(Ok(pkgs)) => Ok(pkgs),
991        }
992    }
993
994    /***************************************************************************************************
995     * Move execution
996     **************************************************************************************************/
997
998    fn vm_move_call(
999        context: &mut ExecutionContext<'_, '_, '_>,
1000        module_id: &ModuleId,
1001        function: &IdentStr,
1002        type_arguments: Vec<Type>,
1003        tx_context_kind: TxContextKind,
1004        mut serialized_arguments: Vec<Vec<u8>>,
1005        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1006    ) -> Result<SerializedReturnValues, ExecutionError> {
1007        match tx_context_kind {
1008            TxContextKind::None => (),
1009            TxContextKind::Mutable | TxContextKind::Immutable => {
1010                serialized_arguments.push(context.tx_context.borrow().to_bcs_legacy_context());
1011            }
1012        }
1013        // script visibility checked manually for entry points
1014        let mut result = context
1015            .execute_function_bypass_visibility(
1016                module_id,
1017                function,
1018                type_arguments,
1019                serialized_arguments,
1020                trace_builder_opt,
1021            )
1022            .map_err(|e| context.convert_vm_error(e))?;
1023
1024        // When this function is used during publishing, it
1025        // may be executed several times, with objects being
1026        // created in the Move VM in each Move call. In such
1027        // case, we need to update TxContext value so that it
1028        // reflects what happened each time we call into the
1029        // Move VM (e.g. to account for the number of created
1030        // objects).
1031        if tx_context_kind == TxContextKind::Mutable {
1032            let Some((_, ctx_bytes, _)) = result.mutable_reference_outputs.pop() else {
1033                invariant_violation!("Missing TxContext in reference outputs");
1034            };
1035            let updated_ctx: MoveLegacyTxContext = bcs::from_bytes(&ctx_bytes).map_err(|e| {
1036                ExecutionError::invariant_violation(format!(
1037                    "Unable to deserialize TxContext bytes. {e}"
1038                ))
1039            })?;
1040            context.tx_context.borrow_mut().update_state(updated_ctx)?;
1041        }
1042        Ok(result)
1043    }
1044
1045    fn publish_and_verify_modules(
1046        context: &mut ExecutionContext<'_, '_, '_>,
1047        package_id: ObjectID,
1048        modules: &[CompiledModule],
1049    ) -> Result<(), ExecutionError> {
1050        // 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>`
1051        let binary_version = context.protocol_config.move_binary_format_version();
1052        let new_module_bytes: Vec<_> = modules
1053            .iter()
1054            .map(|m| {
1055                let mut bytes = Vec::new();
1056                let version = if binary_version > VERSION_6 {
1057                    m.version
1058                } else {
1059                    VERSION_6
1060                };
1061                m.serialize_with_version(version, &mut bytes).unwrap();
1062                bytes
1063            })
1064            .collect();
1065        context
1066            .publish_module_bundle(new_module_bytes, AccountAddress::from(package_id))
1067            .map_err(|e| context.convert_vm_error(e))?;
1068
1069        // run the Sui verifier
1070        for module in modules {
1071            // Run Sui bytecode verifier, which runs some additional checks that assume the Move
1072            // bytecode verifier has passed.
1073            sui_verifier::verifier::sui_verify_module_unmetered(
1074                module,
1075                &BTreeMap::new(),
1076                &context
1077                    .protocol_config
1078                    .verifier_config(/* signing_limits */ None),
1079            )?;
1080        }
1081
1082        Ok(())
1083    }
1084
1085    fn init_modules<'a, Mode: ExecutionMode>(
1086        context: &mut ExecutionContext<'_, '_, '_>,
1087        argument_updates: &mut Mode::ArgumentUpdates,
1088        modules: impl IntoIterator<Item = &'a CompiledModule>,
1089        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1090    ) -> Result<(), ExecutionError> {
1091        let modules_to_init = modules.into_iter().filter_map(|module| {
1092            for fdef in &module.function_defs {
1093                let fhandle = module.function_handle_at(fdef.function);
1094                let fname = module.identifier_at(fhandle.name);
1095                if fname == INIT_FN_NAME {
1096                    return Some(module.self_id());
1097                }
1098            }
1099            None
1100        });
1101
1102        for module_id in modules_to_init {
1103            trace_utils::trace_move_call_start(trace_builder_opt);
1104            let return_values = execute_move_call::<Mode>(
1105                context,
1106                argument_updates,
1107                // `init` is currently only called on packages when they are published for the
1108                // first time, meaning their runtime and storage IDs match. If this were to change
1109                // for some reason, then we would need to perform relocation here.
1110                &module_id,
1111                &module_id,
1112                INIT_FN_NAME,
1113                vec![],
1114                vec![],
1115                /* is_init */ true,
1116                trace_builder_opt,
1117            )?;
1118
1119            assert_invariant!(
1120                return_values.is_empty(),
1121                "init should not have return values"
1122            );
1123
1124            trace_utils::trace_move_call_end(trace_builder_opt);
1125        }
1126
1127        Ok(())
1128    }
1129
1130    /***************************************************************************************************
1131     * Move signatures
1132     **************************************************************************************************/
1133
1134    /// Helper marking what function we are invoking
1135    #[derive(PartialEq, Eq, Clone, Copy)]
1136    enum FunctionKind {
1137        PrivateEntry,
1138        PublicEntry,
1139        NonEntry,
1140        Init,
1141    }
1142
1143    /// Used to remember type information about a type when resolving the signature
1144    enum ValueKind {
1145        Object {
1146            type_: MoveObjectType,
1147            has_public_transfer: bool,
1148        },
1149        Raw(Type, AbilitySet),
1150    }
1151
1152    struct LoadedFunctionInfo {
1153        /// The kind of the function, e.g. public or private or init
1154        kind: FunctionKind,
1155        /// The signature information of the function
1156        signature: LoadedFunctionInstantiation,
1157        /// Object or type information for the return values
1158        return_value_kinds: Vec<ValueKind>,
1159        /// Definition index of the function
1160        index: FunctionDefinitionIndex,
1161        /// The length of the function used for setting error information, or 0 if native
1162        last_instr: CodeOffset,
1163    }
1164
1165    /// Checks that the function to be called is either
1166    /// - an entry function
1167    /// - a public function that does not return references
1168    /// - module init (only internal usage)
1169    fn check_visibility_and_signature<Mode: ExecutionMode>(
1170        context: &mut ExecutionContext<'_, '_, '_>,
1171        module_id: &ModuleId,
1172        function: &IdentStr,
1173        type_arguments: &[Type],
1174        from_init: bool,
1175    ) -> Result<LoadedFunctionInfo, ExecutionError> {
1176        if from_init {
1177            let result = context.load_function(module_id, function, type_arguments);
1178            assert_invariant!(
1179                result.is_ok(),
1180                "The modules init should be able to be loaded"
1181            );
1182        }
1183        let no_new_packages = vec![];
1184        let data_store = SuiDataStore::new(&context.linkage_view, &no_new_packages);
1185        let module = context
1186            .vm
1187            .get_runtime()
1188            .load_module(module_id, &data_store)
1189            .map_err(|e| context.convert_vm_error(e))?;
1190        let Some((index, fdef)) = module
1191            .function_defs
1192            .iter()
1193            .enumerate()
1194            .find(|(_index, fdef)| {
1195                module.identifier_at(module.function_handle_at(fdef.function).name) == function
1196            })
1197        else {
1198            return Err(ExecutionError::new_with_source(
1199                ExecutionErrorKind::FunctionNotFound,
1200                format!(
1201                    "Could not resolve function '{}' in module {}",
1202                    function, &module_id,
1203                ),
1204            ));
1205        };
1206
1207        // entry on init is now banned, so ban invoking it
1208        if !from_init && function == INIT_FN_NAME && context.protocol_config.ban_entry_init() {
1209            return Err(ExecutionError::new_with_source(
1210                ExecutionErrorKind::NonEntryFunctionInvoked,
1211                "Cannot call 'init'",
1212            ));
1213        }
1214
1215        let last_instr: CodeOffset = fdef
1216            .code
1217            .as_ref()
1218            .map(|code| code.code.len() - 1)
1219            .unwrap_or(0) as CodeOffset;
1220        let function_kind = match (fdef.visibility, fdef.is_entry) {
1221            (Visibility::Private | Visibility::Friend, true) => FunctionKind::PrivateEntry,
1222            (Visibility::Public, true) => FunctionKind::PublicEntry,
1223            (Visibility::Public, false) => FunctionKind::NonEntry,
1224            (Visibility::Private, false) if from_init => {
1225                assert_invariant!(
1226                    function == INIT_FN_NAME,
1227                    "module init specified non-init function"
1228                );
1229                FunctionKind::Init
1230            }
1231            (Visibility::Private | Visibility::Friend, false)
1232                if Mode::allow_arbitrary_function_calls() =>
1233            {
1234                FunctionKind::NonEntry
1235            }
1236            (Visibility::Private | Visibility::Friend, false) => {
1237                return Err(ExecutionError::new_with_source(
1238                    ExecutionErrorKind::NonEntryFunctionInvoked,
1239                    "Can only call `entry` or `public` functions",
1240                ));
1241            }
1242        };
1243        let signature = context
1244            .load_function(module_id, function, type_arguments)
1245            .map_err(|e| context.convert_vm_error(e))?;
1246        let signature =
1247            subst_signature(signature, type_arguments).map_err(|e| context.convert_vm_error(e))?;
1248        let return_value_kinds = match function_kind {
1249            FunctionKind::Init => {
1250                assert_invariant!(
1251                    signature.return_.is_empty(),
1252                    "init functions must have no return values"
1253                );
1254                vec![]
1255            }
1256            FunctionKind::PrivateEntry | FunctionKind::PublicEntry | FunctionKind::NonEntry => {
1257                check_non_entry_signature::<Mode>(context, module_id, function, &signature)?
1258            }
1259        };
1260        if context.protocol_config.private_generics_verifier_v2() {
1261            check_private_generics_v2(module_id, function)?;
1262        } else {
1263            check_private_generics(module_id, function)?;
1264        }
1265        Ok(LoadedFunctionInfo {
1266            kind: function_kind,
1267            signature,
1268            return_value_kinds,
1269            index: FunctionDefinitionIndex(index as u16),
1270            last_instr,
1271        })
1272    }
1273
1274    /// substitutes the type arguments into the parameter and return types
1275    pub fn subst_signature(
1276        signature: LoadedFunctionInstantiation,
1277        type_arguments: &[Type],
1278    ) -> VMResult<LoadedFunctionInstantiation> {
1279        let LoadedFunctionInstantiation {
1280            parameters,
1281            return_,
1282            instruction_length,
1283            definition_index,
1284        } = signature;
1285        let parameters = parameters
1286            .into_iter()
1287            .map(|ty| ty.subst(type_arguments))
1288            .collect::<PartialVMResult<Vec<_>>>()
1289            .map_err(|err| err.finish(Location::Undefined))?;
1290        let return_ = return_
1291            .into_iter()
1292            .map(|ty| ty.subst(type_arguments))
1293            .collect::<PartialVMResult<Vec<_>>>()
1294            .map_err(|err| err.finish(Location::Undefined))?;
1295        Ok(LoadedFunctionInstantiation {
1296            parameters,
1297            return_,
1298            instruction_length,
1299            definition_index,
1300        })
1301    }
1302
1303    /// Checks that the non-entry function does not return references. And marks the return values
1304    /// as object or non-object return values
1305    fn check_non_entry_signature<Mode: ExecutionMode>(
1306        context: &mut ExecutionContext<'_, '_, '_>,
1307        _module_id: &ModuleId,
1308        _function: &IdentStr,
1309        signature: &LoadedFunctionInstantiation,
1310    ) -> Result<Vec<ValueKind>, ExecutionError> {
1311        signature
1312            .return_
1313            .iter()
1314            .enumerate()
1315            .map(|(idx, return_type)| {
1316                let return_type = match return_type {
1317                    // for dev-inspect, just dereference the value
1318                    Type::Reference(inner) | Type::MutableReference(inner)
1319                        if Mode::allow_arbitrary_values() =>
1320                    {
1321                        inner
1322                    }
1323                    Type::Reference(_) | Type::MutableReference(_) => {
1324                        return Err(ExecutionError::from_kind(
1325                            ExecutionErrorKind::InvalidPublicFunctionReturnType { idx: idx as u16 },
1326                        ));
1327                    }
1328                    t => t,
1329                };
1330                let abilities = context.get_type_abilities(return_type)?;
1331                Ok(match return_type {
1332                    Type::MutableReference(_) | Type::Reference(_) => unreachable!(),
1333                    Type::TyParam(_) => {
1334                        invariant_violation!("TyParam should have been substituted")
1335                    }
1336                    Type::Datatype(_) | Type::DatatypeInstantiation(_) if abilities.has_key() => {
1337                        let type_tag = context
1338                            .vm
1339                            .get_runtime()
1340                            .get_type_tag(return_type)
1341                            .map_err(|e| context.convert_vm_error(e))?;
1342                        let TypeTag::Struct(struct_tag) = type_tag else {
1343                            invariant_violation!("Struct type make a non struct type tag")
1344                        };
1345                        ValueKind::Object {
1346                            type_: MoveObjectType::from(*struct_tag),
1347                            has_public_transfer: abilities.has_store(),
1348                        }
1349                    }
1350                    Type::Datatype(_)
1351                    | Type::DatatypeInstantiation(_)
1352                    | Type::Bool
1353                    | Type::U8
1354                    | Type::U64
1355                    | Type::U128
1356                    | Type::Address
1357                    | Type::Signer
1358                    | Type::Vector(_)
1359                    | Type::U16
1360                    | Type::U32
1361                    | Type::U256 => ValueKind::Raw(return_type.clone(), abilities),
1362                })
1363            })
1364            .collect()
1365    }
1366
1367    pub fn check_private_generics(
1368        module_id: &ModuleId,
1369        function: &IdentStr,
1370    ) -> Result<(), ExecutionError> {
1371        let module_ident = (module_id.address(), module_id.name());
1372        if module_ident == (&SUI_FRAMEWORK_ADDRESS, EVENT_MODULE) {
1373            return Err(ExecutionError::new_with_source(
1374                ExecutionErrorKind::NonEntryFunctionInvoked,
1375                format!("Cannot directly call functions in sui::{}", EVENT_MODULE),
1376            ));
1377        }
1378
1379        if module_ident == (&SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE)
1380            && PRIVATE_TRANSFER_FUNCTIONS.contains(&function)
1381        {
1382            let msg = format!(
1383                "Cannot directly call sui::{m}::{f}. \
1384                Use the public variant instead, sui::{m}::public_{f}",
1385                m = TRANSFER_MODULE,
1386                f = function
1387            );
1388            return Err(ExecutionError::new_with_source(
1389                ExecutionErrorKind::NonEntryFunctionInvoked,
1390                msg,
1391            ));
1392        }
1393
1394        Ok(())
1395    }
1396
1397    pub fn check_private_generics_v2(
1398        callee_package: &ModuleId,
1399        callee_function: &IdentStr,
1400    ) -> Result<(), ExecutionError> {
1401        let callee_address = *callee_package.address();
1402        let callee_module = callee_package.name();
1403        let callee = (callee_address, callee_module, callee_function);
1404        let Some((_f, internal_type_parameters)) = private_generics_verifier_v2::FUNCTIONS_TO_CHECK
1405            .iter()
1406            .find(|(f, _)| &callee == f)
1407        else {
1408            return Ok(());
1409        };
1410        // If we find an internal type parameter, the call is automatically invalid--since we
1411        // are not in a module and cannot define any types to satisfy the internal constraint.
1412        let Some((internal_idx, _)) = internal_type_parameters
1413            .iter()
1414            .enumerate()
1415            .find(|(_, is_internal)| **is_internal)
1416        else {
1417            // No `internal` type parameters, so it is ok to call
1418            return Ok(());
1419        };
1420        let callee_package_name =
1421            private_generics_verifier_v2::callee_package_name(&callee_address);
1422        let help = private_generics_verifier_v2::help_message(
1423            &callee_address,
1424            callee_module,
1425            callee_function,
1426        );
1427        let msg = format!(
1428            "Cannot directly call function '{}::{}::{}' since type parameter #{} can \
1429                 only be instantiated with types defined within the caller's module.{}",
1430            callee_package_name, callee_module, callee_function, internal_idx, help,
1431        );
1432        Err(ExecutionError::new_with_source(
1433            ExecutionErrorKind::NonEntryFunctionInvoked,
1434            msg,
1435        ))
1436    }
1437
1438    type ArgInfo = (
1439        TxContextKind,
1440        /* mut ref */
1441        Vec<(LocalIndex, ValueKind)>,
1442        Vec<Vec<u8>>,
1443    );
1444
1445    /// Serializes the arguments into BCS values for Move. Performs the necessary type checking for
1446    /// each value
1447    fn build_move_args<Mode: ExecutionMode>(
1448        context: &mut ExecutionContext<'_, '_, '_>,
1449        _module_id: &ModuleId,
1450        function: &IdentStr,
1451        function_kind: FunctionKind,
1452        signature: &LoadedFunctionInstantiation,
1453        args: &[Arg],
1454    ) -> Result<ArgInfo, ExecutionError> {
1455        // check the arity
1456        let parameters = &signature.parameters;
1457        let tx_ctx_kind = match parameters.last() {
1458            Some(t) => is_tx_context(context, t)?,
1459            None => TxContextKind::None,
1460        };
1461        // an init function can have one or two arguments, with the last one always being of type
1462        // &mut TxContext and the additional (first) one representing a one time witness type (see
1463        // one_time_witness verifier pass for additional explanation)
1464        let has_one_time_witness = function_kind == FunctionKind::Init && parameters.len() == 2;
1465        let has_tx_context = tx_ctx_kind != TxContextKind::None;
1466        let num_args = args.len() + (has_one_time_witness as usize) + (has_tx_context as usize);
1467        if num_args != parameters.len() {
1468            return Err(ExecutionError::new_with_source(
1469                ExecutionErrorKind::ArityMismatch,
1470                format!(
1471                    "Expected {:?} argument{} calling function '{}', but found {:?}",
1472                    parameters.len(),
1473                    if parameters.len() == 1 { "" } else { "s" },
1474                    function,
1475                    num_args
1476                ),
1477            ));
1478        }
1479
1480        // check the types and remember which are by mutable ref
1481        let mut by_mut_ref = vec![];
1482        let mut serialized_args = Vec::with_capacity(num_args);
1483        // an init function can have one or two arguments, with the last one always being of type
1484        // &mut TxContext and the additional (first) one representing a one time witness type (see
1485        // one_time_witness verifier pass for additional explanation)
1486        if has_one_time_witness {
1487            // one time witness type is a struct with a single bool filed which in bcs is encoded as
1488            // 0x01
1489            let bcs_true_value = bcs::to_bytes(&true).unwrap();
1490            serialized_args.push(bcs_true_value)
1491        }
1492        for ((idx, arg), param_ty) in args.iter().copied().enumerate().zip(parameters) {
1493            let (value, non_ref_param_ty): (Value, &Type) = match param_ty {
1494                Type::MutableReference(inner) => {
1495                    let value = context.borrow_arg_mut(idx, arg)?;
1496                    let object_info = if let Value::Object(ObjectValue {
1497                        type_,
1498                        has_public_transfer,
1499                        ..
1500                    }) = &value
1501                    {
1502                        let type_tag = context
1503                            .vm
1504                            .get_runtime()
1505                            .get_type_tag(type_)
1506                            .map_err(|e| context.convert_vm_error(e))?;
1507                        let TypeTag::Struct(struct_tag) = type_tag else {
1508                            invariant_violation!("Struct type make a non struct type tag")
1509                        };
1510                        let type_ = (*struct_tag).into();
1511                        ValueKind::Object {
1512                            type_,
1513                            has_public_transfer: *has_public_transfer,
1514                        }
1515                    } else {
1516                        let abilities = context.get_type_abilities(inner)?;
1517                        ValueKind::Raw((**inner).clone(), abilities)
1518                    };
1519                    by_mut_ref.push((idx as LocalIndex, object_info));
1520                    (value, inner)
1521                }
1522                Type::Reference(inner) => (context.borrow_arg(idx, arg, param_ty)?, inner),
1523                t => {
1524                    let value = context.by_value_arg(CommandKind::MoveCall, idx, arg)?;
1525                    (value, t)
1526                }
1527            };
1528            if matches!(
1529                function_kind,
1530                FunctionKind::PrivateEntry | FunctionKind::Init
1531            ) && value.was_used_in_non_entry_move_call()
1532            {
1533                return Err(command_argument_error(
1534                    CommandArgumentError::InvalidArgumentToPrivateEntryFunction,
1535                    idx,
1536                ));
1537            }
1538            check_param_type::<Mode>(context, idx, &value, non_ref_param_ty)?;
1539            let bytes = {
1540                let mut v = vec![];
1541                value.write_bcs_bytes(&mut v, None)?;
1542                v
1543            };
1544            serialized_args.push(bytes);
1545        }
1546        Ok((tx_ctx_kind, by_mut_ref, serialized_args))
1547    }
1548
1549    /// checks that the value is compatible with the specified type
1550    fn check_param_type<Mode: ExecutionMode>(
1551        context: &mut ExecutionContext<'_, '_, '_>,
1552        idx: usize,
1553        value: &Value,
1554        param_ty: &Type,
1555    ) -> Result<(), ExecutionError> {
1556        match value {
1557            // For dev-spect, allow any BCS bytes. This does mean internal invariants for types can
1558            // be violated (like for string or Option)
1559            Value::Raw(RawValueType::Any, bytes) if Mode::allow_arbitrary_values() => {
1560                if let Some(bound) = amplification_bound_::<Mode>(context, param_ty)? {
1561                    let bound = context.size_bound_raw(bound);
1562                    return ensure_serialized_size(bytes.len() as u64, bound);
1563                } else {
1564                    return Ok(());
1565                }
1566            }
1567            // Any means this was just some bytes passed in as an argument (as opposed to being
1568            // generated from a Move function). Meaning we only allow "primitive" values
1569            // and might need to run validation in addition to the BCS layout
1570            Value::Raw(RawValueType::Any, bytes) => {
1571                let Some(layout) = primitive_serialization_layout(context, param_ty)? else {
1572                    let msg = format!(
1573                        "Non-primitive argument at index {}. If it is an object, it must be \
1574                        populated by an object",
1575                        idx,
1576                    );
1577                    return Err(ExecutionError::new_with_source(
1578                        ExecutionErrorKind::command_argument_error(
1579                            CommandArgumentError::InvalidUsageOfPureArg,
1580                            idx as u16,
1581                        ),
1582                        msg,
1583                    ));
1584                };
1585                bcs_argument_validate(bytes, idx as u16, layout)?;
1586                return Ok(());
1587            }
1588            Value::Raw(RawValueType::Loaded { ty, abilities, .. }, _) => {
1589                assert_invariant!(
1590                    Mode::allow_arbitrary_values() || !abilities.has_key(),
1591                    "Raw value should never be an object"
1592                );
1593                if ty != param_ty {
1594                    return Err(command_argument_error(
1595                        CommandArgumentError::TypeMismatch,
1596                        idx,
1597                    ));
1598                }
1599            }
1600            Value::Object(obj) => {
1601                let ty = &obj.type_;
1602                if ty != param_ty {
1603                    return Err(command_argument_error(
1604                        CommandArgumentError::TypeMismatch,
1605                        idx,
1606                    ));
1607                }
1608            }
1609            Value::Receiving(_, _, assigned_type) => {
1610                // If the type has been fixed, make sure the types match up
1611                if let Some(assigned_type) = assigned_type
1612                    && assigned_type != param_ty
1613                {
1614                    return Err(command_argument_error(
1615                        CommandArgumentError::TypeMismatch,
1616                        idx,
1617                    ));
1618                }
1619
1620                // Now make sure the param type is a struct instantiation of the receiving struct
1621                let Type::DatatypeInstantiation(inst) = param_ty else {
1622                    return Err(command_argument_error(
1623                        CommandArgumentError::TypeMismatch,
1624                        idx,
1625                    ));
1626                };
1627                let (sidx, targs) = &**inst;
1628                let Some(s) = context.vm.get_runtime().get_type(*sidx) else {
1629                    invariant_violation!("sui::transfer::Receiving struct not found in session")
1630                };
1631                let resolved_struct = get_datatype_ident(&s);
1632
1633                if resolved_struct != RESOLVED_RECEIVING_STRUCT || targs.len() != 1 {
1634                    return Err(command_argument_error(
1635                        CommandArgumentError::TypeMismatch,
1636                        idx,
1637                    ));
1638                }
1639            }
1640        }
1641        Ok(())
1642    }
1643
1644    fn to_identifier(
1645        context: &mut ExecutionContext<'_, '_, '_>,
1646        ident: String,
1647    ) -> Result<Identifier, ExecutionError> {
1648        if context.protocol_config.validate_identifier_inputs() {
1649            Identifier::new(ident).map_err(|e| {
1650                ExecutionError::new_with_source(
1651                    ExecutionErrorKind::VMInvariantViolation,
1652                    e.to_string(),
1653                )
1654            })
1655        } else {
1656            // SAFETY: Preserving existing behaviour for identifier deserialization.
1657            Ok(unsafe { Identifier::new_unchecked(ident) })
1658        }
1659    }
1660
1661    // Convert a type input which may refer to a type by multiple different IDs and convert it to a
1662    // TypeTag that only uses defining IDs.
1663    //
1664    // It's suboptimal to traverse the type, load, and then go back to a typetag to resolve to
1665    // defining IDs in the typetag, but it's the cleanest solution ATM without adding in additional
1666    // machinery. With the new linkage resolution that we will be adding this will
1667    // be much cleaner however, we'll hold off on adding that in here, and instead add it in the
1668    // new execution code.
1669    fn to_type_tag(
1670        context: &mut ExecutionContext<'_, '_, '_>,
1671        type_input: TypeInput,
1672        idx: usize,
1673    ) -> Result<TypeTag, ExecutionError> {
1674        let type_tag_no_def_ids = to_type_tag_(context, type_input, idx)?;
1675        if context
1676            .protocol_config
1677            .resolve_type_input_ids_to_defining_id()
1678        {
1679            let ix = if context
1680                .protocol_config
1681                .better_adapter_type_resolution_errors()
1682            {
1683                idx
1684            } else {
1685                0
1686            };
1687
1688            let ty = context
1689                .load_type(&type_tag_no_def_ids)
1690                .map_err(|e| context.convert_type_argument_error(ix, e))?;
1691            context.get_type_tag(&ty)
1692        } else {
1693            Ok(type_tag_no_def_ids)
1694        }
1695    }
1696
1697    fn to_type_tag_(
1698        context: &mut ExecutionContext<'_, '_, '_>,
1699        type_input: TypeInput,
1700        idx: usize,
1701    ) -> Result<TypeTag, ExecutionError> {
1702        use TypeInput as I;
1703        use TypeTag as T;
1704        Ok(match type_input {
1705            I::Bool => T::Bool,
1706            I::U8 => T::U8,
1707            I::U16 => T::U16,
1708            I::U32 => T::U32,
1709            I::U64 => T::U64,
1710            I::U128 => T::U128,
1711            I::U256 => T::U256,
1712            I::Address => T::Address,
1713            I::Signer => T::Signer,
1714            I::Vector(t) => T::Vector(Box::new(to_type_tag_(context, *t, idx)?)),
1715            I::Struct(s) => {
1716                let StructInput {
1717                    address,
1718                    module,
1719                    name,
1720                    type_params,
1721                } = *s;
1722                let type_params = type_params
1723                    .into_iter()
1724                    .map(|t| to_type_tag_(context, t, idx))
1725                    .collect::<Result<_, _>>()?;
1726                let (module, name) = resolve_datatype_names(context, address, module, name, idx)?;
1727                T::Struct(Box::new(StructTag {
1728                    address,
1729                    module,
1730                    name,
1731                    type_params,
1732                }))
1733            }
1734        })
1735    }
1736
1737    fn resolve_datatype_names(
1738        context: &ExecutionContext<'_, '_, '_>,
1739        addr: AccountAddress,
1740        module: String,
1741        name: String,
1742        idx: usize,
1743    ) -> Result<(Identifier, Identifier), ExecutionError> {
1744        let validate_identifiers = context.protocol_config.validate_identifier_inputs();
1745        let better_resolution_errors = context
1746            .protocol_config
1747            .better_adapter_type_resolution_errors();
1748
1749        let to_ident = |s| {
1750            if validate_identifiers {
1751                Identifier::new(s).map_err(|e| {
1752                    ExecutionError::new_with_source(
1753                        ExecutionErrorKind::VMInvariantViolation,
1754                        e.to_string(),
1755                    )
1756                })
1757            } else {
1758                // SAFETY: Preserving existing behaviour for identifier deserialization within type
1759                // tags and inputs.
1760                unsafe { Ok(Identifier::new_unchecked(s)) }
1761            }
1762        };
1763
1764        let module_ident = to_ident(module.clone())?;
1765        let name_ident = to_ident(name.clone())?;
1766
1767        if better_resolution_errors
1768            && context
1769                .linkage_view
1770                .get_package(&addr.into())
1771                .ok()
1772                .flatten()
1773                .is_none_or(|pkg| !pkg.type_origin_map().contains_key(&(module, name)))
1774        {
1775            return Err(ExecutionError::from_kind(
1776                ExecutionErrorKind::TypeArgumentError {
1777                    argument_idx: idx as u16,
1778                    kind: TypeArgumentError::TypeNotFound,
1779                },
1780            ));
1781        }
1782
1783        Ok((module_ident, name_ident))
1784    }
1785
1786    fn get_datatype_ident(s: &CachedDatatype) -> (&AccountAddress, &IdentStr, &IdentStr) {
1787        let module_id = &s.defining_id;
1788        let struct_name = &s.name;
1789        (
1790            module_id.address(),
1791            module_id.name(),
1792            struct_name.as_ident_str(),
1793        )
1794    }
1795
1796    // Returns Some(kind) if the type is a reference to the TxnContext. kind being Mutable with
1797    // a MutableReference, and Immutable otherwise.
1798    // Returns None for all other types
1799    pub fn is_tx_context(
1800        context: &mut ExecutionContext<'_, '_, '_>,
1801        t: &Type,
1802    ) -> Result<TxContextKind, ExecutionError> {
1803        let (is_mut, inner) = match t {
1804            Type::MutableReference(inner) => (true, inner),
1805            Type::Reference(inner) => (false, inner),
1806            _ => return Ok(TxContextKind::None),
1807        };
1808        let Type::Datatype(idx) = &**inner else {
1809            return Ok(TxContextKind::None);
1810        };
1811        let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1812            invariant_violation!("Loaded struct not found")
1813        };
1814        let (module_addr, module_name, struct_name) = get_datatype_ident(&s);
1815        let is_tx_context_type = module_addr == &SUI_FRAMEWORK_ADDRESS
1816            && module_name == TX_CONTEXT_MODULE_NAME
1817            && struct_name == TX_CONTEXT_STRUCT_NAME;
1818        Ok(if is_tx_context_type {
1819            if is_mut {
1820                TxContextKind::Mutable
1821            } else {
1822                TxContextKind::Immutable
1823            }
1824        } else {
1825            TxContextKind::None
1826        })
1827    }
1828
1829    /// Returns Some(layout) iff it is a primitive, an ID, a String, or an option/vector of a valid type
1830    fn primitive_serialization_layout(
1831        context: &mut ExecutionContext<'_, '_, '_>,
1832        param_ty: &Type,
1833    ) -> Result<Option<PrimitiveArgumentLayout>, ExecutionError> {
1834        Ok(match param_ty {
1835            Type::Signer => return Ok(None),
1836            Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => {
1837                invariant_violation!("references and type parameters should be checked elsewhere")
1838            }
1839            Type::Bool => Some(PrimitiveArgumentLayout::Bool),
1840            Type::U8 => Some(PrimitiveArgumentLayout::U8),
1841            Type::U16 => Some(PrimitiveArgumentLayout::U16),
1842            Type::U32 => Some(PrimitiveArgumentLayout::U32),
1843            Type::U64 => Some(PrimitiveArgumentLayout::U64),
1844            Type::U128 => Some(PrimitiveArgumentLayout::U128),
1845            Type::U256 => Some(PrimitiveArgumentLayout::U256),
1846            Type::Address => Some(PrimitiveArgumentLayout::Address),
1847
1848            Type::Vector(inner) => {
1849                let info_opt = primitive_serialization_layout(context, inner)?;
1850                info_opt.map(|layout| PrimitiveArgumentLayout::Vector(Box::new(layout)))
1851            }
1852            Type::DatatypeInstantiation(inst) => {
1853                let (idx, targs) = &**inst;
1854                let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1855                    invariant_violation!("Loaded struct not found")
1856                };
1857                let resolved_struct = get_datatype_ident(&s);
1858                // is option of a string
1859                if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
1860                    let info_opt = primitive_serialization_layout(context, &targs[0])?;
1861                    info_opt.map(|layout| PrimitiveArgumentLayout::Option(Box::new(layout)))
1862                } else {
1863                    None
1864                }
1865            }
1866            Type::Datatype(idx) => {
1867                let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1868                    invariant_violation!("Loaded struct not found")
1869                };
1870                let resolved_struct = get_datatype_ident(&s);
1871                if resolved_struct == RESOLVED_SUI_ID {
1872                    Some(PrimitiveArgumentLayout::Address)
1873                } else if resolved_struct == RESOLVED_ASCII_STR {
1874                    Some(PrimitiveArgumentLayout::Ascii)
1875                } else if resolved_struct == RESOLVED_UTF8_STR {
1876                    Some(PrimitiveArgumentLayout::UTF8)
1877                } else {
1878                    None
1879                }
1880            }
1881        })
1882    }
1883
1884    // We use a `OnceCell` for two reasons. One to cache the ability set for the type so that it
1885    // is not recomputed for each element of the vector. And two, to avoid computing the abilities
1886    // in the case where `max_ptb_value_size_v2` is false--this removes any case of diverging
1887    // based on the result of `get_type_abilities`.
1888    fn amplification_bound<Mode: ExecutionMode>(
1889        context: &mut ExecutionContext<'_, '_, '_>,
1890        param_ty: &Type,
1891        abilities: &OnceCell<AbilitySet>,
1892    ) -> Result<Option<u64>, ExecutionError> {
1893        if context.protocol_config.max_ptb_value_size_v2() {
1894            if abilities.get().is_none() {
1895                abilities
1896                    .set(context.get_type_abilities(param_ty)?)
1897                    .unwrap();
1898            }
1899            if !abilities.get().unwrap().has_copy() {
1900                return Ok(None);
1901            }
1902        }
1903        amplification_bound_::<Mode>(context, param_ty)
1904    }
1905
1906    fn amplification_bound_<Mode: ExecutionMode>(
1907        context: &mut ExecutionContext<'_, '_, '_>,
1908        param_ty: &Type,
1909    ) -> Result<Option<u64>, ExecutionError> {
1910        // Do not cap size for epoch change/genesis
1911        if Mode::packages_are_predefined() {
1912            return Ok(None);
1913        }
1914
1915        let Some(bound) = context.protocol_config.max_ptb_value_size_as_option() else {
1916            return Ok(None);
1917        };
1918
1919        fn amplification(prim_layout: &PrimitiveArgumentLayout) -> Result<u64, ExecutionError> {
1920            use PrimitiveArgumentLayout as PAL;
1921            Ok(match prim_layout {
1922                PAL::Option(inner_layout) => 1u64 + amplification(inner_layout)?,
1923                PAL::Vector(inner_layout) => amplification(inner_layout)?,
1924                PAL::Ascii | PAL::UTF8 => 2,
1925                PAL::Bool | PAL::U8 | PAL::U16 | PAL::U32 | PAL::U64 => 1,
1926                PAL::U128 | PAL::U256 | PAL::Address => 2,
1927            })
1928        }
1929
1930        let mut amplification = match primitive_serialization_layout(context, param_ty)? {
1931            // No primitive type layout was able to be determined for the type. Assume the worst
1932            // and the value is of maximal depth.
1933            None => context.protocol_config.max_move_value_depth(),
1934            Some(layout) => amplification(&layout)?,
1935        };
1936
1937        // Computed amplification should never be zero
1938        debug_assert!(amplification != 0);
1939        // We assume here that any value that can be created must be bounded by the max move value
1940        // depth so assert that this invariant holds.
1941        debug_assert!(
1942            context.protocol_config.max_move_value_depth()
1943                >= context.protocol_config.max_type_argument_depth() as u64
1944        );
1945        assert_ne!(context.protocol_config.max_move_value_depth(), 0);
1946        if amplification == 0 {
1947            amplification = context.protocol_config.max_move_value_depth();
1948        }
1949        Ok(Some(bound / amplification))
1950    }
1951
1952    /***************************************************************************************************
1953     * Special serialization formats
1954     **************************************************************************************************/
1955
1956    /// Special enum for values that need additional validation, in other words
1957    /// There is validation to do on top of the BCS layout. Currently only needed for
1958    /// strings
1959    #[derive(Debug)]
1960    pub enum PrimitiveArgumentLayout {
1961        /// An option
1962        Option(Box<PrimitiveArgumentLayout>),
1963        /// A vector
1964        Vector(Box<PrimitiveArgumentLayout>),
1965        /// An ASCII encoded string
1966        Ascii,
1967        /// A UTF8 encoded string
1968        UTF8,
1969        // needed for Option validation
1970        Bool,
1971        U8,
1972        U16,
1973        U32,
1974        U64,
1975        U128,
1976        U256,
1977        Address,
1978    }
1979
1980    impl PrimitiveArgumentLayout {
1981        /// returns true iff all BCS compatible bytes are actually values for this type.
1982        /// For example, this function returns false for Option and Strings since they need additional
1983        /// validation.
1984        pub fn bcs_only(&self) -> bool {
1985            match self {
1986                // have additional restrictions past BCS
1987                PrimitiveArgumentLayout::Option(_)
1988                | PrimitiveArgumentLayout::Ascii
1989                | PrimitiveArgumentLayout::UTF8 => false,
1990                // Move primitives are BCS compatible and do not need additional validation
1991                PrimitiveArgumentLayout::Bool
1992                | PrimitiveArgumentLayout::U8
1993                | PrimitiveArgumentLayout::U16
1994                | PrimitiveArgumentLayout::U32
1995                | PrimitiveArgumentLayout::U64
1996                | PrimitiveArgumentLayout::U128
1997                | PrimitiveArgumentLayout::U256
1998                | PrimitiveArgumentLayout::Address => true,
1999                // vector only needs validation if it's inner type does
2000                PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
2001            }
2002        }
2003    }
2004
2005    /// Checks the bytes against the `SpecialArgumentLayout` using `bcs`. It does not actually generate
2006    /// the deserialized value, only walks the bytes. While not necessary if the layout does not contain
2007    /// special arguments (e.g. Option or String) we check the BCS bytes for predictability
2008    pub fn bcs_argument_validate(
2009        bytes: &[u8],
2010        idx: u16,
2011        layout: PrimitiveArgumentLayout,
2012    ) -> Result<(), ExecutionError> {
2013        bcs::from_bytes_seed(&layout, bytes).map_err(|_| {
2014            ExecutionError::new_with_source(
2015                ExecutionErrorKind::command_argument_error(
2016                    CommandArgumentError::InvalidBCSBytes,
2017                    idx,
2018                ),
2019                format!("Function expects {layout} but provided argument's value does not match",),
2020            )
2021        })
2022    }
2023
2024    impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout {
2025        type Value = ();
2026        fn deserialize<D: serde::de::Deserializer<'d>>(
2027            self,
2028            deserializer: D,
2029        ) -> Result<Self::Value, D::Error> {
2030            use serde::de::Error;
2031            match self {
2032                PrimitiveArgumentLayout::Ascii => {
2033                    let s: &str = serde::Deserialize::deserialize(deserializer)?;
2034                    if !s.is_ascii() {
2035                        Err(D::Error::custom("not an ascii string"))
2036                    } else {
2037                        Ok(())
2038                    }
2039                }
2040                PrimitiveArgumentLayout::UTF8 => {
2041                    deserializer.deserialize_string(serde::de::IgnoredAny)?;
2042                    Ok(())
2043                }
2044                PrimitiveArgumentLayout::Option(layout) => {
2045                    deserializer.deserialize_option(OptionElementVisitor(layout))
2046                }
2047                PrimitiveArgumentLayout::Vector(layout) => {
2048                    deserializer.deserialize_seq(VectorElementVisitor(layout))
2049                }
2050                // primitive move value cases, which are hit to make sure the correct number of bytes
2051                // are removed for elements of an option/vector
2052                PrimitiveArgumentLayout::Bool => {
2053                    deserializer.deserialize_bool(serde::de::IgnoredAny)?;
2054                    Ok(())
2055                }
2056                PrimitiveArgumentLayout::U8 => {
2057                    deserializer.deserialize_u8(serde::de::IgnoredAny)?;
2058                    Ok(())
2059                }
2060                PrimitiveArgumentLayout::U16 => {
2061                    deserializer.deserialize_u16(serde::de::IgnoredAny)?;
2062                    Ok(())
2063                }
2064                PrimitiveArgumentLayout::U32 => {
2065                    deserializer.deserialize_u32(serde::de::IgnoredAny)?;
2066                    Ok(())
2067                }
2068                PrimitiveArgumentLayout::U64 => {
2069                    deserializer.deserialize_u64(serde::de::IgnoredAny)?;
2070                    Ok(())
2071                }
2072                PrimitiveArgumentLayout::U128 => {
2073                    deserializer.deserialize_u128(serde::de::IgnoredAny)?;
2074                    Ok(())
2075                }
2076                PrimitiveArgumentLayout::U256 => {
2077                    U256::deserialize(deserializer)?;
2078                    Ok(())
2079                }
2080                PrimitiveArgumentLayout::Address => {
2081                    SuiAddress::deserialize(deserializer)?;
2082                    Ok(())
2083                }
2084            }
2085        }
2086    }
2087
2088    struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2089
2090    impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> {
2091        type Value = ();
2092
2093        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2094            formatter.write_str("Vector")
2095        }
2096
2097        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
2098        where
2099            A: serde::de::SeqAccess<'d>,
2100        {
2101            while seq.next_element_seed(self.0)?.is_some() {}
2102            Ok(())
2103        }
2104    }
2105
2106    struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2107
2108    impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> {
2109        type Value = ();
2110
2111        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2112            formatter.write_str("Option")
2113        }
2114
2115        fn visit_none<E>(self) -> Result<Self::Value, E>
2116        where
2117            E: serde::de::Error,
2118        {
2119            Ok(())
2120        }
2121
2122        fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
2123        where
2124            D: serde::Deserializer<'d>,
2125        {
2126            self.0.deserialize(deserializer)
2127        }
2128    }
2129
2130    impl fmt::Display for PrimitiveArgumentLayout {
2131        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2132            match self {
2133                PrimitiveArgumentLayout::Vector(inner) => {
2134                    write!(f, "vector<{inner}>")
2135                }
2136                PrimitiveArgumentLayout::Option(inner) => {
2137                    write!(f, "std::option::Option<{inner}>")
2138                }
2139                PrimitiveArgumentLayout::Ascii => {
2140                    write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2)
2141                }
2142                PrimitiveArgumentLayout::UTF8 => {
2143                    write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2)
2144                }
2145                PrimitiveArgumentLayout::Bool => write!(f, "bool"),
2146                PrimitiveArgumentLayout::U8 => write!(f, "u8"),
2147                PrimitiveArgumentLayout::U16 => write!(f, "u16"),
2148                PrimitiveArgumentLayout::U32 => write!(f, "u32"),
2149                PrimitiveArgumentLayout::U64 => write!(f, "u64"),
2150                PrimitiveArgumentLayout::U128 => write!(f, "u128"),
2151                PrimitiveArgumentLayout::U256 => write!(f, "u256"),
2152                PrimitiveArgumentLayout::Address => write!(f, "address"),
2153            }
2154        }
2155    }
2156}