1use iota_sdk_types::TypeTag;
6use move_core_types::{
7 account_address::AccountAddress,
8 annotated_value as A,
9 annotated_visitor::{self, StructDriver, ValueDriver, VariantDriver, VecDriver, Visitor},
10 u256::U256,
11};
12
13use super::{DynamicFieldInfo, DynamicFieldType};
14use crate::{
15 base_types::ObjectID,
16 id::UID,
17 iota_sdk_types_conversions::{struct_tag_core_to_sdk, type_tag_core_to_sdk},
18};
19
20pub struct FieldVisitor;
23
24#[derive(Debug, Clone)]
25pub struct Field<'b, 'l> {
26 pub id: ObjectID,
27 pub kind: DynamicFieldType,
28 pub name_layout: &'l A::MoveTypeLayout,
29 pub name_bytes: &'b [u8],
30 pub value_layout: &'l A::MoveTypeLayout,
31 pub value_bytes: &'b [u8],
32}
33
34pub enum ValueMetadata {
35 DynamicField(TypeTag),
36 DynamicObjectField(ObjectID),
37}
38
39#[derive(thiserror::Error, Debug)]
40pub enum Error {
41 #[error("Not a dynamic field")]
42 NotADynamicField,
43
44 #[error("Not a dynamic object field")]
45 NotADynamicObjectField,
46
47 #[error("{0}")]
48 Visitor(#[from] annotated_visitor::Error),
49}
50
51impl FieldVisitor {
52 pub fn deserialize<'b, 'l>(
56 bytes: &'b [u8],
57 layout: &'l A::MoveTypeLayout,
58 ) -> anyhow::Result<Field<'b, 'l>> {
59 A::MoveValue::visit_deserialize(bytes, layout, &mut FieldVisitor)
60 }
61}
62
63impl Field<'_, '_> {
64 pub fn value_metadata(&self) -> Result<ValueMetadata, Error> {
68 match self.kind {
69 DynamicFieldType::DynamicField => {
70 Ok(ValueMetadata::DynamicField(type_tag_core_to_sdk(
71 &move_core_types::language_storage::TypeTag::from(self.value_layout),
72 )))
73 }
74
75 DynamicFieldType::DynamicObject => {
76 let id: ObjectID =
77 bcs::from_bytes(self.value_bytes).map_err(|_| Error::NotADynamicObjectField)?;
78 Ok(ValueMetadata::DynamicObjectField(id))
79 }
80 }
81 }
82}
83
84impl<'b, 'l> Visitor<'b, 'l> for FieldVisitor {
85 type Value = Field<'b, 'l>;
86 type Error = Error;
87
88 fn visit_struct(
89 &mut self,
90 driver: &mut StructDriver<'_, 'b, 'l>,
91 ) -> Result<Self::Value, Error> {
92 if !DynamicFieldInfo::is_dynamic_field(&struct_tag_core_to_sdk(
93 &driver.struct_layout().type_,
94 )) {
95 return Err(Error::NotADynamicField);
96 }
97
98 let mut id = None;
101 let mut name_parts = None;
102 let mut value_parts = None;
103
104 while let Some(A::MoveFieldLayout { name, layout }) = driver.peek_field() {
105 match name.as_str() {
106 "id" => {
107 let lo = driver.position();
108 driver.skip_field()?;
109 let hi = driver.position();
110
111 if !matches!(layout, A::MoveTypeLayout::Struct(s) if s.as_ref() == &UID::layout())
112 {
113 return Err(Error::NotADynamicField);
114 }
115
116 let bytes = &driver.bytes()[lo..hi];
118 id = Some(ObjectID::from_bytes(bytes).map_err(|_| Error::NotADynamicField)?);
119 }
120
121 "name" => {
122 let lo = driver.position();
123 driver.skip_field()?;
124 let hi = driver.position();
125
126 let (kind, layout) = extract_name_layout(layout)?;
127 name_parts = Some((&driver.bytes()[lo..hi], layout, kind));
128 }
129
130 "value" => {
131 let lo = driver.position();
132 driver.skip_field()?;
133 let hi = driver.position();
134 value_parts = Some((&driver.bytes()[lo..hi], layout));
135 }
136
137 _ => {
138 return Err(Error::NotADynamicField);
139 }
140 }
141 }
142
143 let (Some(id), Some((name_bytes, name_layout, kind)), Some((value_bytes, value_layout))) =
144 (id, name_parts, value_parts)
145 else {
146 return Err(Error::NotADynamicField);
147 };
148
149 Ok(Field {
150 id,
151 kind,
152 name_layout,
153 name_bytes,
154 value_layout,
155 value_bytes,
156 })
157 }
158
159 fn visit_u8(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u8) -> Result<Self::Value, Error> {
165 Err(Error::NotADynamicField)
166 }
167
168 fn visit_u16(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u16) -> Result<Self::Value, Error> {
169 Err(Error::NotADynamicField)
170 }
171
172 fn visit_u32(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u32) -> Result<Self::Value, Error> {
173 Err(Error::NotADynamicField)
174 }
175
176 fn visit_u64(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u64) -> Result<Self::Value, Error> {
177 Err(Error::NotADynamicField)
178 }
179
180 fn visit_u128(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u128) -> Result<Self::Value, Error> {
181 Err(Error::NotADynamicField)
182 }
183
184 fn visit_u256(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: U256) -> Result<Self::Value, Error> {
185 Err(Error::NotADynamicField)
186 }
187
188 fn visit_bool(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: bool) -> Result<Self::Value, Error> {
189 Err(Error::NotADynamicField)
190 }
191
192 fn visit_address(
193 &mut self,
194 _: &ValueDriver<'_, 'b, 'l>,
195 _: AccountAddress,
196 ) -> Result<Self::Value, Error> {
197 Err(Error::NotADynamicField)
198 }
199
200 fn visit_signer(
201 &mut self,
202 _: &ValueDriver<'_, 'b, 'l>,
203 _: AccountAddress,
204 ) -> Result<Self::Value, Error> {
205 Err(Error::NotADynamicField)
206 }
207
208 fn visit_vector(&mut self, _: &mut VecDriver<'_, 'b, 'l>) -> Result<Self::Value, Error> {
209 Err(Error::NotADynamicField)
210 }
211
212 fn visit_variant(&mut self, _: &mut VariantDriver<'_, 'b, 'l>) -> Result<Self::Value, Error> {
213 Err(Error::NotADynamicField)
214 }
215}
216
217fn extract_name_layout(
220 layout: &A::MoveTypeLayout,
221) -> Result<(DynamicFieldType, &A::MoveTypeLayout), Error> {
222 let A::MoveTypeLayout::Struct(struct_) = layout else {
223 return Ok((DynamicFieldType::DynamicField, layout));
224 };
225
226 if !DynamicFieldInfo::is_dynamic_object_field_wrapper(&struct_tag_core_to_sdk(&struct_.type_)) {
227 return Ok((DynamicFieldType::DynamicField, layout));
228 }
229
230 let [A::MoveFieldLayout { name, layout }] = &struct_.fields[..] else {
232 return Err(Error::NotADynamicField);
233 };
234
235 if name.as_str() != "name" {
237 return Err(Error::NotADynamicField);
238 }
239
240 Ok((DynamicFieldType::DynamicObject, layout))
241}
242
243#[cfg(test)]
244mod tests {
245 use std::str::FromStr;
246
247 use move_core_types::{
248 account_address::AccountAddress, annotated_value as A, language_storage::TypeTag,
249 };
250
251 use super::*;
252 use crate::{
253 base_types::ObjectID,
254 dynamic_field,
255 id::UID,
256 object::bounded_visitor::tests::{enum_, layout_, value_, variant_},
257 };
258
259 #[test]
260 fn test_dynamic_field_name() {
261 for (name, name_layout, name_bcs) in fixtures() {
262 for (value, value_layout, value_bcs) in fixtures() {
263 let df = serialized_df("0x264", name.clone(), value.clone());
264 let df_layout = df_layout(name_layout.clone(), value_layout.clone());
265 let field = FieldVisitor::deserialize(&df, &df_layout)
266 .unwrap_or_else(|e| panic!("Failed to deserialize {name} => {value}: {e}"));
267
268 assert_eq!(field.id, oid_("0x264"), "{name} => {value}");
269 assert_eq!(field.name_bytes, &name_bcs, "{name} => {value}");
270 assert_eq!(field.value_bytes, &value_bcs, "{name} => {value}");
271
272 assert_eq!(
273 field.kind,
274 DynamicFieldType::DynamicField,
275 "{name} => {value}",
276 );
277
278 assert_eq!(
279 TypeTag::from(field.name_layout),
280 TypeTag::from(&name_layout),
281 "{name} => {value}",
282 );
283
284 assert_eq!(
285 TypeTag::from(field.value_layout),
286 TypeTag::from(&value_layout),
287 "{name} => {value}",
288 );
289 }
290 }
291 }
292
293 #[test]
294 fn test_dynamic_object_field_name() {
295 let addr = A::MoveValue::Address(AccountAddress::ONE);
296 let id = value_("0x2::object::ID", vec![("bytes", addr)]);
297 let id_bcs = id.clone().undecorate().simple_serialize().unwrap();
298
299 for (name, name_layout, name_bcs) in fixtures() {
300 let df = serialized_df("0x264", name.clone(), id.clone());
301 let df_layout = dof_layout(name_layout.clone());
302 let field = FieldVisitor::deserialize(&df, &df_layout)
303 .unwrap_or_else(|e| panic!("Failed to deserialize {name}: {e}"));
304
305 assert_eq!(field.id, oid_("0x264"), "{name}");
306 assert_eq!(field.name_bytes, &name_bcs, "{name}");
307 assert_eq!(field.value_bytes, &id_bcs, "{name}");
308
309 assert_eq!(field.kind, DynamicFieldType::DynamicObject, "{name}",);
310
311 assert_eq!(
312 TypeTag::from(field.name_layout),
313 TypeTag::from(&name_layout),
314 "{name}",
315 );
316
317 assert_eq!(
318 TypeTag::from(field.value_layout),
319 TypeTag::from(&id_layout()),
320 "{name}",
321 );
322 }
323 }
324
325 #[test]
326 fn test_name_from_not_dynamic_field() {
327 for (value, layout, bytes) in fixtures() {
328 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
329 panic!("Expected NotADynamicField error for {value}");
330 };
331
332 assert_eq!(
333 e.to_string(),
334 "Not a dynamic field",
335 "Unexpected error for {value}"
336 );
337 }
338 }
339
340 #[test]
343 fn test_from_bad_type() {
344 for (value, layout, bytes) in fixtures() {
345 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
346 panic!("Expected NotADynamicField error for {value}");
347 };
348
349 assert_eq!(
350 e.to_string(),
351 "Not a dynamic field",
352 "Unexpected error for {value}"
353 );
354 }
355 }
356
357 #[test]
358 fn test_from_dynamic_field_missing_id() {
359 let bytes = bcs::to_bytes(&(42u8, 43u8)).unwrap();
360 let layout = layout_(
361 "0x2::dynamic_field::Field<u8, u8>",
362 vec![
363 ("name", A::MoveTypeLayout::U8),
364 ("value", A::MoveTypeLayout::U8),
365 ],
366 );
367
368 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
369 panic!("Expected NotADynamicField error");
370 };
371
372 assert_eq!(e.to_string(), "Not a dynamic field");
373 }
374
375 #[test]
376 fn test_from_dynamic_field_missing_name() {
377 let bytes = bcs::to_bytes(&(oid_("0x264"), 43u8)).unwrap();
378 let layout = layout_(
379 "0x2::dynamic_field::Field<u8, u8>",
380 vec![("id", id_layout()), ("value", A::MoveTypeLayout::U8)],
381 );
382
383 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
384 panic!("Expected NotADynamicField error");
385 };
386
387 assert_eq!(e.to_string(), "Not a dynamic field");
388 }
389
390 #[test]
391 fn test_from_dynamic_field_missing_value() {
392 let bytes = bcs::to_bytes(&(oid_("0x264"), 42u8)).unwrap();
393 let layout = layout_(
394 "0x2::dynamic_field::Field<u8, u8>",
395 vec![("id", id_layout()), ("name", A::MoveTypeLayout::U8)],
396 );
397
398 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
399 panic!("Expected NotADynamicField error");
400 };
401
402 assert_eq!(e.to_string(), "Not a dynamic field");
403 }
404
405 #[test]
406 fn test_from_dynamic_field_weird_id() {
407 let bytes = bcs::to_bytes(&(42u8, 43u8, 44u8)).unwrap();
408 let layout = layout_(
409 "0x2::dynamic_field::Field<u8, u8>",
410 vec![
411 ("id", A::MoveTypeLayout::U8),
412 ("name", A::MoveTypeLayout::U8),
413 ("value", A::MoveTypeLayout::U8),
414 ],
415 );
416
417 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
418 panic!("Expected NotADynamicField error");
419 };
420
421 assert_eq!(e.to_string(), "Not a dynamic field");
422 }
423
424 #[test]
428 fn test_from_dynamic_object_field_bad_wrapper() {
429 let bytes = bcs::to_bytes(&(oid_("0x264"), 42u8)).unwrap();
430 let layout = layout_(
431 "0x2::dynamic_field::Field<0x2::dynamic_object_field::Wrapper<u8>, u8>",
432 vec![
433 ("id", id_layout()),
434 (
435 "name",
436 layout_(
437 "0x2::dynamic_object_field::Wrapper<u8>",
438 vec![("wrapped", A::MoveTypeLayout::U8)],
440 ),
441 ),
442 ("value", A::MoveTypeLayout::U8),
443 ],
444 );
445
446 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
447 panic!("Expected NotADynamicField error");
448 };
449
450 assert_eq!(e.to_string(), "Not a dynamic field");
451 }
452
453 fn fixtures() -> Vec<(A::MoveValue, A::MoveTypeLayout, Vec<u8>)> {
455 use A::{MoveTypeLayout as T, MoveValue as V};
456
457 vec![
458 fixture(V::U8(42), T::U8),
459 fixture(V::Address(AccountAddress::ONE), T::Address),
460 fixture(
461 V::Vector(vec![V::U32(43), V::U32(44), V::U32(45)]),
462 T::Vector(Box::new(T::U32)),
463 ),
464 fixture(
465 value_(
466 "0x2::object::ID",
467 vec![("bytes", V::Address(AccountAddress::TWO))],
468 ),
469 layout_("0x2::object::ID", vec![("bytes", T::Address)]),
470 ),
471 fixture(
472 variant_(
473 "0x1::option::Option<u64>",
474 "Some",
475 1,
476 vec![("value", V::U64(46))],
477 ),
478 enum_(
479 "0x1::option::Option<u64>",
480 vec![
481 (("None", 0), vec![]),
482 (("Some", 1), vec![("value", T::U64)]),
483 ],
484 ),
485 ),
486 ]
487 }
488
489 fn fixture(
490 value: A::MoveValue,
491 layout: A::MoveTypeLayout,
492 ) -> (A::MoveValue, A::MoveTypeLayout, Vec<u8>) {
493 let bytes = value
494 .clone()
495 .undecorate()
496 .simple_serialize()
497 .unwrap_or_else(|| panic!("Failed to serialize {}", value.clone()));
498
499 (value, layout, bytes)
500 }
501
502 fn oid_(rep: &str) -> ObjectID {
503 ObjectID::from_str(rep).unwrap()
504 }
505
506 fn serialized_df(id: &str, name: A::MoveValue, value: A::MoveValue) -> Vec<u8> {
507 bcs::to_bytes(&dynamic_field::Field {
508 id: UID::new(oid_(id)),
509 name: name.undecorate(),
510 value: value.undecorate(),
511 })
512 .unwrap()
513 }
514
515 fn id_layout() -> A::MoveTypeLayout {
516 let addr = A::MoveTypeLayout::Address;
517 layout_("0x2::object::ID", vec![("bytes", addr)])
518 }
519
520 fn df_layout(name: A::MoveTypeLayout, value: A::MoveTypeLayout) -> A::MoveTypeLayout {
521 let uid = layout_("0x2::object::UID", vec![("id", id_layout())]);
522 let field = format!(
523 "0x2::dynamic_field::Field<{}, {}>",
524 TypeTag::from(&name).to_canonical_display(true),
525 TypeTag::from(&value).to_canonical_display(true)
526 );
527
528 layout_(&field, vec![("id", uid), ("name", name), ("value", value)])
529 }
530
531 fn dof_layout(name: A::MoveTypeLayout) -> A::MoveTypeLayout {
532 let tag = TypeTag::from(&name);
533 let wrapper = format!(
534 "0x2::dynamic_object_field::Wrapper<{}>",
535 tag.to_canonical_display(true)
536 );
537
538 let name = layout_(&wrapper, vec![("name", name)]);
539 df_layout(name, id_layout())
540 }
541}