Skip to main content

xmtp_mls_common/app_data/
fields.rs

1//! Typed metadata fields: the developer-facing view of a group's AppData
2//! dictionary.
3//!
4//! `xmtp_mls`'s `MlsGroup` metadata-field methods (`metadata_fields`,
5//! `metadata_values`, `user_data`, `update_metadata_field`,
6//! `update_user_data`, …) read and write application and public well-known
7//! components here. The bindings do not expose them yet. Everything is a pure function of one committed dictionary: a
8//! [`FieldSnapshot`] lists the fields its registry describes, decodes their
9//! values, and encodes writes. Nothing here consults the network or storage.
10//!
11//! - [`MetadataFieldRef`] names a field: its component ID plus a label. The
12//!   well-known constants ([`MetadataFieldRef::GROUP_NAME`], …) cover the
13//!   public well-known fields. The core resolves a ref by ID only.
14//! - [`MetadataFieldDescriptor`] describes a listed field: its ref,
15//!   [`MetadataComponentType`], and committed permissions.
16//! - [`FieldValue`], [`FieldKey`], and [`MetadataValue`] carry typed values;
17//!   [`MetadataFieldValue`] and [`UserFieldValue`] pair them with a ref.
18//! - [`ComponentMutation`] and [`UserFieldUpdate`] describe writes.
19//!   [`FieldSnapshot::field_write`] and [`FieldSnapshot::user_data_writes`]
20//!   encode them as [`FieldWrite`]s, and [`FieldSnapshot::resolve_writes`]
21//!   turns those into `AppDataUpdate` operations against a dictionary.
22//!
23//! ## Authority
24//!
25//! The committed registry is the only authority for a field's existence and
26//! policy. A well-known field's type is fixed by the protocol ([`component_type`]);
27//! an application field's type is its registry entry's tag. The backend
28//! catalogue only supplies the label of an application field and so the name
29//! that [`FieldSnapshot::field`] matches. A registry entry that does not
30//! decode is preserved in the dictionary but is not a field.
31
32use std::{
33    borrow::Cow,
34    collections::{BTreeMap, BTreeSet},
35};
36
37use openmls::{extensions::AppDataDictionary, messages::proposals::AppDataUpdateOperation};
38use tls_codec::{Deserialize, Serialize, Size, VLBytes};
39use xmtp_configuration::ApplicationComponentDefinition;
40use xmtp_proto::xmtp::mls::message_contents::{ComponentPermissions, ComponentType};
41
42use crate::{
43    app_data::{
44        component_id::ComponentId,
45        component_registry::ComponentRegistry,
46        component_source::{ComponentSourceError, apply_app_data_update_payload, component_type},
47    },
48    inbox_id::InboxId,
49    tls_map::{TlsMap, TlsMapDelta},
50    tls_set::{TlsKeyHash, TlsSet, TlsSetDelta},
51};
52
53/// Names a metadata field: its component ID and, when one exists, its
54/// well-known or backend catalogue name.
55///
56/// The name is only a label. Every lookup resolves a ref by
57/// `component_id`, so a ref built on a client with another backend
58/// snapshot still names the same field. `==` compares the label too;
59/// compare `component_id`s to ask whether two refs name one field.
60#[derive(Debug, Clone, PartialEq, Eq, Hash)]
61pub struct MetadataFieldRef {
62    pub component_id: ComponentId,
63    pub name: Option<Cow<'static, str>>,
64}
65
66impl MetadataFieldRef {
67    pub const GROUP_NAME: Self = Self::well_known(ComponentId::GROUP_NAME);
68    pub const GROUP_DESCRIPTION: Self = Self::well_known(ComponentId::GROUP_DESCRIPTION);
69    pub const GROUP_IMAGE_URL: Self = Self::well_known(ComponentId::GROUP_IMAGE_URL);
70    pub const GROUP_IMAGE: Self = Self::well_known(ComponentId::GROUP_IMAGE);
71    pub const APP_DATA: Self = Self::well_known(ComponentId::APP_DATA);
72    pub const USER_DISPLAY_NAME: Self = Self::well_known(ComponentId::USER_DISPLAY_NAME);
73    pub const MESSAGE_DISAPPEAR_FROM_NS: Self =
74        Self::well_known(ComponentId::MESSAGE_DISAPPEAR_FROM_NS);
75    pub const MESSAGE_DISAPPEAR_IN_NS: Self =
76        Self::well_known(ComponentId::MESSAGE_DISAPPEAR_IN_NS);
77
78    /// The well-known fields an app may see, when the group registers them.
79    /// Registry, role, membership, protocol, signer, and bootstrap
80    /// components are internal.
81    // implements: META-069
82    pub const PUBLIC_WELL_KNOWN: [Self; 8] = [
83        Self::GROUP_NAME,
84        Self::GROUP_DESCRIPTION,
85        Self::GROUP_IMAGE_URL,
86        Self::GROUP_IMAGE,
87        Self::APP_DATA,
88        Self::USER_DISPLAY_NAME,
89        Self::MESSAGE_DISAPPEAR_FROM_NS,
90        Self::MESSAGE_DISAPPEAR_IN_NS,
91    ];
92
93    /// An unnamed ref to `component_id`.
94    pub const fn new(component_id: ComponentId) -> Self {
95        Self {
96            component_id,
97            name: None,
98        }
99    }
100
101    /// The ref of a well-known ID, named from [`ComponentId::WELL_KNOWN_NAMES`].
102    /// Fails to compile for an ID without a well-known name.
103    const fn well_known(id: ComponentId) -> Self {
104        let names = ComponentId::WELL_KNOWN_NAMES;
105        let mut i = 0;
106        while names[i].0.as_u16() != id.as_u16() {
107            i += 1;
108        }
109        Self {
110            component_id: id,
111            name: Some(Cow::Borrowed(names[i].1)),
112        }
113    }
114}
115
116/// The type of a scalar field or of a map value.
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub enum MetadataScalarType {
119    Bytes,
120    String,
121}
122
123/// The type of a map or set key.
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum MetadataKeyType {
126    Bytes,
127    InboxId,
128}
129
130/// The shape of a field's value.
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub enum MetadataComponentType {
133    Bytes,
134    String,
135    Map {
136        key_type: MetadataKeyType,
137        value_type: MetadataScalarType,
138    },
139    Set {
140        key_type: MetadataKeyType,
141    },
142    /// A registry type tag this build does not know. Its values can be
143    /// neither read nor written.
144    Unknown {
145        tag: i32,
146    },
147}
148
149impl MetadataComponentType {
150    /// The shape of a registry `component_type` tag.
151    pub fn from_tag(tag: i32) -> Self {
152        use MetadataKeyType as K;
153        use MetadataScalarType as S;
154        match ComponentType::try_from(tag) {
155            Ok(ComponentType::Bytes) => Self::Bytes,
156            Ok(ComponentType::String) => Self::String,
157            Ok(ComponentType::TlsMapBytesBytes) => Self::map(K::Bytes, S::Bytes),
158            Ok(ComponentType::TlsMapInboxIdBytes) => Self::map(K::InboxId, S::Bytes),
159            Ok(ComponentType::TlsMapInboxIdString) => Self::map(K::InboxId, S::String),
160            Ok(ComponentType::TlsSetBytes) => Self::Set { key_type: K::Bytes },
161            Ok(ComponentType::TlsSetInboxId) => Self::Set {
162                key_type: K::InboxId,
163            },
164            Ok(ComponentType::Unspecified) | Err(_) => Self::Unknown { tag },
165        }
166    }
167
168    /// The registry tag of this shape, or `None` for a shape no tag
169    /// describes (a map with byte keys and string values).
170    pub fn tag(self) -> Option<ComponentType> {
171        use MetadataKeyType as K;
172        use MetadataScalarType as S;
173        Some(match self {
174            Self::Bytes => ComponentType::Bytes,
175            Self::String => ComponentType::String,
176            Self::Map {
177                key_type: K::Bytes,
178                value_type: S::Bytes,
179            } => ComponentType::TlsMapBytesBytes,
180            Self::Map {
181                key_type: K::InboxId,
182                value_type: S::Bytes,
183            } => ComponentType::TlsMapInboxIdBytes,
184            Self::Map {
185                key_type: K::InboxId,
186                value_type: S::String,
187            } => ComponentType::TlsMapInboxIdString,
188            Self::Set { key_type: K::Bytes } => ComponentType::TlsSetBytes,
189            Self::Set {
190                key_type: K::InboxId,
191            } => ComponentType::TlsSetInboxId,
192            Self::Map {
193                key_type: K::Bytes,
194                value_type: S::String,
195            }
196            | Self::Unknown { .. } => return None,
197        })
198    }
199
200    const fn map(key_type: MetadataKeyType, value_type: MetadataScalarType) -> Self {
201        Self::Map {
202            key_type,
203            value_type,
204        }
205    }
206}
207
208/// A field listed by [`FieldSnapshot::fields`].
209#[derive(Debug, Clone, PartialEq)]
210pub struct MetadataFieldDescriptor {
211    pub field: MetadataFieldRef,
212    pub component_type: MetadataComponentType,
213    /// The committed insert, update, and delete policies.
214    pub permissions: ComponentPermissions,
215}
216
217impl MetadataFieldDescriptor {
218    /// Whether this is a per-user field: a map keyed by inbox ID.
219    // implements: META-069
220    pub fn is_user_field(&self) -> bool {
221        matches!(
222            self.component_type,
223            MetadataComponentType::Map {
224                key_type: MetadataKeyType::InboxId,
225                ..
226            }
227        )
228    }
229
230    /// The scalar type of a user field's values.
231    fn user_value_type(&self) -> Option<MetadataScalarType> {
232        match self.component_type {
233            MetadataComponentType::Map {
234                key_type: MetadataKeyType::InboxId,
235                value_type,
236            } => Some(value_type),
237            _ => None,
238        }
239    }
240}
241
242/// A scalar value or map value.
243#[derive(Debug, Clone, PartialEq, Eq)]
244pub enum FieldValue {
245    Bytes(Vec<u8>),
246    String(String),
247}
248
249/// A map or set key.
250#[derive(Debug, Clone, PartialEq, Eq)]
251pub enum FieldKey {
252    Bytes(Vec<u8>),
253    InboxId(InboxId),
254}
255
256/// A decoded field value.
257#[derive(Debug, Clone, PartialEq, Eq)]
258pub enum MetadataValue {
259    Scalar(FieldValue),
260    /// Entries in key order.
261    Map(Vec<MapEntry>),
262    /// Keys in key order.
263    Set(Vec<FieldKey>),
264}
265
266#[derive(Debug, Clone, PartialEq, Eq)]
267pub struct MapEntry {
268    pub key: FieldKey,
269    pub value: FieldValue,
270}
271
272/// A field and its value, absent when the group holds none.
273#[derive(Debug, Clone, PartialEq, Eq)]
274pub struct MetadataFieldValue {
275    pub field: MetadataFieldRef,
276    pub value: Option<MetadataValue>,
277}
278
279/// One inbox's value of a user field.
280#[derive(Debug, Clone, PartialEq, Eq)]
281pub struct UserFieldValue {
282    pub field: MetadataFieldRef,
283    pub value: FieldValue,
284}
285
286/// Set (`Some`) or clear (`None`) the caller's own entry of a user field.
287#[derive(Debug, Clone, PartialEq, Eq)]
288pub struct UserFieldUpdate {
289    pub field: MetadataFieldRef,
290    pub value: Option<FieldValue>,
291}
292
293/// One key-level change to a map field.
294#[derive(Debug, Clone, PartialEq, Eq)]
295pub enum MapMutation {
296    Insert(FieldKey, FieldValue),
297    Update(FieldKey, FieldValue),
298    Delete(FieldKey),
299}
300
301/// One key-level change to a set field.
302#[derive(Debug, Clone, PartialEq, Eq)]
303pub enum SetMutation {
304    Insert(FieldKey),
305    Delete(FieldKey),
306    /// Delete the key whose TLS serialization hashes (SHA-256) to this value.
307    DeleteByHash([u8; 32]),
308}
309
310/// A write to one field. Map and set deltas apply atomically: an Insert of
311/// a present key, or an Update or Delete of an absent key, rejects the
312/// whole commit.
313#[derive(Debug, Clone, PartialEq, Eq)]
314pub enum ComponentMutation {
315    /// Set a scalar field.
316    Replace(FieldValue),
317    /// Remove the whole component.
318    Remove,
319    MapDelta(Vec<MapMutation>),
320    SetDelta(Vec<SetMutation>),
321}
322
323/// An encoded write, fixed to the type it was encoded under.
324/// [`FieldSnapshot::resolve_writes`] refuses it if the field's type changed.
325#[derive(Debug, Clone, PartialEq, Eq)]
326pub struct FieldWrite {
327    pub component_id: ComponentId,
328    pub component_type: ComponentType,
329    pub operation: WriteOperation,
330}
331
332#[derive(Debug, Clone, PartialEq, Eq)]
333pub enum WriteOperation {
334    /// An `AppDataUpdate::Update` payload.
335    Update(Vec<u8>),
336    /// Remove the component if present.
337    Remove,
338    /// Insert or update the writer's own key of an inbox-keyed map with
339    /// these value bytes.
340    SetOwn(Vec<u8>),
341    /// Delete the writer's own key of an inbox-keyed map if present.
342    ClearOwn,
343}
344
345#[derive(Debug, thiserror::Error)]
346pub enum FieldError {
347    /// The group's registry lists no field with this ID.
348    #[error("no metadata field {0} in this group")]
349    UnknownField(ComponentId),
350    #[error("metadata field {0} is not a user field")]
351    NotUserField(ComponentId),
352    #[error("metadata field {0} appears more than once")]
353    DuplicateField(ComponentId),
354    #[error("metadata field {component_id} has unsupported type tag {tag}")]
355    UnsupportedType { component_id: ComponentId, tag: i32 },
356    /// A value, key, or mutation does not match the field's type.
357    #[error("value does not match the type of metadata field {0}")]
358    TypeMismatch(ComponentId),
359    /// The field's committed type changed after the write was encoded.
360    #[error("metadata field {component_id} changed type from {expected:?} to {actual:?}")]
361    TypeChanged {
362        component_id: ComponentId,
363        expected: ComponentType,
364        actual: MetadataComponentType,
365    },
366    /// The committed registry's policies deny this write.
367    #[error("the group's policy denies this write to metadata field {0}")]
368    Denied(ComponentId),
369    #[error(transparent)]
370    Component(#[from] ComponentSourceError),
371}
372
373impl From<tls_codec::Error> for FieldError {
374    fn from(error: tls_codec::Error) -> Self {
375        ComponentSourceError::from(error).into()
376    }
377}
378
379/// The metadata fields of one dictionary and their values.
380pub struct FieldSnapshot<'a> {
381    dictionary: Option<&'a AppDataDictionary>,
382    registry: ComponentRegistry,
383    fields: Vec<MetadataFieldDescriptor>,
384}
385
386impl<'a> FieldSnapshot<'a> {
387    /// List the fields of `dictionary`'s registry, labelling each
388    /// application field with the first `catalogue` definition of its ID
389    /// when that definition is valid, as registration does. `None` is a
390    /// group without a dictionary. [`ComponentRegistry::iter`] already
391    /// skips an entry missing any of its three member policies, so every
392    /// listed field has all three.
393    // implements: META-069
394    pub fn new(
395        dictionary: Option<&'a AppDataDictionary>,
396        catalogue: &[ApplicationComponentDefinition],
397    ) -> Result<Self, FieldError> {
398        let registry =
399            match dictionary.and_then(|d| d.get(&ComponentId::COMPONENT_REGISTRY.as_u16())) {
400                Some(bytes) => ComponentRegistry::from_bytes(bytes).map_err(|e| {
401                    ComponentSourceError::MalformedComponentValue {
402                        component_id: ComponentId::COMPONENT_REGISTRY,
403                        reason: format!("registry decode: {e}"),
404                    }
405                })?,
406                None => ComponentRegistry::new(),
407            };
408        let fields = registry
409            .iter()
410            .filter_map(Result::ok)
411            .filter_map(|(id, meta)| {
412                let (name, component_type) = if id.is_app_range() {
413                    let name = catalogue
414                        .iter()
415                        .find(|d| d.component_id == id.as_u16())
416                        .filter(|d| d.validate().is_ok())
417                        .map(|d| Cow::Owned(d.name.clone()));
418                    (name, MetadataComponentType::from_tag(meta.component_type))
419                } else {
420                    let known = MetadataFieldRef::PUBLIC_WELL_KNOWN
421                        .into_iter()
422                        .find(|f| f.component_id == id)?;
423                    (known.name, component_type(id)?.into())
424                };
425                Some(MetadataFieldDescriptor {
426                    field: MetadataFieldRef {
427                        component_id: id,
428                        name,
429                    },
430                    component_type,
431                    permissions: meta.permissions?,
432                })
433            })
434            .collect();
435        Ok(Self {
436            dictionary,
437            registry,
438            fields,
439        })
440    }
441
442    /// The group's fields in component ID order.
443    pub fn fields(&self) -> &[MetadataFieldDescriptor] {
444        &self.fields
445    }
446
447    /// The listed field named `name`. Well-known IDs sort before
448    /// application IDs, so a well-known field wins a name clash.
449    // implements: META-069
450    pub fn field(&self, name: &str) -> Option<&MetadataFieldDescriptor> {
451        self.fields
452            .iter()
453            .find(|f| f.field.name.as_deref() == Some(name))
454    }
455
456    /// The registry the fields come from.
457    pub fn registry(&self) -> &ComponentRegistry {
458        &self.registry
459    }
460
461    /// The listed field with `field`'s ID; its name is ignored.
462    pub fn resolve(
463        &self,
464        field: &MetadataFieldRef,
465    ) -> Result<&MetadataFieldDescriptor, FieldError> {
466        self.resolve_id(field.component_id)
467    }
468
469    fn resolve_id(&self, id: ComponentId) -> Result<&MetadataFieldDescriptor, FieldError> {
470        self.fields
471            .iter()
472            .find(|f| f.field.component_id == id)
473            .ok_or(FieldError::UnknownField(id))
474    }
475
476    /// The value of `field`, `None` when absent.
477    // implements: META-070
478    pub fn value(&self, field: &MetadataFieldRef) -> Result<Option<MetadataValue>, FieldError> {
479        let descriptor = self.resolve(field)?;
480        let id = descriptor.field.component_id;
481        self.bytes(id)
482            .map(|bytes| decode(id, descriptor.component_type, bytes))
483            .transpose()
484    }
485
486    /// The values of `fields`, in request order. Each record carries the
487    /// listed ref, whatever name the request used.
488    // implements: META-070
489    pub fn values(
490        &self,
491        fields: &[MetadataFieldRef],
492    ) -> Result<Vec<MetadataFieldValue>, FieldError> {
493        fields
494            .iter()
495            .map(|field| {
496                Ok(MetadataFieldValue {
497                    field: self.resolve(field)?.field.clone(),
498                    value: self.value(field)?,
499                })
500            })
501            .collect()
502    }
503
504    /// The value under `key` of the map `field`.
505    // implements: META-070
506    pub fn map_value(
507        &self,
508        field: &MetadataFieldRef,
509        key: &FieldKey,
510    ) -> Result<Option<FieldValue>, FieldError> {
511        let descriptor = self.resolve(field)?;
512        let id = descriptor.field.component_id;
513        match (descriptor.component_type, key) {
514            (MetadataComponentType::Unknown { tag }, _) => {
515                return Err(FieldError::UnsupportedType {
516                    component_id: id,
517                    tag,
518                });
519            }
520            (
521                MetadataComponentType::Map {
522                    key_type: MetadataKeyType::Bytes,
523                    ..
524                },
525                FieldKey::Bytes(_),
526            )
527            | (
528                MetadataComponentType::Map {
529                    key_type: MetadataKeyType::InboxId,
530                    ..
531                },
532                FieldKey::InboxId(_),
533            ) => {}
534            _ => return Err(FieldError::TypeMismatch(id)),
535        }
536        let Some(MetadataValue::Map(entries)) = self.value(field)? else {
537            return Ok(None);
538        };
539        Ok(entries
540            .into_iter()
541            .find(|entry| entry.key == *key)
542            .map(|entry| entry.value))
543    }
544
545    /// Per-inbox values of user fields. `fields = None` selects every user
546    /// field in ID order; `inbox_ids = None` selects the `DM_MEMBERS` pair
547    /// when present (only a DM holds it) and the
548    /// `GROUP_MEMBERSHIP` inboxes otherwise. Every selected inbox has an
549    /// entry, empty when it holds no selected value. A field whose value
550    /// does not decode under its committed type contributes no values; a
551    /// field named twice is refused.
552    // implements: META-072
553    pub fn user_data(
554        &self,
555        fields: Option<&[MetadataFieldRef]>,
556        inbox_ids: Option<&[InboxId]>,
557    ) -> Result<BTreeMap<InboxId, Vec<UserFieldValue>>, FieldError> {
558        if let Some(id) =
559            fields.and_then(|refs| first_duplicate(refs.iter().map(|f| f.component_id)))
560        {
561            return Err(FieldError::DuplicateField(id));
562        }
563        let fields = match fields {
564            None => self.fields.iter().filter(|f| f.is_user_field()).collect(),
565            Some(refs) => refs
566                .iter()
567                .map(|field| {
568                    let descriptor = self.resolve(field)?;
569                    if descriptor.is_user_field() {
570                        Ok(descriptor)
571                    } else {
572                        Err(FieldError::NotUserField(field.component_id))
573                    }
574                })
575                .collect::<Result<Vec<_>, _>>()?,
576        };
577        let mut data: BTreeMap<InboxId, Vec<UserFieldValue>> = match inbox_ids {
578            Some(ids) => ids.iter().map(|id| (*id, Vec::new())).collect(),
579            None => self.default_inboxes()?.map(|id| (id, Vec::new())).collect(),
580        };
581        for descriptor in fields {
582            // A registry update may re-type a field over its stored value, so
583            // a value can fail to decode; it holds no typed values to return,
584            // and must not hide every other field's.
585            let entries = match self.value(&descriptor.field) {
586                Ok(Some(MetadataValue::Map(entries))) => entries,
587                Ok(_) => continue,
588                Err(error) => {
589                    tracing::warn!(field = %descriptor.field.component_id, %error, "skipping undecodable user field");
590                    continue;
591                }
592            };
593            for MapEntry { key, value } in entries {
594                if let FieldKey::InboxId(inbox) = key
595                    && let Some(values) = data.get_mut(&inbox)
596                {
597                    values.push(UserFieldValue {
598                        field: descriptor.field.clone(),
599                        value,
600                    });
601                }
602            }
603        }
604        Ok(data)
605    }
606
607    fn default_inboxes(&self) -> Result<impl Iterator<Item = InboxId>, FieldError> {
608        let inboxes: Vec<InboxId> = match self.bytes(ComponentId::DM_MEMBERS) {
609            Some(bytes) => TlsSet::<InboxId>::tls_deserialize_exact(bytes)
610                .map_err(|e| malformed(ComponentId::DM_MEMBERS, e))?
611                .into_iter()
612                .collect(),
613            None => self
614                .bytes(ComponentId::GROUP_MEMBERSHIP)
615                .map(TlsMap::<InboxId, VLBytes>::tls_deserialize_exact)
616                .transpose()
617                .map_err(|e| malformed(ComponentId::GROUP_MEMBERSHIP, e))?
618                .into_iter()
619                .flat_map(|map| map.into_iter().map(|(inbox, _)| inbox))
620                .collect(),
621        };
622        Ok(inboxes.into_iter())
623    }
624
625    /// Encode `mutation` of `field` under the field's committed type.
626    // implements: META-071
627    pub fn field_write(
628        &self,
629        field: &MetadataFieldRef,
630        mutation: &ComponentMutation,
631    ) -> Result<FieldWrite, FieldError> {
632        use MetadataComponentType as T;
633        use MetadataKeyType as K;
634        let descriptor = self.resolve(field)?;
635        let id = descriptor.field.component_id;
636        let ty = descriptor.component_type;
637        let component_type = wire_type(id, ty)?;
638        let payload = match (ty, mutation) {
639            (_, ComponentMutation::Remove) => {
640                return Ok(FieldWrite {
641                    component_id: id,
642                    component_type,
643                    operation: WriteOperation::Remove,
644                });
645            }
646            (T::Bytes, ComponentMutation::Replace(value)) => {
647                scalar(MetadataScalarType::Bytes, value).map(|b| Ok(b.to_vec()))
648            }
649            (T::String, ComponentMutation::Replace(value)) => {
650                scalar(MetadataScalarType::String, value).map(|b| Ok(b.to_vec()))
651            }
652            (
653                T::Map {
654                    key_type: K::Bytes,
655                    value_type,
656                },
657                ComponentMutation::MapDelta(mutations),
658            ) => map_delta(mutations, value_type, bytes_key).map(|d| d.tls_serialize_detached()),
659            (
660                T::Map {
661                    key_type: K::InboxId,
662                    value_type,
663                },
664                ComponentMutation::MapDelta(mutations),
665            ) => map_delta(mutations, value_type, inbox_key).map(|d| d.tls_serialize_detached()),
666            (T::Set { key_type: K::Bytes }, ComponentMutation::SetDelta(mutations)) => {
667                set_delta(mutations, bytes_key).map(|d| d.tls_serialize_detached())
668            }
669            (
670                T::Set {
671                    key_type: K::InboxId,
672                },
673                ComponentMutation::SetDelta(mutations),
674            ) => set_delta(mutations, inbox_key).map(|d| d.tls_serialize_detached()),
675            _ => None,
676        }
677        .ok_or(FieldError::TypeMismatch(id))??;
678        Ok(FieldWrite {
679            component_id: id,
680            component_type,
681            operation: WriteOperation::Update(payload),
682        })
683    }
684
685    /// Encode the writer's own-key sets and clears of user fields.
686    // implements: META-073
687    pub fn user_data_writes(
688        &self,
689        values: &[UserFieldUpdate],
690    ) -> Result<Vec<FieldWrite>, FieldError> {
691        if let Some(id) = first_duplicate(values.iter().map(|v| v.field.component_id)) {
692            return Err(FieldError::DuplicateField(id));
693        }
694        values
695            .iter()
696            .map(|UserFieldUpdate { field, value }| {
697                let descriptor = self.resolve(field)?;
698                let id = descriptor.field.component_id;
699                let value_type = descriptor
700                    .user_value_type()
701                    .ok_or(FieldError::NotUserField(id))?;
702                let operation = match value {
703                    Some(value) => WriteOperation::SetOwn(
704                        scalar(value_type, value)
705                            .ok_or(FieldError::TypeMismatch(id))?
706                            .to_vec(),
707                    ),
708                    None => WriteOperation::ClearOwn,
709                };
710                Ok(FieldWrite {
711                    component_id: id,
712                    component_type: wire_type(id, descriptor.component_type)?,
713                    operation,
714                })
715            })
716            .collect()
717    }
718
719    /// The `AppDataUpdate` operations that carry out `writes` for the
720    /// inbox `own`, given the current component values in `values`: this
721    /// snapshot's dictionary with any pending updates applied.
722    ///
723    /// Types and the field list come from this snapshot, which must be the
724    /// committed one. Each write is refused if its field is gone
725    /// or changed type. Every payload is applied to its current value, so
726    /// bounds, UTF-8, and key presence fail here rather than in the commit.
727    /// A write that changes nothing is dropped: a clear of an absent key, a
728    /// removal of an absent component, or an update whose result equals the
729    /// current bytes.
730    // implements: META-071, META-073
731    pub fn resolve_writes(
732        &self,
733        values: Option<&AppDataDictionary>,
734        own: InboxId,
735        writes: &[FieldWrite],
736    ) -> Result<Vec<(ComponentId, AppDataUpdateOperation)>, FieldError> {
737        self.operations(values, own, writes, false)
738    }
739
740    /// The operations of `writes` as [`Self::resolve_writes`] gives them,
741    /// keeping an update whose result equals the current bytes: the
742    /// operations a receiver would check if they were published.
743    // implements: META-071
744    pub fn write_operations(
745        &self,
746        values: Option<&AppDataDictionary>,
747        own: InboxId,
748        writes: &[FieldWrite],
749    ) -> Result<Vec<(ComponentId, AppDataUpdateOperation)>, FieldError> {
750        self.operations(values, own, writes, true)
751    }
752
753    fn operations(
754        &self,
755        values: Option<&AppDataDictionary>,
756        own: InboxId,
757        writes: &[FieldWrite],
758        keep_unchanged: bool,
759    ) -> Result<Vec<(ComponentId, AppDataUpdateOperation)>, FieldError> {
760        if let Some(id) = first_duplicate(writes.iter().map(|w| w.component_id)) {
761            return Err(FieldError::DuplicateField(id));
762        }
763        writes
764            .iter()
765            .filter_map(|write| {
766                self.resolve_write(values, own, write, keep_unchanged)
767                    .map(|op| op.map(|op| (write.component_id, op)))
768                    .transpose()
769            })
770            .collect()
771    }
772
773    fn resolve_write(
774        &self,
775        values: Option<&AppDataDictionary>,
776        own: InboxId,
777        write: &FieldWrite,
778        keep_unchanged: bool,
779    ) -> Result<Option<AppDataUpdateOperation>, FieldError> {
780        let id = write.component_id;
781        let descriptor = self.resolve_id(id)?;
782        let actual = descriptor.component_type;
783        if actual.tag() != Some(write.component_type) {
784            return Err(FieldError::TypeChanged {
785                component_id: id,
786                expected: write.component_type,
787                actual,
788            });
789        }
790        let own_key = matches!(
791            write.operation,
792            WriteOperation::SetOwn(_) | WriteOperation::ClearOwn
793        );
794        if own_key && !descriptor.is_user_field() {
795            return Err(FieldError::TypeMismatch(id));
796        }
797        let current = values.and_then(|d| d.get(&id.as_u16()));
798        let owned = || -> Result<bool, FieldError> {
799            Ok(current
800                .map(TlsMap::<InboxId, VLBytes>::tls_deserialize_exact)
801                .transpose()
802                .map_err(|e| malformed(id, e))?
803                .is_some_and(|map| map.contains_key(&own)))
804        };
805        let delta = TlsMapDelta::<InboxId, VLBytes>::new();
806        let payload = match &write.operation {
807            WriteOperation::Remove => return Ok(current.map(|_| AppDataUpdateOperation::Remove)),
808            WriteOperation::Update(payload) => payload.clone(),
809            WriteOperation::SetOwn(value) if owned()? => delta
810                .update(own, value.as_slice().into())
811                .tls_serialize_detached()?,
812            WriteOperation::SetOwn(value) => delta
813                .insert(own, value.as_slice().into())
814                .tls_serialize_detached()?,
815            WriteOperation::ClearOwn if owned()? => delta.delete(own).tls_serialize_detached()?,
816            WriteOperation::ClearOwn => return Ok(None),
817        };
818        let next = apply_app_data_update_payload(id, &payload, current, &self.registry)?;
819        Ok((keep_unchanged || current != Some(next.as_slice()))
820            .then(|| AppDataUpdateOperation::Update(payload.into())))
821    }
822
823    fn bytes(&self, id: ComponentId) -> Option<&'a [u8]> {
824        self.dictionary.and_then(|d| d.get(&id.as_u16()))
825    }
826}
827
828impl From<ComponentType> for MetadataComponentType {
829    fn from(ty: ComponentType) -> Self {
830        Self::from_tag(ty as i32)
831    }
832}
833
834/// The registry tag a write to field `id` of shape `ty` is encoded under.
835fn wire_type(id: ComponentId, ty: MetadataComponentType) -> Result<ComponentType, FieldError> {
836    ty.tag().ok_or(FieldError::UnsupportedType {
837        component_id: id,
838        tag: match ty {
839            MetadataComponentType::Unknown { tag } => tag,
840            _ => ComponentType::Unspecified as i32,
841        },
842    })
843}
844
845fn decode(
846    id: ComponentId,
847    ty: MetadataComponentType,
848    bytes: &[u8],
849) -> Result<MetadataValue, FieldError> {
850    use MetadataComponentType as T;
851    use MetadataKeyType as K;
852    let value = |ty, bytes: VLBytes| field_value(id, ty, bytes.into());
853    let tls = |e| malformed(id, e);
854    Ok(match ty {
855        T::Bytes => {
856            MetadataValue::Scalar(field_value(id, MetadataScalarType::Bytes, bytes.into())?)
857        }
858        T::String => {
859            MetadataValue::Scalar(field_value(id, MetadataScalarType::String, bytes.into())?)
860        }
861        T::Map {
862            key_type: K::Bytes,
863            value_type,
864        } => MetadataValue::Map(
865            TlsMap::<VLBytes, VLBytes>::tls_deserialize_exact(bytes)
866                .map_err(tls)?
867                .into_iter()
868                .map(|(key, v)| {
869                    Ok(MapEntry {
870                        key: FieldKey::Bytes(key.into()),
871                        value: value(value_type, v)?,
872                    })
873                })
874                .collect::<Result<_, FieldError>>()?,
875        ),
876        T::Map {
877            key_type: K::InboxId,
878            value_type,
879        } => MetadataValue::Map(
880            TlsMap::<InboxId, VLBytes>::tls_deserialize_exact(bytes)
881                .map_err(tls)?
882                .into_iter()
883                .map(|(key, v)| {
884                    Ok(MapEntry {
885                        key: FieldKey::InboxId(key),
886                        value: value(value_type, v)?,
887                    })
888                })
889                .collect::<Result<_, FieldError>>()?,
890        ),
891        T::Set { key_type: K::Bytes } => MetadataValue::Set(
892            TlsSet::<VLBytes>::tls_deserialize_exact(bytes)
893                .map_err(tls)?
894                .into_iter()
895                .map(|key| FieldKey::Bytes(key.into()))
896                .collect(),
897        ),
898        T::Set {
899            key_type: K::InboxId,
900        } => MetadataValue::Set(
901            TlsSet::<InboxId>::tls_deserialize_exact(bytes)
902                .map_err(tls)?
903                .into_iter()
904                .map(FieldKey::InboxId)
905                .collect(),
906        ),
907        T::Unknown { tag } => {
908            return Err(FieldError::UnsupportedType {
909                component_id: id,
910                tag,
911            });
912        }
913    })
914}
915
916fn field_value(
917    id: ComponentId,
918    ty: MetadataScalarType,
919    bytes: Vec<u8>,
920) -> Result<FieldValue, FieldError> {
921    Ok(match ty {
922        MetadataScalarType::Bytes => FieldValue::Bytes(bytes),
923        MetadataScalarType::String => {
924            FieldValue::String(String::from_utf8(bytes).map_err(|e| {
925                ComponentSourceError::MalformedComponentValue {
926                    component_id: id,
927                    reason: format!("invalid UTF-8: {e}"),
928                }
929            })?)
930        }
931    })
932}
933
934fn malformed(id: ComponentId, error: tls_codec::Error) -> FieldError {
935    ComponentSourceError::MalformedComponentValue {
936        component_id: id,
937        reason: format!("TLS decode: {error}"),
938    }
939    .into()
940}
941
942/// The bytes of `value` if it has type `ty`.
943fn scalar(ty: MetadataScalarType, value: &FieldValue) -> Option<&[u8]> {
944    match (ty, value) {
945        (MetadataScalarType::Bytes, FieldValue::Bytes(bytes)) => Some(bytes),
946        (MetadataScalarType::String, FieldValue::String(s)) => Some(s.as_bytes()),
947        _ => None,
948    }
949}
950
951fn bytes_key(key: &FieldKey) -> Option<VLBytes> {
952    match key {
953        FieldKey::Bytes(bytes) => Some(bytes.as_slice().into()),
954        FieldKey::InboxId(_) => None,
955    }
956}
957
958fn inbox_key(key: &FieldKey) -> Option<InboxId> {
959    match key {
960        FieldKey::InboxId(inbox) => Some(*inbox),
961        FieldKey::Bytes(_) => None,
962    }
963}
964
965/// The delta of `mutations`, or `None` if a key or value has the wrong type.
966fn map_delta<K>(
967    mutations: &[MapMutation],
968    value_type: MetadataScalarType,
969    key: fn(&FieldKey) -> Option<K>,
970) -> Option<TlsMapDelta<K, VLBytes>> {
971    mutations
972        .iter()
973        .try_fold(TlsMapDelta::new(), |delta, mutation| {
974            Some(match mutation {
975                MapMutation::Insert(k, v) => delta.insert(key(k)?, scalar(value_type, v)?.into()),
976                MapMutation::Update(k, v) => delta.update(key(k)?, scalar(value_type, v)?.into()),
977                MapMutation::Delete(k) => delta.delete(key(k)?),
978            })
979        })
980}
981
982/// The delta of `mutations`, or `None` if a key has the wrong type.
983fn set_delta<K: Serialize + Size>(
984    mutations: &[SetMutation],
985    key: fn(&FieldKey) -> Option<K>,
986) -> Option<TlsSetDelta<K>> {
987    mutations
988        .iter()
989        .try_fold(TlsSetDelta::new(), |delta, mutation| {
990            Some(match mutation {
991                SetMutation::Insert(k) => delta.insert(key(k)?),
992                SetMutation::Delete(k) => delta.remove(key(k)?),
993                SetMutation::DeleteByHash(hash) => {
994                    delta.remove_by_hash(TlsKeyHash::from_bytes(*hash))
995                }
996            })
997        })
998}
999
1000fn first_duplicate(ids: impl IntoIterator<Item = ComponentId>) -> Option<ComponentId> {
1001    let mut seen = BTreeSet::new();
1002    ids.into_iter().find(|id| !seen.insert(*id))
1003}
1004
1005#[cfg(test)]
1006mod tests {
1007    use prost::Message;
1008    use xmtp_configuration::{
1009        ComponentPermissions as CataloguePermissions, MetadataPolicy as CataloguePolicy,
1010    };
1011    use xmtp_proto::xmtp::mls::message_contents::{
1012        ComponentMetadata, MetadataPolicy,
1013        metadata_policy::{Kind, MetadataBasePolicy},
1014    };
1015
1016    use super::*;
1017
1018    const STATUS: ComponentId = ComponentId::new(0xC001);
1019    const SCORES: ComponentId = ComponentId::new(0xC002);
1020    const FUTURE: ComponentId = ComponentId::new(0xC003);
1021    const BROKEN: ComponentId = ComponentId::new(0xC004);
1022    const TAGS: ComponentId = ComponentId::new(0xC005);
1023    const LINKS: ComponentId = ComponentId::new(0xC006);
1024    const PARTIAL: ComponentId = ComponentId::new(0xC007);
1025
1026    fn inbox(tag: u8) -> InboxId {
1027        InboxId::from_bytes([tag; 32])
1028    }
1029
1030    fn policy(base: MetadataBasePolicy) -> Option<MetadataPolicy> {
1031        Some(MetadataPolicy {
1032            kind: Some(Kind::Base(base as i32)),
1033        })
1034    }
1035
1036    fn permissions(base: MetadataBasePolicy) -> ComponentPermissions {
1037        ComponentPermissions {
1038            insert_policy: policy(base),
1039            update_policy: policy(base),
1040            delete_policy: policy(base),
1041        }
1042    }
1043
1044    /// A registry entry with `tag` and every policy `Allow`.
1045    fn entry(tag: i32) -> Vec<u8> {
1046        ComponentMetadata {
1047            permissions: Some(permissions(MetadataBasePolicy::Allow)),
1048            component_type: tag,
1049            external_committer_permissions: None,
1050        }
1051        .encode_to_vec()
1052    }
1053
1054    fn inbox_map(entries: &[(InboxId, &[u8])]) -> Vec<u8> {
1055        let mut map = TlsMap::<InboxId, VLBytes>::new();
1056        for (key, value) in entries {
1057            map.set(*key, value.to_vec().into());
1058        }
1059        map.tls_serialize_detached().unwrap()
1060    }
1061
1062    fn inbox_set(inboxes: &[InboxId]) -> Vec<u8> {
1063        TlsSet::from_iter(inboxes.iter().copied())
1064            .tls_serialize_detached()
1065            .unwrap()
1066    }
1067
1068    /// A dictionary whose registry holds `registry` verbatim and whose other
1069    /// components hold `values`.
1070    fn dictionary(
1071        registry: &[(ComponentId, Vec<u8>)],
1072        values: &[(ComponentId, Vec<u8>)],
1073    ) -> AppDataDictionary {
1074        let mut entries = TlsMap::<ComponentId, VLBytes>::new();
1075        for (id, raw) in registry {
1076            entries.set(*id, raw.clone().into());
1077        }
1078        let mut dictionary = AppDataDictionary::new();
1079        dictionary.insert(
1080            ComponentId::COMPONENT_REGISTRY.as_u16(),
1081            entries.tls_serialize_detached().unwrap(),
1082        );
1083        for (id, value) in values {
1084            dictionary.insert(id.as_u16(), value.clone());
1085        }
1086        dictionary
1087    }
1088
1089    fn tag(ty: ComponentType) -> i32 {
1090        ty as i32
1091    }
1092
1093    /// Members a and b; c held a display name before it left.
1094    fn group() -> AppDataDictionary {
1095        dictionary(
1096            &[
1097                (
1098                    ComponentId::GROUP_MEMBERSHIP,
1099                    entry(tag(ComponentType::TlsMapInboxIdBytes)),
1100                ),
1101                (ComponentId::GROUP_NAME, entry(tag(ComponentType::String))),
1102                (
1103                    ComponentId::GROUP_DESCRIPTION,
1104                    entry(tag(ComponentType::String)),
1105                ),
1106                (
1107                    ComponentId::MIN_SUPPORTED_PROTOCOL_VERSION,
1108                    entry(tag(ComponentType::String)),
1109                ),
1110                // A registry tag that disagrees with the protocol type.
1111                (
1112                    ComponentId::USER_DISPLAY_NAME,
1113                    entry(tag(ComponentType::Bytes)),
1114                ),
1115                (STATUS, entry(tag(ComponentType::String))),
1116                (SCORES, entry(tag(ComponentType::TlsMapInboxIdBytes))),
1117                (FUTURE, entry(99)),
1118                (BROKEN, vec![0xFF, 0xFF]),
1119                (TAGS, entry(tag(ComponentType::TlsSetInboxId))),
1120                (LINKS, entry(tag(ComponentType::TlsMapBytesBytes))),
1121                // An entry without a delete policy.
1122                (
1123                    PARTIAL,
1124                    ComponentMetadata {
1125                        permissions: Some(ComponentPermissions {
1126                            delete_policy: None,
1127                            ..permissions(MetadataBasePolicy::Allow)
1128                        }),
1129                        component_type: tag(ComponentType::String),
1130                        external_committer_permissions: None,
1131                    }
1132                    .encode_to_vec(),
1133                ),
1134            ],
1135            &[
1136                (
1137                    ComponentId::GROUP_MEMBERSHIP,
1138                    inbox_map(&[(inbox(0xA), b""), (inbox(0xB), b"")]),
1139                ),
1140                (ComponentId::GROUP_NAME, b"Team".to_vec()),
1141                (
1142                    ComponentId::USER_DISPLAY_NAME,
1143                    inbox_map(&[(inbox(0xA), b"Alix"), (inbox(0xC), b"Carol")]),
1144                ),
1145                (SCORES, inbox_map(&[(inbox(0xA), &[7])])),
1146                (FUTURE, vec![9]),
1147                (BROKEN, vec![1, 2, 3]),
1148                (TAGS, inbox_set(&[inbox(0xB)])),
1149            ],
1150        )
1151    }
1152
1153    /// The backend catalogue of a client whose snapshot disagrees with the
1154    /// group: it types `STATUS` as bytes and names it after a well-known
1155    /// field, and its first definition of `SCORES` is invalid, so a later
1156    /// valid one must not name it either.
1157    fn catalogue() -> Vec<ApplicationComponentDefinition> {
1158        let allow = Some(CataloguePolicy::Base(MetadataBasePolicy::Allow as i32));
1159        let definition = |id: ComponentId, name: &str| ApplicationComponentDefinition {
1160            component_id: id.as_u16(),
1161            name: name.into(),
1162            component_type: tag(ComponentType::Bytes),
1163            permissions: CataloguePermissions {
1164                insert: allow.clone(),
1165                update: allow.clone(),
1166                delete: allow.clone(),
1167            },
1168            in_groups: true,
1169            in_dms: true,
1170        };
1171        vec![
1172            definition(STATUS, "GROUP_NAME"),
1173            definition(SCORES, ""),
1174            definition(SCORES, "scores"),
1175        ]
1176    }
1177
1178    fn snapshot(dictionary: &AppDataDictionary) -> FieldSnapshot<'_> {
1179        FieldSnapshot::new(Some(dictionary), &catalogue()).unwrap()
1180    }
1181
1182    fn named(id: ComponentId, name: &'static str) -> MetadataFieldRef {
1183        MetadataFieldRef {
1184            component_id: id,
1185            name: Some(Cow::Borrowed(name)),
1186        }
1187    }
1188
1189    fn string(s: &str) -> FieldValue {
1190        FieldValue::String(s.into())
1191    }
1192
1193    /// The listing holds the valid application entries and the registered
1194    /// public well-known fields, in ID order. A well-known field's type
1195    /// is the protocol's even when its registry tag disagrees; an application
1196    /// field's type is its registry tag, not the catalogue's; an unknown tag
1197    /// is `Unknown`; internal and malformed entries, and entries missing a
1198    /// member policy, are not fields.
1199    // verifies: META-069
1200    #[xmtp_common::test(unwrap_try = true)]
1201    fn fields_describe_the_committed_registry() {
1202        use MetadataComponentType as T;
1203        let dictionary = group();
1204        let fields = snapshot(&dictionary);
1205        let described: Vec<_> = fields
1206            .fields()
1207            .iter()
1208            .map(|f| (f.field.clone(), f.component_type, f.is_user_field()))
1209            .collect();
1210        let string_map = T::Map {
1211            key_type: MetadataKeyType::InboxId,
1212            value_type: MetadataScalarType::String,
1213        };
1214        let bytes_map = T::Map {
1215            key_type: MetadataKeyType::InboxId,
1216            value_type: MetadataScalarType::Bytes,
1217        };
1218        assert_eq!(
1219            described,
1220            vec![
1221                (MetadataFieldRef::GROUP_NAME, T::String, false),
1222                (MetadataFieldRef::GROUP_DESCRIPTION, T::String, false),
1223                (MetadataFieldRef::USER_DISPLAY_NAME, string_map, true),
1224                (named(STATUS, "GROUP_NAME"), T::String, false),
1225                (MetadataFieldRef::new(SCORES), bytes_map, true),
1226                (MetadataFieldRef::new(FUTURE), T::Unknown { tag: 99 }, false),
1227                (
1228                    MetadataFieldRef::new(TAGS),
1229                    T::Set {
1230                        key_type: MetadataKeyType::InboxId
1231                    },
1232                    false
1233                ),
1234                (
1235                    MetadataFieldRef::new(LINKS),
1236                    T::Map {
1237                        key_type: MetadataKeyType::Bytes,
1238                        value_type: MetadataScalarType::Bytes
1239                    },
1240                    false
1241                ),
1242            ]
1243        );
1244        assert!(
1245            fields
1246                .fields()
1247                .iter()
1248                .all(|f| f.permissions == permissions(MetadataBasePolicy::Allow))
1249        );
1250        assert!(
1251            FieldSnapshot::new(None, &catalogue())
1252                .unwrap()
1253                .fields()
1254                .is_empty()
1255        );
1256    }
1257
1258    /// A descriptor's permissions are the committed entry's, not the
1259    /// catalogue's.
1260    // verifies: META-069
1261    #[xmtp_common::test(unwrap_try = true)]
1262    fn permissions_come_from_the_registry() {
1263        let mut raw =
1264            ComponentMetadata::decode(entry(tag(ComponentType::String)).as_slice()).unwrap();
1265        raw.permissions = Some(permissions(MetadataBasePolicy::AllowIfAdmin));
1266        let dictionary = dictionary(&[(STATUS, raw.encode_to_vec())], &[]);
1267        let fields = snapshot(&dictionary);
1268        assert_eq!(
1269            fields.fields()[0].permissions,
1270            permissions(MetadataBasePolicy::AllowIfAdmin)
1271        );
1272    }
1273
1274    /// A name lookup prefers the well-known field when an application field
1275    /// has the same name, and finds nothing for an unlisted name.
1276    // verifies: META-069
1277    #[xmtp_common::test(unwrap_try = true)]
1278    fn name_lookup_prefers_the_well_known_field() {
1279        let dictionary = group();
1280        let fields = snapshot(&dictionary);
1281        assert_eq!(
1282            fields.field("GROUP_NAME").map(|f| &f.field),
1283            Some(&MetadataFieldRef::GROUP_NAME)
1284        );
1285        assert_eq!(
1286            fields.field("USER_DISPLAY_NAME").map(|f| &f.field),
1287            Some(&MetadataFieldRef::USER_DISPLAY_NAME)
1288        );
1289        assert!(fields.field("MIN_SUPPORTED_PROTOCOL_VERSION").is_none());
1290        assert!(fields.field("GROUP_IMAGE").is_none());
1291        assert!(fields.field("nothing").is_none());
1292    }
1293
1294    /// A ref resolves by ID alone: another name or none reads the same
1295    /// field, and a batch record carries the listed ref.
1296    // verifies: META-070
1297    #[xmtp_common::test(unwrap_try = true)]
1298    fn refs_resolve_by_id() {
1299        let dictionary = group();
1300        let fields = snapshot(&dictionary);
1301        let team = Some(MetadataValue::Scalar(string("Team")));
1302        for field in [
1303            MetadataFieldRef::GROUP_NAME,
1304            MetadataFieldRef::new(ComponentId::GROUP_NAME),
1305            named(ComponentId::GROUP_NAME, "STATUS"),
1306        ] {
1307            assert_eq!(fields.value(&field).unwrap(), team);
1308            assert_eq!(
1309                fields.values(std::slice::from_ref(&field)).unwrap(),
1310                vec![MetadataFieldValue {
1311                    field: MetadataFieldRef::GROUP_NAME,
1312                    value: team.clone(),
1313                }]
1314            );
1315        }
1316        // The catalogue names STATUS "GROUP_NAME", but a ref by that name
1317        // with STATUS's ID still reads STATUS.
1318        assert_eq!(fields.value(&named(STATUS, "GROUP_NAME")).unwrap(), None);
1319    }
1320
1321    /// A batch read returns one record per ref in request order, typed by
1322    /// the field, with a missing value absent. An unlisted ref, an internal
1323    /// field, and an unknown type are errors.
1324    // verifies: META-070
1325    #[xmtp_common::test(unwrap_try = true)]
1326    fn values_are_typed_and_in_request_order() {
1327        let dictionary = group();
1328        let fields = snapshot(&dictionary);
1329        let batch = fields
1330            .values(&[
1331                MetadataFieldRef::new(SCORES),
1332                MetadataFieldRef::GROUP_DESCRIPTION,
1333                MetadataFieldRef::new(TAGS),
1334                MetadataFieldRef::USER_DISPLAY_NAME,
1335            ])
1336            .unwrap();
1337        let values: Vec<_> = batch
1338            .into_iter()
1339            .map(|v| (v.field.component_id, v.value))
1340            .collect();
1341        assert_eq!(
1342            values,
1343            vec![
1344                (
1345                    SCORES,
1346                    Some(MetadataValue::Map(vec![MapEntry {
1347                        key: FieldKey::InboxId(inbox(0xA)),
1348                        value: FieldValue::Bytes(vec![7]),
1349                    }]))
1350                ),
1351                (ComponentId::GROUP_DESCRIPTION, None),
1352                (
1353                    TAGS,
1354                    Some(MetadataValue::Set(vec![FieldKey::InboxId(inbox(0xB))]))
1355                ),
1356                (
1357                    ComponentId::USER_DISPLAY_NAME,
1358                    Some(MetadataValue::Map(vec![
1359                        MapEntry {
1360                            key: FieldKey::InboxId(inbox(0xA)),
1361                            value: string("Alix"),
1362                        },
1363                        MapEntry {
1364                            key: FieldKey::InboxId(inbox(0xC)),
1365                            value: string("Carol"),
1366                        },
1367                    ]))
1368                ),
1369            ]
1370        );
1371        assert!(fields.values(&[]).unwrap().is_empty());
1372        for (field, id) in [
1373            (MetadataFieldRef::new(BROKEN), BROKEN),
1374            (MetadataFieldRef::GROUP_IMAGE, ComponentId::GROUP_IMAGE),
1375            (
1376                MetadataFieldRef::new(ComponentId::MIN_SUPPORTED_PROTOCOL_VERSION),
1377                ComponentId::MIN_SUPPORTED_PROTOCOL_VERSION,
1378            ),
1379        ] {
1380            assert!(matches!(
1381                fields.values(&[MetadataFieldRef::GROUP_NAME, field]),
1382                Err(FieldError::UnknownField(unknown)) if unknown == id
1383            ));
1384        }
1385        assert!(matches!(
1386            fields.value(&MetadataFieldRef::new(FUTURE)),
1387            Err(FieldError::UnsupportedType {
1388                component_id: FUTURE,
1389                tag: 99
1390            })
1391        ));
1392    }
1393
1394    /// A map read takes a key of the field's key type and returns the
1395    /// value under it, or none.
1396    // verifies: META-070
1397    #[xmtp_common::test(unwrap_try = true)]
1398    fn map_value_reads_one_key() {
1399        let dictionary = group();
1400        let fields = snapshot(&dictionary);
1401        let names = MetadataFieldRef::USER_DISPLAY_NAME;
1402        assert_eq!(
1403            fields
1404                .map_value(&names, &FieldKey::InboxId(inbox(0xC)))
1405                .unwrap(),
1406            Some(string("Carol"))
1407        );
1408        assert_eq!(
1409            fields
1410                .map_value(&names, &FieldKey::InboxId(inbox(0xB)))
1411                .unwrap(),
1412            None
1413        );
1414        assert_eq!(
1415            fields
1416                .map_value(&MetadataFieldRef::new(LINKS), &FieldKey::Bytes(vec![1]))
1417                .unwrap(),
1418            None
1419        );
1420        for (field, key) in [
1421            (names.clone(), FieldKey::Bytes(vec![0xA; 32])),
1422            (MetadataFieldRef::new(LINKS), FieldKey::InboxId(inbox(0xA))),
1423            (MetadataFieldRef::GROUP_NAME, FieldKey::Bytes(vec![])),
1424            (MetadataFieldRef::new(TAGS), FieldKey::InboxId(inbox(0xB))),
1425        ] {
1426            assert!(matches!(
1427                fields.map_value(&field, &key),
1428                Err(FieldError::TypeMismatch(id)) if id == field.component_id
1429            ));
1430        }
1431        assert!(matches!(
1432            fields.map_value(&MetadataFieldRef::new(FUTURE), &FieldKey::Bytes(vec![])),
1433            Err(FieldError::UnsupportedType { .. })
1434        ));
1435    }
1436
1437    /// User data defaults to every user field and every current member,
1438    /// each with a list (empty when it holds nothing). Explicit inboxes may
1439    /// name a former member; empty filters select nothing. Explicit fields
1440    /// must be user fields in the group, each named once.
1441    // verifies: META-072
1442    #[xmtp_common::test(unwrap_try = true)]
1443    fn user_data_selects_fields_and_inboxes() {
1444        let dictionary = group();
1445        let fields = snapshot(&dictionary);
1446        let value = |field: &MetadataFieldRef, value| UserFieldValue {
1447            field: field.clone(),
1448            value,
1449        };
1450        let scores = MetadataFieldRef::new(SCORES);
1451        let names = MetadataFieldRef::USER_DISPLAY_NAME;
1452        assert_eq!(
1453            fields.user_data(None, None).unwrap(),
1454            BTreeMap::from([
1455                (
1456                    inbox(0xA),
1457                    vec![
1458                        value(&names, string("Alix")),
1459                        value(&scores, FieldValue::Bytes(vec![7])),
1460                    ]
1461                ),
1462                (inbox(0xB), vec![]),
1463            ])
1464        );
1465        assert_eq!(
1466            fields
1467                .user_data(
1468                    Some(std::slice::from_ref(&scores)),
1469                    Some(&[inbox(0xC), inbox(0xA)])
1470                )
1471                .unwrap(),
1472            BTreeMap::from([
1473                (inbox(0xA), vec![value(&scores, FieldValue::Bytes(vec![7]))]),
1474                (inbox(0xC), vec![]),
1475            ])
1476        );
1477        assert_eq!(
1478            fields.user_data(None, Some(&[inbox(0xC)])).unwrap(),
1479            BTreeMap::from([(inbox(0xC), vec![value(&names, string("Carol"))])])
1480        );
1481        assert_eq!(
1482            fields.user_data(Some(&[]), None).unwrap(),
1483            BTreeMap::from([(inbox(0xA), vec![]), (inbox(0xB), vec![])])
1484        );
1485        assert!(fields.user_data(None, Some(&[])).unwrap().is_empty());
1486        assert!(matches!(
1487            fields.user_data(Some(&[MetadataFieldRef::GROUP_NAME]), None),
1488            Err(FieldError::NotUserField(ComponentId::GROUP_NAME))
1489        ));
1490        assert!(matches!(
1491            fields.user_data(Some(&[MetadataFieldRef::new(LINKS)]), None),
1492            Err(FieldError::NotUserField(LINKS))
1493        ));
1494        assert!(matches!(
1495            fields.user_data(Some(&[MetadataFieldRef::new(BROKEN)]), None),
1496            Err(FieldError::UnknownField(BROKEN))
1497        ));
1498        assert!(matches!(
1499            fields.user_data(Some(&[scores.clone(), named(SCORES, "scores")]), None),
1500            Err(FieldError::DuplicateField(SCORES))
1501        ));
1502    }
1503
1504    /// A user field whose stored value does not decode under its committed
1505    /// type, as after a registry update re-types it, yields no user data
1506    /// instead of failing the read of every other field. A single read of
1507    /// it still reports the malformed value.
1508    // verifies: META-072
1509    #[xmtp_common::test(unwrap_try = true)]
1510    fn user_data_skips_undecodable_fields() {
1511        let dictionary = dictionary(
1512            &[
1513                (
1514                    ComponentId::GROUP_MEMBERSHIP,
1515                    entry(tag(ComponentType::TlsMapInboxIdBytes)),
1516                ),
1517                (
1518                    ComponentId::USER_DISPLAY_NAME,
1519                    entry(tag(ComponentType::TlsMapInboxIdString)),
1520                ),
1521                (STATUS, entry(tag(ComponentType::TlsMapInboxIdString))),
1522            ],
1523            &[
1524                (
1525                    ComponentId::GROUP_MEMBERSHIP,
1526                    inbox_map(&[(inbox(0xA), &[1])]),
1527                ),
1528                (
1529                    ComponentId::USER_DISPLAY_NAME,
1530                    inbox_map(&[(inbox(0xA), b"Alix")]),
1531                ),
1532                (STATUS, inbox_map(&[(inbox(0xA), &[0xFF])])),
1533            ],
1534        );
1535        let fields = snapshot(&dictionary);
1536        let status = named(STATUS, "GROUP_NAME");
1537        let names = UserFieldValue {
1538            field: MetadataFieldRef::USER_DISPLAY_NAME,
1539            value: string("Alix"),
1540        };
1541        assert_eq!(
1542            fields.user_data(None, None)?,
1543            BTreeMap::from([(inbox(0xA), vec![names])])
1544        );
1545        assert_eq!(
1546            fields.user_data(Some(std::slice::from_ref(&status)), None)?,
1547            BTreeMap::from([(inbox(0xA), vec![])])
1548        );
1549        assert!(fields.value(&status).is_err());
1550    }
1551
1552    /// In a DM the default inboxes are the `DM_MEMBERS` pair, whatever the
1553    /// membership map holds.
1554    // verifies: META-072
1555    #[xmtp_common::test(unwrap_try = true)]
1556    fn user_data_defaults_to_the_dm_pair() {
1557        let dictionary = dictionary(
1558            &[(
1559                ComponentId::USER_DISPLAY_NAME,
1560                entry(tag(ComponentType::TlsMapInboxIdString)),
1561            )],
1562            &[
1563                (
1564                    ComponentId::DM_MEMBERS,
1565                    inbox_set(&[inbox(0xA), inbox(0xD)]),
1566                ),
1567                (
1568                    ComponentId::GROUP_MEMBERSHIP,
1569                    inbox_map(&[(inbox(0xA), b"")]),
1570                ),
1571            ],
1572        );
1573        let data = snapshot(&dictionary).user_data(None, None).unwrap();
1574        assert_eq!(
1575            data.into_keys().collect::<Vec<_>>(),
1576            vec![inbox(0xA), inbox(0xD)]
1577        );
1578    }
1579
1580    fn update(write: FieldWrite) -> Vec<u8> {
1581        match write.operation {
1582            WriteOperation::Update(payload) => payload,
1583            other => panic!("expected an update, got {other:?}"),
1584        }
1585    }
1586
1587    /// A write is encoded under the committed type: the protocol's for a
1588    /// well-known field and the registry's for an application field, never
1589    /// the catalogue's. A value, key, or mutation of the wrong shape, an
1590    /// unknown type, and an unlisted field are refused.
1591    // verifies: META-071
1592    #[xmtp_common::test(unwrap_try = true)]
1593    fn field_writes_follow_the_committed_type() {
1594        use ComponentMutation as M;
1595        let dictionary = group();
1596        let fields = snapshot(&dictionary);
1597        let status = MetadataFieldRef::new(STATUS);
1598        let write = fields
1599            .field_write(&status, &M::Replace(string("away")))
1600            .unwrap();
1601        assert_eq!(write.component_type, ComponentType::String);
1602        assert_eq!(update(write), b"away");
1603
1604        let insert = M::MapDelta(vec![MapMutation::Insert(
1605            FieldKey::InboxId(inbox(0xB)),
1606            string("Bo"),
1607        )]);
1608        let write = fields
1609            .field_write(&MetadataFieldRef::USER_DISPLAY_NAME, &insert)
1610            .unwrap();
1611        assert_eq!(write.component_type, ComponentType::TlsMapInboxIdString);
1612        assert_eq!(
1613            update(write),
1614            TlsMapDelta::<InboxId, VLBytes>::new()
1615                .insert(inbox(0xB), b"Bo".as_slice().into())
1616                .tls_serialize_detached()
1617                .unwrap()
1618        );
1619
1620        let set = M::SetDelta(vec![
1621            SetMutation::Insert(FieldKey::InboxId(inbox(0xA))),
1622            SetMutation::DeleteByHash([1; 32]),
1623        ]);
1624        let write = fields
1625            .field_write(&MetadataFieldRef::new(TAGS), &set)
1626            .unwrap();
1627        assert_eq!(
1628            update(write),
1629            TlsSetDelta::<InboxId>::new()
1630                .insert(inbox(0xA))
1631                .remove_by_hash(TlsKeyHash::from_bytes([1; 32]))
1632                .tls_serialize_detached()
1633                .unwrap()
1634        );
1635        assert_eq!(
1636            fields.field_write(&status, &M::Remove).unwrap().operation,
1637            WriteOperation::Remove
1638        );
1639
1640        let bytes_name = M::MapDelta(vec![MapMutation::Update(
1641            FieldKey::InboxId(inbox(0xA)),
1642            FieldValue::Bytes(b"Alix".to_vec()),
1643        )]);
1644        for (field, mutation) in [
1645            (
1646                status.clone(),
1647                M::Replace(FieldValue::Bytes(b"away".to_vec())),
1648            ),
1649            (status.clone(), M::MapDelta(vec![])),
1650            (MetadataFieldRef::USER_DISPLAY_NAME, bytes_name),
1651            (
1652                MetadataFieldRef::new(SCORES),
1653                M::MapDelta(vec![MapMutation::Delete(FieldKey::Bytes(vec![0xA; 32]))]),
1654            ),
1655            (
1656                MetadataFieldRef::new(TAGS),
1657                M::SetDelta(vec![SetMutation::Delete(FieldKey::Bytes(vec![]))]),
1658            ),
1659            (MetadataFieldRef::new(LINKS), M::SetDelta(vec![])),
1660        ] {
1661            assert!(matches!(
1662                fields.field_write(&field, &mutation),
1663                Err(FieldError::TypeMismatch(id)) if id == field.component_id
1664            ));
1665        }
1666        for mutation in [M::Remove, M::Replace(FieldValue::Bytes(vec![]))] {
1667            assert!(matches!(
1668                fields.field_write(&MetadataFieldRef::new(FUTURE), &mutation),
1669                Err(FieldError::UnsupportedType {
1670                    component_id: FUTURE,
1671                    tag: 99
1672                })
1673            ));
1674        }
1675        assert!(matches!(
1676            fields.field_write(&MetadataFieldRef::GROUP_IMAGE, &M::Remove),
1677            Err(FieldError::UnknownField(ComponentId::GROUP_IMAGE))
1678        ));
1679    }
1680
1681    /// Own user data updates name each user field once, with a value of its
1682    /// map value type.
1683    // verifies: META-073
1684    #[xmtp_common::test(unwrap_try = true)]
1685    fn user_data_writes_refuse_bad_updates() {
1686        let dictionary = group();
1687        let fields = snapshot(&dictionary);
1688        let set = |field: MetadataFieldRef, value| UserFieldUpdate { field, value };
1689        assert_eq!(
1690            fields
1691                .user_data_writes(&[
1692                    set(MetadataFieldRef::USER_DISPLAY_NAME, Some(string("Bo"))),
1693                    set(MetadataFieldRef::new(SCORES), None),
1694                ])
1695                .unwrap(),
1696            vec![
1697                FieldWrite {
1698                    component_id: ComponentId::USER_DISPLAY_NAME,
1699                    component_type: ComponentType::TlsMapInboxIdString,
1700                    operation: WriteOperation::SetOwn(b"Bo".to_vec()),
1701                },
1702                FieldWrite {
1703                    component_id: SCORES,
1704                    component_type: ComponentType::TlsMapInboxIdBytes,
1705                    operation: WriteOperation::ClearOwn,
1706                },
1707            ]
1708        );
1709        assert!(fields.user_data_writes(&[]).unwrap().is_empty());
1710        assert!(matches!(
1711            fields.user_data_writes(&[
1712                set(MetadataFieldRef::USER_DISPLAY_NAME, Some(string("Bo"))),
1713                set(named(ComponentId::USER_DISPLAY_NAME, "nickname"), None),
1714            ]),
1715            Err(FieldError::DuplicateField(ComponentId::USER_DISPLAY_NAME))
1716        ));
1717        assert!(matches!(
1718            fields.user_data_writes(&[set(MetadataFieldRef::GROUP_NAME, Some(string("x")))]),
1719            Err(FieldError::NotUserField(ComponentId::GROUP_NAME))
1720        ));
1721        assert!(matches!(
1722            fields.user_data_writes(&[set(MetadataFieldRef::new(SCORES), Some(string("7")))]),
1723            Err(FieldError::TypeMismatch(SCORES))
1724        ));
1725        assert!(matches!(
1726            fields.user_data_writes(&[set(
1727                MetadataFieldRef::USER_DISPLAY_NAME,
1728                Some(FieldValue::Bytes(b"Bo".to_vec()))
1729            )]),
1730            Err(FieldError::TypeMismatch(ComponentId::USER_DISPLAY_NAME))
1731        ));
1732        assert!(matches!(
1733            fields.user_data_writes(&[set(MetadataFieldRef::new(BROKEN), None)]),
1734            Err(FieldError::UnknownField(BROKEN))
1735        ));
1736    }
1737
1738    fn own_write(id: ComponentId, ty: ComponentType, operation: WriteOperation) -> FieldWrite {
1739        FieldWrite {
1740            component_id: id,
1741            component_type: ty,
1742            operation,
1743        }
1744    }
1745
1746    fn map_delta(delta: TlsMapDelta<InboxId, VLBytes>) -> AppDataUpdateOperation {
1747        AppDataUpdateOperation::Update(delta.tls_serialize_detached().unwrap().into())
1748    }
1749
1750    /// An own-key write only ever names the writer's inbox: a set is an
1751    /// Insert when the key is absent and an Update when present, a clear
1752    /// is a Delete when present and nothing when absent.
1753    // verifies: META-073
1754    #[xmtp_common::test(unwrap_try = true)]
1755    fn own_writes_resolve_against_current_values() {
1756        let dictionary = group();
1757        let fields = snapshot(&dictionary);
1758        let names = |operation| {
1759            own_write(
1760                ComponentId::USER_DISPLAY_NAME,
1761                ComponentType::TlsMapInboxIdString,
1762                operation,
1763            )
1764        };
1765        let scores = |operation| own_write(SCORES, ComponentType::TlsMapInboxIdBytes, operation);
1766        let resolve = |own, writes: &[FieldWrite]| {
1767            fields
1768                .resolve_writes(Some(&dictionary), own, writes)
1769                .unwrap()
1770        };
1771        let id = ComponentId::USER_DISPLAY_NAME;
1772        assert_eq!(
1773            resolve(inbox(0xA), &[names(WriteOperation::SetOwn(b"Al".to_vec()))]),
1774            vec![(
1775                id,
1776                map_delta(TlsMapDelta::new().update(inbox(0xA), b"Al".as_slice().into()))
1777            )]
1778        );
1779        assert_eq!(
1780            resolve(inbox(0xB), &[names(WriteOperation::SetOwn(b"Bo".to_vec()))]),
1781            vec![(
1782                id,
1783                map_delta(TlsMapDelta::new().insert(inbox(0xB), b"Bo".as_slice().into()))
1784            )]
1785        );
1786        assert_eq!(
1787            resolve(
1788                inbox(0xA),
1789                &[
1790                    names(WriteOperation::ClearOwn),
1791                    scores(WriteOperation::ClearOwn)
1792                ]
1793            ),
1794            vec![
1795                (id, map_delta(TlsMapDelta::new().delete(inbox(0xA)))),
1796                (SCORES, map_delta(TlsMapDelta::new().delete(inbox(0xA)))),
1797            ]
1798        );
1799        assert!(
1800            resolve(
1801                inbox(0xB),
1802                &[
1803                    names(WriteOperation::ClearOwn),
1804                    scores(WriteOperation::ClearOwn)
1805                ]
1806            )
1807            .is_empty()
1808        );
1809        assert!(resolve(inbox(0xB), &[]).is_empty());
1810        // Against an empty dictionary every set inserts.
1811        assert_eq!(
1812            fields
1813                .resolve_writes(
1814                    None,
1815                    inbox(0xA),
1816                    &[names(WriteOperation::SetOwn(b"A".to_vec()))]
1817                )
1818                .unwrap(),
1819            vec![(
1820                id,
1821                map_delta(TlsMapDelta::new().insert(inbox(0xA), b"A".as_slice().into()))
1822            )]
1823        );
1824        assert!(matches!(
1825            fields.resolve_writes(
1826                Some(&dictionary),
1827                inbox(0xA),
1828                &[
1829                    names(WriteOperation::ClearOwn),
1830                    names(WriteOperation::ClearOwn)
1831                ]
1832            ),
1833            Err(FieldError::DuplicateField(ComponentId::USER_DISPLAY_NAME))
1834        ));
1835    }
1836
1837    /// A write is checked against its current value before any commit: a
1838    /// removal of an absent component is dropped, and a value the group
1839    /// would reject, or a delta that no longer applies, is refused.
1840    // verifies: META-071
1841    #[xmtp_common::test(unwrap_try = true)]
1842    fn writes_are_checked_against_current_values() {
1843        let dictionary = group();
1844        let fields = snapshot(&dictionary);
1845        let status = |operation| own_write(STATUS, ComponentType::String, operation);
1846        let resolve =
1847            |writes: &[FieldWrite]| fields.resolve_writes(Some(&dictionary), inbox(0xA), writes);
1848        assert!(
1849            resolve(&[status(WriteOperation::Remove)])
1850                .unwrap()
1851                .is_empty()
1852        );
1853        assert_eq!(
1854            resolve(&[own_write(
1855                SCORES,
1856                ComponentType::TlsMapInboxIdBytes,
1857                WriteOperation::Remove
1858            )])
1859            .unwrap(),
1860            vec![(SCORES, AppDataUpdateOperation::Remove)]
1861        );
1862        assert!(matches!(
1863            resolve(&[status(WriteOperation::Update(vec![b'x'; 8193]))]),
1864            Err(FieldError::Component(_))
1865        ));
1866        assert!(matches!(
1867            resolve(&[status(WriteOperation::Update(vec![0xC3, 0x28]))]),
1868            Err(FieldError::Component(_))
1869        ));
1870        let insert_present = TlsMapDelta::<InboxId, VLBytes>::new()
1871            .insert(inbox(0xA), b"again".as_slice().into())
1872            .tls_serialize_detached()
1873            .unwrap();
1874        assert!(matches!(
1875            resolve(&[own_write(
1876                ComponentId::USER_DISPLAY_NAME,
1877                ComponentType::TlsMapInboxIdString,
1878                WriteOperation::Update(insert_present)
1879            )]),
1880            Err(FieldError::Component(_))
1881        ));
1882    }
1883
1884    /// A write whose result equals the current value is dropped, so an
1885    /// unchanged field never costs a commit or an epoch. The comparison is
1886    /// on the applied bytes: an empty delta creates an absent map. The
1887    /// operations that would carry the writes out keep them.
1888    // verifies: META-071
1889    #[xmtp_common::test(unwrap_try = true)]
1890    fn unchanged_writes_are_dropped() {
1891        let dictionary = group();
1892        let fields = snapshot(&dictionary);
1893        let resolve = |writes: &[FieldWrite]| {
1894            fields
1895                .resolve_writes(Some(&dictionary), inbox(0xA), writes)
1896                .unwrap()
1897        };
1898        let name = |value: &[u8]| {
1899            own_write(
1900                ComponentId::GROUP_NAME,
1901                ComponentType::String,
1902                WriteOperation::Update(value.to_vec()),
1903            )
1904        };
1905        let empty_links = TlsMapDelta::<VLBytes, VLBytes>::new().tls_serialize_detached()?;
1906        let empty_scores = TlsMapDelta::<InboxId, VLBytes>::new().tls_serialize_detached()?;
1907        assert!(
1908            resolve(&[
1909                name(b"Team"),
1910                own_write(
1911                    ComponentId::USER_DISPLAY_NAME,
1912                    ComponentType::TlsMapInboxIdString,
1913                    WriteOperation::SetOwn(b"Alix".to_vec()),
1914                ),
1915                own_write(
1916                    SCORES,
1917                    ComponentType::TlsMapInboxIdBytes,
1918                    WriteOperation::Update(empty_scores),
1919                ),
1920            ])
1921            .is_empty()
1922        );
1923        assert_eq!(
1924            resolve(&[
1925                name(b"Teams"),
1926                own_write(
1927                    LINKS,
1928                    ComponentType::TlsMapBytesBytes,
1929                    WriteOperation::Update(empty_links.clone()),
1930                ),
1931            ]),
1932            vec![
1933                (
1934                    ComponentId::GROUP_NAME,
1935                    AppDataUpdateOperation::Update(b"Teams".to_vec().into())
1936                ),
1937                (LINKS, AppDataUpdateOperation::Update(empty_links.into())),
1938            ]
1939        );
1940        assert_eq!(
1941            fields.write_operations(
1942                Some(&dictionary),
1943                inbox(0xA),
1944                &[
1945                    name(b"Team"),
1946                    own_write(
1947                        ComponentId::USER_DISPLAY_NAME,
1948                        ComponentType::TlsMapInboxIdString,
1949                        WriteOperation::SetOwn(b"Alix".to_vec()),
1950                    ),
1951                ]
1952            )?,
1953            vec![
1954                (
1955                    ComponentId::GROUP_NAME,
1956                    AppDataUpdateOperation::Update(b"Team".to_vec().into())
1957                ),
1958                (
1959                    ComponentId::USER_DISPLAY_NAME,
1960                    map_delta(TlsMapDelta::new().update(inbox(0xA), b"Alix".as_slice().into()))
1961                ),
1962            ]
1963        );
1964    }
1965
1966    /// An own-key write names an entry of an inbox-keyed map, so one queued
1967    /// for any other shape is refused rather than applied as that shape's
1968    /// payload, even when its type tag matches.
1969    // verifies: META-073
1970    #[xmtp_common::test(unwrap_try = true)]
1971    fn own_writes_need_a_user_field() {
1972        let dictionary = group();
1973        let fields = snapshot(&dictionary);
1974        for (id, ty) in [
1975            (STATUS, ComponentType::String),
1976            (LINKS, ComponentType::TlsMapBytesBytes),
1977        ] {
1978            for operation in [
1979                WriteOperation::SetOwn(b"x".to_vec()),
1980                WriteOperation::ClearOwn,
1981            ] {
1982                assert!(matches!(
1983                    fields.resolve_writes(
1984                        Some(&dictionary),
1985                        inbox(0xA),
1986                        &[own_write(id, ty, operation)]
1987                    ),
1988                    Err(FieldError::TypeMismatch(got)) if got == id
1989                ));
1990            }
1991        }
1992    }
1993
1994    /// A write encoded under one snapshot is refused if its field changed
1995    /// type or left the registry before it is resolved.
1996    // verifies: META-071
1997    #[xmtp_common::test(unwrap_try = true)]
1998    fn stale_writes_are_refused() {
1999        let encoded = snapshot(&group())
2000            .field_write(
2001                &MetadataFieldRef::new(STATUS),
2002                &ComponentMutation::Replace(string("x")),
2003            )
2004            .unwrap();
2005        let retyped = dictionary(&[(STATUS, entry(tag(ComponentType::Bytes)))], &[]);
2006        assert!(matches!(
2007            snapshot(&retyped).resolve_writes(
2008                Some(&retyped),
2009                inbox(0xA),
2010                std::slice::from_ref(&encoded)
2011            ),
2012            Err(FieldError::TypeChanged {
2013                component_id: STATUS,
2014                expected: ComponentType::String,
2015                actual: MetadataComponentType::Bytes,
2016            })
2017        ));
2018        let removed = dictionary(&[], &[]);
2019        assert!(matches!(
2020            snapshot(&removed).resolve_writes(Some(&removed), inbox(0xA), &[encoded]),
2021            Err(FieldError::UnknownField(STATUS))
2022        ));
2023    }
2024
2025    /// An entry this build cannot interpret neither blocks the snapshot nor
2026    /// is touched: other fields still read and write, the entry keeps its
2027    /// bytes, and a write names only its own component.
2028    // verifies: META-016, META-017
2029    #[xmtp_common::test(unwrap_try = true)]
2030    fn uninterpreted_entries_are_left_alone() {
2031        let dictionary = group();
2032        let before = dictionary.clone();
2033        let fields = snapshot(&dictionary);
2034        let writes = fields
2035            .user_data_writes(&[UserFieldUpdate {
2036                field: MetadataFieldRef::USER_DISPLAY_NAME,
2037                value: Some(string("Bo")),
2038            }])
2039            .unwrap();
2040        let updates = fields
2041            .resolve_writes(Some(&dictionary), inbox(0xB), &writes)
2042            .unwrap();
2043        assert_eq!(
2044            updates.iter().map(|(id, _)| *id).collect::<Vec<_>>(),
2045            vec![ComponentId::USER_DISPLAY_NAME]
2046        );
2047        let _ = fields.values(&[MetadataFieldRef::new(FUTURE)]);
2048        assert_eq!(dictionary, before);
2049        assert_eq!(dictionary.get(&BROKEN.as_u16()), Some([1, 2, 3].as_slice()));
2050    }
2051
2052    /// Every wire type tag maps to its shape and back; an unspecified or
2053    /// unknown tag is `Unknown` and has no wire tag.
2054    #[xmtp_common::test(unwrap_try = true)]
2055    fn type_tags_round_trip() {
2056        for tag in 1..=7 {
2057            let shape = MetadataComponentType::from_tag(tag);
2058            assert_eq!(shape.tag().map(|t| t as i32), Some(tag));
2059        }
2060        for tag in [0, 8, -1] {
2061            assert_eq!(
2062                MetadataComponentType::from_tag(tag),
2063                MetadataComponentType::Unknown { tag }
2064            );
2065            assert_eq!(MetadataComponentType::from_tag(tag).tag(), None);
2066        }
2067    }
2068}