1use move_core_types::{
5 account_address::AccountAddress,
6 annotated_value as A,
7 annotated_visitor::{self, StructDriver, Visitor},
8 language_storage::TypeTag,
9 u256::U256,
10 visitor_default,
11};
12
13use crate::{base_types::ObjectID, id::UID};
14
15use super::{DynamicFieldInfo, DynamicFieldType};
16
17pub struct FieldVisitor;
20
21#[derive(Debug, Clone)]
22pub struct Field<'b, 'l> {
23 pub id: ObjectID,
24 pub kind: DynamicFieldType,
25 pub name_layout: &'l A::MoveTypeLayout,
26 pub name_bytes: &'b [u8],
27 pub value_layout: &'l A::MoveTypeLayout,
28 pub value_bytes: &'b [u8],
29}
30
31pub enum ValueMetadata {
32 DynamicField(TypeTag),
33 DynamicObjectField(ObjectID),
34}
35
36#[derive(thiserror::Error, Debug)]
37pub enum Error {
38 #[error("Not a dynamic field")]
39 NotADynamicField,
40
41 #[error("Not a dynamic object field")]
42 NotADynamicObjectField,
43
44 #[error("{0}")]
45 Visitor(#[from] annotated_visitor::Error),
46}
47
48impl FieldVisitor {
49 pub fn deserialize<'b, 'l>(
52 bytes: &'b [u8],
53 layout: &'l A::MoveTypeLayout,
54 ) -> Result<Field<'b, 'l>, Error> {
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 visitor_default! { <'b, 'l> u8, u16, u32, u64, u128, u256 = Err(Error::NotADynamicField) }
86 visitor_default! { <'b, 'l> bool, address, signer, vector, variant = Err(Error::NotADynamicField) }
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(&driver.struct_layout().type_) {
93 return Err(Error::NotADynamicField);
94 }
95
96 let mut id = None;
99 let mut name_parts = None;
100 let mut value_parts = None;
101
102 while let Some(A::MoveFieldLayout { name, layout }) = driver.peek_field() {
103 match name.as_str() {
104 "id" => {
105 let lo = driver.position();
106 driver.skip_field()?;
107 let hi = driver.position();
108
109 if !matches!(layout, A::MoveTypeLayout::Struct(s) if s.as_ref() == &UID::layout())
110 {
111 return Err(Error::NotADynamicField);
112 }
113
114 let bytes = &driver.bytes()[lo..hi];
116 id = Some(ObjectID::from_bytes(bytes).map_err(|_| Error::NotADynamicField)?);
117 }
118
119 "name" => {
120 let lo = driver.position();
121 driver.skip_field()?;
122 let hi = driver.position();
123
124 let (kind, layout) = extract_name_layout(layout)?;
125 name_parts = Some((&driver.bytes()[lo..hi], layout, kind));
126 }
127
128 "value" => {
129 let lo = driver.position();
130 driver.skip_field()?;
131 let hi = driver.position();
132 value_parts = Some((&driver.bytes()[lo..hi], layout));
133 }
134
135 _ => {
136 return Err(Error::NotADynamicField);
137 }
138 }
139 }
140
141 let (Some(id), Some((name_bytes, name_layout, kind)), Some((value_bytes, value_layout))) =
142 (id, name_parts, value_parts)
143 else {
144 return Err(Error::NotADynamicField);
145 };
146
147 Ok(Field {
148 id,
149 kind,
150 name_layout,
151 name_bytes,
152 value_layout,
153 value_bytes,
154 })
155 }
156}
157
158fn extract_name_layout(
160 layout: &A::MoveTypeLayout,
161) -> Result<(DynamicFieldType, &A::MoveTypeLayout), Error> {
162 let A::MoveTypeLayout::Struct(struct_) = layout else {
163 return Ok((DynamicFieldType::DynamicField, layout));
164 };
165
166 if !DynamicFieldInfo::is_dynamic_object_field_wrapper(&struct_.type_) {
167 return Ok((DynamicFieldType::DynamicField, layout));
168 }
169
170 let [A::MoveFieldLayout { name, layout }] = &struct_.fields[..] else {
172 return Err(Error::NotADynamicField);
173 };
174
175 if name.as_str() != "name" {
177 return Err(Error::NotADynamicField);
178 }
179
180 Ok((DynamicFieldType::DynamicObject, layout))
181}
182
183#[cfg(test)]
184mod tests {
185 use std::str::FromStr;
186
187 use move_core_types::{
188 account_address::AccountAddress, annotated_value as A, language_storage::TypeTag,
189 };
190
191 use crate::{
192 base_types::ObjectID,
193 dynamic_field,
194 id::UID,
195 object::bounded_visitor::tests::{enum_, layout_, value_, variant_},
196 };
197
198 use super::*;
199
200 #[test]
201 fn test_dynamic_field_name() {
202 for (name, name_layout, name_bcs) in fixtures() {
203 for (value, value_layout, value_bcs) in fixtures() {
204 let df = serialized_df("0x264", name.clone(), value.clone());
205 let df_layout = df_layout(name_layout.clone(), value_layout.clone());
206 let field = FieldVisitor::deserialize(&df, &df_layout)
207 .unwrap_or_else(|e| panic!("Failed to deserialize {name} => {value}: {e}"));
208
209 assert_eq!(field.id, oid_("0x264"), "{name} => {value}");
210 assert_eq!(field.name_bytes, &name_bcs, "{name} => {value}");
211 assert_eq!(field.value_bytes, &value_bcs, "{name} => {value}");
212
213 assert_eq!(
214 field.kind,
215 DynamicFieldType::DynamicField,
216 "{name} => {value}",
217 );
218
219 assert_eq!(
220 TypeTag::from(field.name_layout),
221 TypeTag::from(&name_layout),
222 "{name} => {value}",
223 );
224
225 assert_eq!(
226 TypeTag::from(field.value_layout),
227 TypeTag::from(&value_layout),
228 "{name} => {value}",
229 );
230 }
231 }
232 }
233
234 #[test]
235 fn test_dynamic_object_field_name() {
236 let addr = A::MoveValue::Address(AccountAddress::ONE);
237 let id = value_("0x2::object::ID", vec![("bytes", addr)]);
238 let id_bcs = id.clone().undecorate().simple_serialize().unwrap();
239
240 for (name, name_layout, name_bcs) in fixtures() {
241 let df = serialized_df("0x264", name.clone(), id.clone());
242 let df_layout = dof_layout(name_layout.clone());
243 let field = FieldVisitor::deserialize(&df, &df_layout)
244 .unwrap_or_else(|e| panic!("Failed to deserialize {name}: {e}"));
245
246 assert_eq!(field.id, oid_("0x264"), "{name}");
247 assert_eq!(field.name_bytes, &name_bcs, "{name}");
248 assert_eq!(field.value_bytes, &id_bcs, "{name}");
249
250 assert_eq!(field.kind, DynamicFieldType::DynamicObject, "{name}",);
251
252 assert_eq!(
253 TypeTag::from(field.name_layout),
254 TypeTag::from(&name_layout),
255 "{name}",
256 );
257
258 assert_eq!(
259 TypeTag::from(field.value_layout),
260 TypeTag::from(&id_layout()),
261 "{name}",
262 );
263 }
264 }
265
266 #[test]
267 fn test_name_from_not_dynamic_field() {
268 for (value, layout, bytes) in fixtures() {
269 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
270 panic!("Expected NotADynamicField error for {value}");
271 };
272
273 assert_eq!(
274 e.to_string(),
275 "Not a dynamic field",
276 "Unexpected error for {value}"
277 );
278 }
279 }
280
281 #[test]
284 fn test_from_bad_type() {
285 for (value, layout, bytes) in fixtures() {
286 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
287 panic!("Expected NotADynamicField error for {value}");
288 };
289
290 assert_eq!(
291 e.to_string(),
292 "Not a dynamic field",
293 "Unexpected error for {value}"
294 );
295 }
296 }
297
298 #[test]
299 fn test_from_dynamic_field_missing_id() {
300 let bytes = bcs::to_bytes(&(42u8, 43u8)).unwrap();
301 let layout = layout_(
302 "0x2::dynamic_field::Field<u8, u8>",
303 vec![
304 ("name", A::MoveTypeLayout::U8),
305 ("value", A::MoveTypeLayout::U8),
306 ],
307 );
308
309 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
310 panic!("Expected NotADynamicField error");
311 };
312
313 assert_eq!(e.to_string(), "Not a dynamic field");
314 }
315
316 #[test]
317 fn test_from_dynamic_field_missing_name() {
318 let bytes = bcs::to_bytes(&(oid_("0x264"), 43u8)).unwrap();
319 let layout = layout_(
320 "0x2::dynamic_field::Field<u8, u8>",
321 vec![("id", id_layout()), ("value", A::MoveTypeLayout::U8)],
322 );
323
324 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
325 panic!("Expected NotADynamicField error");
326 };
327
328 assert_eq!(e.to_string(), "Not a dynamic field");
329 }
330
331 #[test]
332 fn test_from_dynamic_field_missing_value() {
333 let bytes = bcs::to_bytes(&(oid_("0x264"), 42u8)).unwrap();
334 let layout = layout_(
335 "0x2::dynamic_field::Field<u8, u8>",
336 vec![("id", id_layout()), ("name", A::MoveTypeLayout::U8)],
337 );
338
339 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
340 panic!("Expected NotADynamicField error");
341 };
342
343 assert_eq!(e.to_string(), "Not a dynamic field");
344 }
345
346 #[test]
347 fn test_from_dynamic_field_weird_id() {
348 let bytes = bcs::to_bytes(&(42u8, 43u8, 44u8)).unwrap();
349 let layout = layout_(
350 "0x2::dynamic_field::Field<u8, u8>",
351 vec![
352 ("id", A::MoveTypeLayout::U8),
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]
368 fn test_from_dynamic_object_field_bad_wrapper() {
369 let bytes = bcs::to_bytes(&(oid_("0x264"), 42u8)).unwrap();
370 let layout = layout_(
371 "0x2::dynamic_field::Field<0x2::dynamic_object_field::Wrapper<u8>, u8>",
372 vec![
373 ("id", id_layout()),
374 (
375 "name",
376 layout_(
377 "0x2::dynamic_object_field::Wrapper<u8>",
378 vec![("wrapped", A::MoveTypeLayout::U8)],
380 ),
381 ),
382 ("value", A::MoveTypeLayout::U8),
383 ],
384 );
385
386 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
387 panic!("Expected NotADynamicField error");
388 };
389
390 assert_eq!(e.to_string(), "Not a dynamic field");
391 }
392
393 fn fixtures() -> Vec<(A::MoveValue, A::MoveTypeLayout, Vec<u8>)> {
395 use A::MoveTypeLayout as T;
396 use A::MoveValue as V;
397
398 vec![
399 fixture(V::U8(42), T::U8),
400 fixture(V::Address(AccountAddress::ONE), T::Address),
401 fixture(
402 V::Vector(vec![V::U32(43), V::U32(44), V::U32(45)]),
403 T::Vector(Box::new(T::U32)),
404 ),
405 fixture(
406 value_(
407 "0x2::object::ID",
408 vec![("bytes", V::Address(AccountAddress::TWO))],
409 ),
410 layout_("0x2::object::ID", vec![("bytes", T::Address)]),
411 ),
412 fixture(
413 variant_(
414 "0x1::option::Option<u64>",
415 "Some",
416 1,
417 vec![("value", V::U64(46))],
418 ),
419 enum_(
420 "0x1::option::Option<u64>",
421 vec![
422 (("None", 0), vec![]),
423 (("Some", 1), vec![("value", T::U64)]),
424 ],
425 ),
426 ),
427 ]
428 }
429
430 fn fixture(
431 value: A::MoveValue,
432 layout: A::MoveTypeLayout,
433 ) -> (A::MoveValue, A::MoveTypeLayout, Vec<u8>) {
434 let bytes = value
435 .clone()
436 .undecorate()
437 .simple_serialize()
438 .unwrap_or_else(|| panic!("Failed to serialize {}", value.clone()));
439
440 (value, layout, bytes)
441 }
442
443 fn oid_(rep: &str) -> ObjectID {
444 ObjectID::from_str(rep).unwrap()
445 }
446
447 fn serialized_df(id: &str, name: A::MoveValue, value: A::MoveValue) -> Vec<u8> {
448 bcs::to_bytes(&dynamic_field::Field {
449 id: UID::new(oid_(id)),
450 name: name.undecorate(),
451 value: value.undecorate(),
452 })
453 .unwrap()
454 }
455
456 fn id_layout() -> A::MoveTypeLayout {
457 let addr = A::MoveTypeLayout::Address;
458 layout_("0x2::object::ID", vec![("bytes", addr)])
459 }
460
461 fn df_layout(name: A::MoveTypeLayout, value: A::MoveTypeLayout) -> A::MoveTypeLayout {
462 let uid = layout_("0x2::object::UID", vec![("id", id_layout())]);
463 let field = format!(
464 "0x2::dynamic_field::Field<{}, {}>",
465 TypeTag::from(&name).to_canonical_display(true),
466 TypeTag::from(&value).to_canonical_display(true)
467 );
468
469 layout_(&field, vec![("id", uid), ("name", name), ("value", value)])
470 }
471
472 fn dof_layout(name: A::MoveTypeLayout) -> A::MoveTypeLayout {
473 let tag = TypeTag::from(&name);
474 let wrapper = format!(
475 "0x2::dynamic_object_field::Wrapper<{}>",
476 tag.to_canonical_display(true)
477 );
478
479 let name = layout_(&wrapper, vec![("name", name)]);
480 df_layout(name, id_layout())
481 }
482}