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