iota_adapter_latest/programmable_transactions/
execution.rs

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