iota_move_natives_latest/
config.rs1use std::collections::VecDeque;
6
7use iota_types::{TypeTag, base_types::MoveObjectType};
8use move_binary_format::errors::{PartialVMError, PartialVMResult};
9use move_core_types::{
10 account_address::AccountAddress, gas_algebra::InternalGas, language_storage::StructTag,
11 runtime_value as R, vm_status::StatusCode,
12};
13use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
14use move_vm_types::{
15 loaded_data::runtime_types::Type,
16 natives::function::NativeResult,
17 pop_arg,
18 values::{Struct, Value, Vector},
19};
20use smallvec::smallvec;
21use tracing::{error, instrument};
22
23use crate::{NativesCostTable, object_runtime::ObjectRuntime};
24
25const E_BCS_SERIALIZATION_FAILURE: u64 = 2;
26
27#[derive(Clone)]
28pub struct ConfigReadSettingImplCostParams {
29 pub config_read_setting_impl_cost_base: Option<InternalGas>,
30 pub config_read_setting_impl_cost_per_byte: Option<InternalGas>,
31}
32
33#[instrument(level = "trace", skip_all)]
34pub fn read_setting_impl(
35 context: &mut NativeContext,
36 mut ty_args: Vec<Type>,
37 mut args: VecDeque<Value>,
38) -> PartialVMResult<NativeResult> {
39 assert_eq!(ty_args.len(), 4);
40 assert_eq!(args.len(), 3);
41
42 let ConfigReadSettingImplCostParams {
43 config_read_setting_impl_cost_base,
44 config_read_setting_impl_cost_per_byte,
45 } = context
46 .extensions_mut()
47 .get::<NativesCostTable>()
48 .config_read_setting_impl_cost_params
49 .clone();
50
51 let config_read_setting_impl_cost_base =
52 config_read_setting_impl_cost_base.ok_or_else(|| {
53 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
54 .with_message("gas cost is not set".to_string())
55 })?;
56 let config_read_setting_impl_cost_per_byte = config_read_setting_impl_cost_per_byte
57 .ok_or_else(|| {
58 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
59 .with_message("gas cost is not set".to_string())
60 })?;
61 native_charge_gas_early_exit!(context, config_read_setting_impl_cost_base);
63
64 let value_ty = ty_args.pop().unwrap();
65 let setting_data_value_ty = ty_args.pop().unwrap();
66 let setting_value_ty = ty_args.pop().unwrap();
67 let field_setting_ty = ty_args.pop().unwrap();
68
69 let current_epoch = pop_arg!(args, u64);
70 let name_df_addr = pop_arg!(args, AccountAddress);
71 let config_addr = pop_arg!(args, AccountAddress);
72
73 let field_setting_tag: StructTag = match context.type_to_type_tag(&field_setting_ty)? {
74 TypeTag::Struct(s) => *s,
75 _ => {
76 return Err(
77 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
78 .with_message("IOTA verifier guarantees this is a struct".to_string()),
79 );
80 }
81 };
82 let Some(field_setting_layout) = context.type_to_type_layout(&field_setting_ty)? else {
83 return Ok(NativeResult::err(
84 context.gas_used(),
85 E_BCS_SERIALIZATION_FAILURE,
86 ));
87 };
88 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut();
89
90 let read_value_opt = consistent_value_before_current_epoch(
91 object_runtime,
92 &field_setting_ty,
93 field_setting_tag,
94 &field_setting_layout,
95 &setting_value_ty,
96 &setting_data_value_ty,
97 &value_ty,
98 config_addr,
99 name_df_addr,
100 current_epoch,
101 )?;
102
103 native_charge_gas_early_exit!(
104 context,
105 config_read_setting_impl_cost_per_byte * u64::from(read_value_opt.legacy_size()).into()
106 );
107
108 Ok(NativeResult::ok(
109 context.gas_used(),
110 smallvec![read_value_opt],
111 ))
112}
113
114fn consistent_value_before_current_epoch(
115 object_runtime: &mut ObjectRuntime,
116 field_setting_ty: &Type,
117 field_setting_tag: StructTag,
118 field_setting_layout: &R::MoveTypeLayout,
119 _setting_value_ty: &Type,
120 setting_data_value_ty: &Type,
121 value_ty: &Type,
122 config_addr: AccountAddress,
123 name_df_addr: AccountAddress,
124 current_epoch: u64,
125) -> PartialVMResult<Value> {
126 let field_setting_obj_ty = MoveObjectType::from(field_setting_tag);
127 let Some(field) = object_runtime.config_setting_unsequenced_read(
128 config_addr.into(),
129 name_df_addr.into(),
130 field_setting_ty,
131 field_setting_layout,
132 &field_setting_obj_ty,
133 ) else {
134 return option_none(value_ty);
135 };
136
137 let [_id, _name, setting]: [Value; 3] = unpack_struct(field)?;
138 let [data_opt]: [Value; 1] = unpack_struct(setting)?;
139 let data = match unpack_option(data_opt, setting_data_value_ty)? {
140 None => {
141 error!(
142 "
143 SettingData is none.
144 config_addr: {config_addr},
145 name_df_addr: {name_df_addr},
146 field_setting_obj_ty: {field_setting_obj_ty:?}",
147 );
148 return option_none(value_ty);
149 }
150 Some(data) => data,
151 };
152 let [newer_value_epoch, newer_value, older_value_opt]: [Value; 3] = unpack_struct(data)?;
153 let newer_value_epoch: u64 = newer_value_epoch.value_as()?;
154 debug_assert!(
155 unpack_option(newer_value.copy_value()?, value_ty)?.is_some()
156 || unpack_option(older_value_opt.copy_value()?, value_ty)?.is_some()
157 );
158 Ok(if current_epoch > newer_value_epoch {
159 newer_value
160 } else {
161 older_value_opt
162 })
163}
164
165fn unpack_struct<const N: usize>(s: Value) -> PartialVMResult<[Value; N]> {
166 let s: Struct = s.value_as()?;
167 s.unpack()?.collect::<Vec<_>>().try_into().map_err(|e| {
168 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
169 .with_message(format!("struct expected to have have {N} fields: {e:?}"))
170 })
171}
172
173fn unpack_option(option: Value, type_param: &Type) -> PartialVMResult<Option<Value>> {
174 let [vec_value]: [Value; 1] = unpack_struct(option)?;
175 let vec: Vector = vec_value.value_as()?;
176 Ok(if vec.elem_len() == 0 {
177 None
178 } else {
179 let [elem]: [Value; 1] = vec.unpack(type_param, 1)?.try_into().map_err(|e| {
180 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
181 .with_message(format!("vector expected to have one element: {e:?}"))
182 })?;
183 Some(elem)
184 })
185}
186
187fn option_none(type_param: &Type) -> PartialVMResult<Value> {
188 Ok(Value::struct_(Struct::pack(vec![Vector::empty(
189 type_param,
190 )?])))
191}