iota_move_natives_latest/
config.rs1use std::collections::VecDeque;
6
7use iota_sdk_types::{ObjectId, StructTag};
8use iota_types::iota_sdk_types_conversions::struct_tag_core_to_sdk;
9use move_binary_format::errors::{PartialVMError, PartialVMResult};
10use move_core_types::{
11 account_address::AccountAddress, gas_algebra::InternalGas, runtime_value as R,
12 vm_status::StatusCode,
13};
14use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
15use move_vm_types::{
16 loaded_data::runtime_types::Type,
17 natives::function::NativeResult,
18 pop_arg,
19 values::{Struct, Value, Vector},
20};
21use smallvec::smallvec;
22use tracing::{error, instrument};
23
24use crate::{NativesCostTable, object_runtime::ObjectRuntime};
25
26const E_BCS_SERIALIZATION_FAILURE: u64 = 2;
27
28#[derive(Clone)]
29pub struct ConfigReadSettingImplCostParams {
30 pub config_read_setting_impl_cost_base: Option<InternalGas>,
31 pub config_read_setting_impl_cost_per_byte: Option<InternalGas>,
32}
33
34#[instrument(level = "trace", skip_all)]
35pub fn read_setting_impl(
36 context: &mut NativeContext,
37 mut ty_args: Vec<Type>,
38 mut args: VecDeque<Value>,
39) -> PartialVMResult<NativeResult> {
40 assert_eq!(ty_args.len(), 4);
41 assert_eq!(args.len(), 3);
42
43 let ConfigReadSettingImplCostParams {
44 config_read_setting_impl_cost_base,
45 config_read_setting_impl_cost_per_byte,
46 } = context
47 .extensions_mut()
48 .get::<NativesCostTable>()?
49 .config_read_setting_impl_cost_params
50 .clone();
51
52 let config_read_setting_impl_cost_base =
53 config_read_setting_impl_cost_base.ok_or_else(|| {
54 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
55 .with_message("gas cost is not set".to_string())
56 })?;
57 let config_read_setting_impl_cost_per_byte = config_read_setting_impl_cost_per_byte
58 .ok_or_else(|| {
59 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
60 .with_message("gas cost is not set".to_string())
61 })?;
62 native_charge_gas_early_exit!(context, config_read_setting_impl_cost_base);
64
65 let value_ty = ty_args.pop().unwrap();
66 let setting_data_value_ty = ty_args.pop().unwrap();
67 let setting_value_ty = ty_args.pop().unwrap();
68 let field_setting_ty = ty_args.pop().unwrap();
69
70 let current_epoch = pop_arg!(args, u64);
71 let name_df_addr = pop_arg!(args, AccountAddress);
72 let config_addr = pop_arg!(args, AccountAddress);
73
74 let field_setting_tag: StructTag = match context.type_to_type_tag(&field_setting_ty)? {
75 move_core_types::language_storage::TypeTag::Struct(s) => struct_tag_core_to_sdk(&s),
76 _ => {
77 return Err(
78 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
79 .with_message("IOTA verifier guarantees this is a struct".to_string()),
80 );
81 }
82 };
83 let Some(field_setting_layout) = context.type_to_type_layout(&field_setting_ty)? else {
84 return Ok(NativeResult::err(
85 context.gas_used(),
86 E_BCS_SERIALIZATION_FAILURE,
87 ));
88 };
89 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
90
91 let read_value_opt = consistent_value_before_current_epoch(
92 object_runtime,
93 &field_setting_ty,
94 field_setting_tag,
95 &field_setting_layout,
96 &setting_value_ty,
97 &setting_data_value_ty,
98 &value_ty,
99 config_addr,
100 name_df_addr,
101 current_epoch,
102 )?;
103
104 native_charge_gas_early_exit!(
105 context,
106 config_read_setting_impl_cost_per_byte * u64::from(read_value_opt.legacy_size()).into()
107 );
108
109 Ok(NativeResult::ok(
110 context.gas_used(),
111 smallvec![read_value_opt],
112 ))
113}
114
115fn consistent_value_before_current_epoch(
116 object_runtime: &mut ObjectRuntime,
117 field_setting_ty: &Type,
118 field_setting_tag: StructTag,
119 field_setting_layout: &R::MoveTypeLayout,
120 _setting_value_ty: &Type,
121 setting_data_value_ty: &Type,
122 value_ty: &Type,
123 config_addr: AccountAddress,
124 name_df_addr: AccountAddress,
125 current_epoch: u64,
126) -> PartialVMResult<Value> {
127 let Some(field) = object_runtime.config_setting_unsequenced_read(
128 ObjectId::new(config_addr.into_bytes()),
129 ObjectId::new(name_df_addr.into_bytes()),
130 field_setting_ty,
131 field_setting_layout,
132 &field_setting_tag,
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_tag: {field_setting_tag:?}",
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.try_into()?,
190 )?])))
191}