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