iota_move_natives_latest/
dynamic_field.rs1use std::collections::VecDeque;
6
7use iota_types::{base_types::MoveObjectType, dynamic_field::derive_dynamic_field_id};
8use move_binary_format::errors::{PartialVMError, PartialVMResult};
9use move_core_types::{
10 account_address::AccountAddress,
11 gas_algebra::InternalGas,
12 language_storage::{StructTag, TypeTag},
13 vm_status::StatusCode,
14};
15use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
16use move_vm_types::{
17 loaded_data::runtime_types::Type,
18 natives::function::NativeResult,
19 pop_arg,
20 values::{StructRef, Value},
21};
22use smallvec::smallvec;
23use tracing::instrument;
24
25use crate::{
26 NativesCostTable, get_nested_struct_field, get_object_id,
27 object_runtime::{ObjectRuntime, object_store::ObjectResult},
28};
29
30const E_KEY_DOES_NOT_EXIST: u64 = 1;
31const E_FIELD_TYPE_MISMATCH: u64 = 2;
32const E_BCS_SERIALIZATION_FAILURE: u64 = 3;
33
34macro_rules! get_or_fetch_object {
35 ($context:ident, $ty_args:ident, $parent:ident, $child_id:ident, $ty_cost_per_byte:expr) => {{
36 let child_ty = $ty_args.pop().unwrap();
37 native_charge_gas_early_exit!(
38 $context,
39 $ty_cost_per_byte * u64::from(child_ty.size()).into()
40 );
41
42 assert!($ty_args.is_empty());
43 let (tag, layout, annotated_layout) = match crate::get_tag_and_layouts($context, &child_ty)?
44 {
45 Some(res) => res,
46 None => {
47 return Ok(NativeResult::err(
48 $context.gas_used(),
49 E_BCS_SERIALIZATION_FAILURE,
50 ));
51 }
52 };
53
54 let object_runtime: &mut ObjectRuntime = $context.extensions_mut().get_mut();
55 object_runtime.get_or_fetch_child_object(
56 $parent,
57 $child_id,
58 &child_ty,
59 &layout,
60 &annotated_layout,
61 MoveObjectType::from(tag),
62 )?
63 }};
64}
65
66#[derive(Clone)]
67pub struct DynamicFieldHashTypeAndKeyCostParams {
68 pub dynamic_field_hash_type_and_key_cost_base: InternalGas,
69 pub dynamic_field_hash_type_and_key_type_cost_per_byte: InternalGas,
70 pub dynamic_field_hash_type_and_key_value_cost_per_byte: InternalGas,
71 pub dynamic_field_hash_type_and_key_type_tag_cost_per_byte: InternalGas,
72}
73
74#[instrument(level = "trace", skip_all)]
90pub fn hash_type_and_key(
91 context: &mut NativeContext,
92 mut ty_args: Vec<Type>,
93 mut args: VecDeque<Value>,
94) -> PartialVMResult<NativeResult> {
95 assert_eq!(ty_args.len(), 1);
96 assert_eq!(args.len(), 2);
97
98 let dynamic_field_hash_type_and_key_cost_params = context
99 .extensions_mut()
100 .get::<NativesCostTable>()
101 .dynamic_field_hash_type_and_key_cost_params
102 .clone();
103
104 native_charge_gas_early_exit!(
106 context,
107 dynamic_field_hash_type_and_key_cost_params.dynamic_field_hash_type_and_key_cost_base
108 );
109
110 let k_ty = ty_args.pop().unwrap();
111 let k: Value = args.pop_back().unwrap();
112 let parent = pop_arg!(args, AccountAddress);
113
114 let k_ty_size = u64::from(k_ty.size());
116 let k_value_size = u64::from(k.legacy_size());
117 native_charge_gas_early_exit!(
118 context,
119 dynamic_field_hash_type_and_key_cost_params
120 .dynamic_field_hash_type_and_key_type_cost_per_byte
121 * k_ty_size.into()
122 + dynamic_field_hash_type_and_key_cost_params
123 .dynamic_field_hash_type_and_key_value_cost_per_byte
124 * k_value_size.into()
125 );
126
127 let k_tag = context.type_to_type_tag(&k_ty)?;
128 let k_tag_size = u64::from(k_tag.abstract_size_for_gas_metering());
129
130 native_charge_gas_early_exit!(
131 context,
132 dynamic_field_hash_type_and_key_cost_params
133 .dynamic_field_hash_type_and_key_type_tag_cost_per_byte
134 * k_tag_size.into()
135 );
136
137 let cost = context.gas_used();
138
139 let k_layout = match context.type_to_type_layout(&k_ty) {
140 Ok(Some(layout)) => layout,
141 _ => return Ok(NativeResult::err(cost, E_BCS_SERIALIZATION_FAILURE)),
142 };
143 let Some(k_bytes) = k.simple_serialize(&k_layout) else {
144 return Ok(NativeResult::err(cost, E_BCS_SERIALIZATION_FAILURE));
145 };
146 let Ok(id) = derive_dynamic_field_id(parent, &k_tag, &k_bytes) else {
147 return Ok(NativeResult::err(cost, E_BCS_SERIALIZATION_FAILURE));
148 };
149
150 Ok(NativeResult::ok(cost, smallvec![Value::address(id.into())]))
151}
152
153#[derive(Clone)]
154pub struct DynamicFieldAddChildObjectCostParams {
155 pub dynamic_field_add_child_object_cost_base: InternalGas,
156 pub dynamic_field_add_child_object_type_cost_per_byte: InternalGas,
157 pub dynamic_field_add_child_object_value_cost_per_byte: InternalGas,
158 pub dynamic_field_add_child_object_struct_tag_cost_per_byte: InternalGas,
159}
160
161#[instrument(level = "trace", skip_all)]
180pub fn add_child_object(
181 context: &mut NativeContext,
182 mut ty_args: Vec<Type>,
183 mut args: VecDeque<Value>,
184) -> PartialVMResult<NativeResult> {
185 assert!(ty_args.len() == 1);
186 assert!(args.len() == 2);
187
188 let dynamic_field_add_child_object_cost_params = context
189 .extensions_mut()
190 .get::<NativesCostTable>()
191 .dynamic_field_add_child_object_cost_params
192 .clone();
193
194 native_charge_gas_early_exit!(
196 context,
197 dynamic_field_add_child_object_cost_params.dynamic_field_add_child_object_cost_base
198 );
199
200 let child = args.pop_back().unwrap();
201 let parent = pop_arg!(args, AccountAddress).into();
202 assert!(args.is_empty());
203
204 let child_value_size = u64::from(child.legacy_size());
205 native_charge_gas_early_exit!(
207 context,
208 dynamic_field_add_child_object_cost_params
209 .dynamic_field_add_child_object_value_cost_per_byte
210 * child_value_size.into()
211 );
212
213 let child_id = get_object_id(child.copy_value().unwrap())
215 .unwrap()
216 .value_as::<AccountAddress>()
217 .unwrap()
218 .into();
219 let child_ty = ty_args.pop().unwrap();
220 let child_type_size = u64::from(child_ty.size());
221
222 native_charge_gas_early_exit!(
223 context,
224 dynamic_field_add_child_object_cost_params
225 .dynamic_field_add_child_object_type_cost_per_byte
226 * child_type_size.into()
227 );
228
229 assert!(ty_args.is_empty());
230 let tag = match context.type_to_type_tag(&child_ty)? {
231 TypeTag::Struct(s) => *s,
232 _ => {
233 return Err(
234 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
235 .with_message("IOTA verifier guarantees this is a struct".to_string()),
236 );
237 }
238 };
239
240 let struct_tag_size = u64::from(tag.abstract_size_for_gas_metering());
241 native_charge_gas_early_exit!(
242 context,
243 dynamic_field_add_child_object_cost_params
244 .dynamic_field_add_child_object_struct_tag_cost_per_byte
245 * struct_tag_size.into()
246 );
247
248 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut();
249 object_runtime.add_child_object(
250 parent,
251 child_id,
252 &child_ty,
253 MoveObjectType::from(tag),
254 child,
255 )?;
256 Ok(NativeResult::ok(context.gas_used(), smallvec![]))
257}
258
259#[derive(Clone)]
260pub struct DynamicFieldBorrowChildObjectCostParams {
261 pub dynamic_field_borrow_child_object_cost_base: InternalGas,
262 pub dynamic_field_borrow_child_object_child_ref_cost_per_byte: InternalGas,
263 pub dynamic_field_borrow_child_object_type_cost_per_byte: InternalGas,
264}
265
266#[instrument(level = "trace", skip_all)]
284pub fn borrow_child_object(
285 context: &mut NativeContext,
286 mut ty_args: Vec<Type>,
287 mut args: VecDeque<Value>,
288) -> PartialVMResult<NativeResult> {
289 assert!(ty_args.len() == 1);
290 assert!(args.len() == 2);
291
292 let dynamic_field_borrow_child_object_cost_params = context
293 .extensions_mut()
294 .get::<NativesCostTable>()
295 .dynamic_field_borrow_child_object_cost_params
296 .clone();
297 native_charge_gas_early_exit!(
298 context,
299 dynamic_field_borrow_child_object_cost_params.dynamic_field_borrow_child_object_cost_base
300 );
301
302 let child_id = pop_arg!(args, AccountAddress).into();
303
304 let parent_uid = pop_arg!(args, StructRef).read_ref().unwrap();
305 let parent = get_nested_struct_field(parent_uid, &[0, 0])
307 .unwrap()
308 .value_as::<AccountAddress>()
309 .unwrap()
310 .into();
311
312 assert!(args.is_empty());
313 let global_value_result = get_or_fetch_object!(
314 context,
315 ty_args,
316 parent,
317 child_id,
318 dynamic_field_borrow_child_object_cost_params
319 .dynamic_field_borrow_child_object_type_cost_per_byte
320 );
321 let global_value = match global_value_result {
322 ObjectResult::MismatchedType => {
323 return Ok(NativeResult::err(context.gas_used(), E_FIELD_TYPE_MISMATCH));
324 }
325 ObjectResult::Loaded(gv) => gv,
326 };
327 if !global_value.exists()? {
328 return Ok(NativeResult::err(context.gas_used(), E_KEY_DOES_NOT_EXIST));
329 }
330 let child_ref = global_value.borrow_global().inspect_err(|err| {
331 assert!(err.major_status() != StatusCode::MISSING_DATA);
332 })?;
333
334 native_charge_gas_early_exit!(
335 context,
336 dynamic_field_borrow_child_object_cost_params
337 .dynamic_field_borrow_child_object_child_ref_cost_per_byte
338 * u64::from(child_ref.legacy_size()).into()
339 );
340
341 Ok(NativeResult::ok(context.gas_used(), smallvec![child_ref]))
342}
343
344#[derive(Clone)]
345pub struct DynamicFieldRemoveChildObjectCostParams {
346 pub dynamic_field_remove_child_object_cost_base: InternalGas,
347 pub dynamic_field_remove_child_object_child_cost_per_byte: InternalGas,
348 pub dynamic_field_remove_child_object_type_cost_per_byte: InternalGas,
349}
350#[instrument(level = "trace", skip_all)]
367pub fn remove_child_object(
368 context: &mut NativeContext,
369 mut ty_args: Vec<Type>,
370 mut args: VecDeque<Value>,
371) -> PartialVMResult<NativeResult> {
372 assert!(ty_args.len() == 1);
373 assert!(args.len() == 2);
374
375 let dynamic_field_remove_child_object_cost_params = context
376 .extensions_mut()
377 .get::<NativesCostTable>()
378 .dynamic_field_remove_child_object_cost_params
379 .clone();
380 native_charge_gas_early_exit!(
381 context,
382 dynamic_field_remove_child_object_cost_params.dynamic_field_remove_child_object_cost_base
383 );
384
385 let child_id = pop_arg!(args, AccountAddress).into();
386 let parent = pop_arg!(args, AccountAddress).into();
387 assert!(args.is_empty());
388 let global_value_result = get_or_fetch_object!(
389 context,
390 ty_args,
391 parent,
392 child_id,
393 dynamic_field_remove_child_object_cost_params
394 .dynamic_field_remove_child_object_type_cost_per_byte
395 );
396 let global_value = match global_value_result {
397 ObjectResult::MismatchedType => {
398 return Ok(NativeResult::err(context.gas_used(), E_FIELD_TYPE_MISMATCH));
399 }
400 ObjectResult::Loaded(gv) => gv,
401 };
402 if !global_value.exists()? {
403 return Ok(NativeResult::err(context.gas_used(), E_KEY_DOES_NOT_EXIST));
404 }
405 let child = global_value.move_from().inspect_err(|err| {
406 assert!(err.major_status() != StatusCode::MISSING_DATA);
407 })?;
408
409 native_charge_gas_early_exit!(
410 context,
411 dynamic_field_remove_child_object_cost_params
412 .dynamic_field_remove_child_object_child_cost_per_byte
413 * u64::from(child.legacy_size()).into()
414 );
415
416 Ok(NativeResult::ok(context.gas_used(), smallvec![child]))
417}
418
419#[derive(Clone)]
420pub struct DynamicFieldHasChildObjectCostParams {
421 pub dynamic_field_has_child_object_cost_base: InternalGas,
423}
424#[instrument(level = "trace", skip_all)]
432pub fn has_child_object(
433 context: &mut NativeContext,
434 ty_args: Vec<Type>,
435 mut args: VecDeque<Value>,
436) -> PartialVMResult<NativeResult> {
437 assert!(ty_args.is_empty());
438 assert!(args.len() == 2);
439
440 let dynamic_field_has_child_object_cost_params = context
441 .extensions_mut()
442 .get::<NativesCostTable>()
443 .dynamic_field_has_child_object_cost_params
444 .clone();
445 native_charge_gas_early_exit!(
446 context,
447 dynamic_field_has_child_object_cost_params.dynamic_field_has_child_object_cost_base
448 );
449
450 let child_id = pop_arg!(args, AccountAddress).into();
451 let parent = pop_arg!(args, AccountAddress).into();
452 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut();
453 let has_child = object_runtime.child_object_exists(parent, child_id)?;
454 Ok(NativeResult::ok(
455 context.gas_used(),
456 smallvec![Value::bool(has_child)],
457 ))
458}
459
460#[derive(Clone)]
461pub struct DynamicFieldHasChildObjectWithTyCostParams {
462 pub dynamic_field_has_child_object_with_ty_cost_base: InternalGas,
463 pub dynamic_field_has_child_object_with_ty_type_cost_per_byte: InternalGas,
464 pub dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: InternalGas,
465}
466#[instrument(level = "trace", skip_all)]
481pub fn has_child_object_with_ty(
482 context: &mut NativeContext,
483 mut ty_args: Vec<Type>,
484 mut args: VecDeque<Value>,
485) -> PartialVMResult<NativeResult> {
486 assert!(ty_args.len() == 1);
487 assert!(args.len() == 2);
488
489 let dynamic_field_has_child_object_with_ty_cost_params = context
490 .extensions_mut()
491 .get::<NativesCostTable>()
492 .dynamic_field_has_child_object_with_ty_cost_params
493 .clone();
494 native_charge_gas_early_exit!(
495 context,
496 dynamic_field_has_child_object_with_ty_cost_params
497 .dynamic_field_has_child_object_with_ty_cost_base
498 );
499
500 let child_id = pop_arg!(args, AccountAddress).into();
501 let parent = pop_arg!(args, AccountAddress).into();
502 assert!(args.is_empty());
503 let ty = ty_args.pop().unwrap();
504
505 native_charge_gas_early_exit!(
506 context,
507 dynamic_field_has_child_object_with_ty_cost_params
508 .dynamic_field_has_child_object_with_ty_type_cost_per_byte
509 * u64::from(ty.size()).into()
510 );
511
512 let tag: StructTag = match context.type_to_type_tag(&ty)? {
513 TypeTag::Struct(s) => *s,
514 _ => {
515 return Err(
516 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
517 .with_message("IOTA verifier guarantees this is a struct".to_string()),
518 );
519 }
520 };
521
522 native_charge_gas_early_exit!(
523 context,
524 dynamic_field_has_child_object_with_ty_cost_params
525 .dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte
526 * u64::from(tag.abstract_size_for_gas_metering()).into()
527 );
528
529 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut();
530 let has_child = object_runtime.child_object_exists_and_has_type(
531 parent,
532 child_id,
533 &MoveObjectType::from(tag),
534 )?;
535 Ok(NativeResult::ok(
536 context.gas_used(),
537 smallvec![Value::bool(has_child)],
538 ))
539}