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