iota_move_natives_latest/
types.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::VecDeque;
6
7use move_binary_format::errors::PartialVMResult;
8use move_core_types::{
9    gas_algebra::InternalGas,
10    language_storage::TypeTag,
11    runtime_value::{MoveStructLayout, MoveTypeLayout},
12};
13use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
14use move_vm_types::{
15    loaded_data::runtime_types::Type, natives::function::NativeResult, values::Value,
16};
17use smallvec::smallvec;
18
19use crate::NativesCostTable;
20
21pub(crate) fn is_otw_struct(
22    struct_layout: &MoveStructLayout,
23    type_tag: &TypeTag,
24    hardened_check: bool,
25) -> bool {
26    let has_one_bool_field = matches!(struct_layout.0.as_slice(), [MoveTypeLayout::Bool]);
27
28    // If a struct type has the same name as the module that defines it but
29    // capitalized, and it has a single field of type bool, it means that it's a
30    // one-time witness type. The remaining properties of a one-time witness
31    // type are checked in the one_time_witness_verifier pass in
32    // the IOTA bytecode verifier (a type with this name and with a single bool
33    // field that does not have all the remaining properties of a one-time
34    // witness type will cause a verifier error).
35    matches!(
36        type_tag,
37        TypeTag::Struct(struct_tag) if
38        has_one_bool_field &&
39        struct_tag.name.to_string() == struct_tag.module.to_string().to_ascii_uppercase() &&
40        // hardened check ==> no generic types
41        (!hardened_check || struct_tag.type_params.is_empty())
42    )
43}
44
45#[derive(Clone)]
46pub struct TypesIsOneTimeWitnessCostParams {
47    pub types_is_one_time_witness_cost_base: InternalGas,
48    pub types_is_one_time_witness_type_tag_cost_per_byte: InternalGas,
49    pub types_is_one_time_witness_type_cost_per_byte: InternalGas,
50}
51/// ****************************************************************************
52/// ********************* native fun is_one_time_witness
53/// Implementation of the Move native function `is_one_time_witness<T: drop>(_:
54/// &T): bool`   gas cost: types_is_one_time_witness_cost_base
55/// | base cost as this can be expensive oper
56///              + types_is_one_time_witness_type_tag_cost_per_byte *
57///                type_tag.size()        | cost per byte of converting type to
58///                type tag
59///              + types_is_one_time_witness_type_cost_per_byte * ty.size() |
60///                cost per byte of converting type to type layout
61/// ****************************************************************************
62/// *******************
63pub fn is_one_time_witness(
64    context: &mut NativeContext,
65    mut ty_args: Vec<Type>,
66    args: VecDeque<Value>,
67) -> PartialVMResult<NativeResult> {
68    debug_assert!(ty_args.len() == 1);
69    debug_assert!(args.len() == 1);
70
71    let type_is_one_time_witness_cost_params = context
72        .extensions_mut()
73        .get::<NativesCostTable>()
74        .type_is_one_time_witness_cost_params
75        .clone();
76
77    native_charge_gas_early_exit!(
78        context,
79        type_is_one_time_witness_cost_params.types_is_one_time_witness_cost_base
80    );
81
82    // unwrap safe because the interface of native function guarantees it.
83    let ty = ty_args.pop().unwrap();
84
85    native_charge_gas_early_exit!(
86        context,
87        type_is_one_time_witness_cost_params.types_is_one_time_witness_type_cost_per_byte
88            * u64::from(ty.size()).into()
89    );
90
91    let type_tag = context.type_to_type_tag(&ty)?;
92    native_charge_gas_early_exit!(
93        context,
94        type_is_one_time_witness_cost_params.types_is_one_time_witness_type_tag_cost_per_byte
95            * u64::from(type_tag.abstract_size_for_gas_metering()).into()
96    );
97
98    let type_layout = context.type_to_type_layout(&ty)?;
99
100    let cost = context.gas_used();
101    let Some(MoveTypeLayout::Struct(struct_layout)) = type_layout else {
102        return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
103    };
104
105    let hardened_check = context.runtime_limits_config().hardened_otw_check;
106    let is_otw = is_otw_struct(&struct_layout, &type_tag, hardened_check);
107
108    Ok(NativeResult::ok(cost, smallvec![Value::bool(is_otw)]))
109}