mls_rs_core/group/
group_state.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright by contributors to this project.
3// SPDX-License-Identifier: (Apache-2.0 OR MIT)
4
5use core::fmt::{self, Debug};
6
7use crate::error::IntoAnyError;
8#[cfg(mls_build_async)]
9use alloc::boxed::Box;
10use alloc::vec::Vec;
11
12/// Generic representation of a group's state.
13#[derive(Clone, PartialEq, Eq)]
14pub struct GroupState {
15    /// A unique group identifier.
16    pub id: Vec<u8>,
17    pub data: Vec<u8>,
18}
19
20impl Debug for GroupState {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        f.debug_struct("GroupState")
23            .field("id", &crate::debug::pretty_bytes(&self.id))
24            .field("data", &crate::debug::pretty_bytes(&self.data))
25            .finish()
26    }
27}
28
29/// Generic representation of a prior epoch.
30#[derive(Clone, PartialEq, Eq)]
31pub struct EpochRecord {
32    /// A unique epoch identifier within a particular group.
33    pub id: u64,
34    pub data: Vec<u8>,
35}
36
37impl Debug for EpochRecord {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.debug_struct("EpochRecord")
40            .field("id", &self.id)
41            .field("data", &crate::debug::pretty_bytes(&self.data))
42            .finish()
43    }
44}
45
46impl EpochRecord {
47    pub fn new(id: u64, data: Vec<u8>) -> Self {
48        Self { id, data }
49    }
50}
51
52/// Storage that can persist and reload a group state.
53///
54/// A group state is recorded as a combination of the current state
55/// (represented by the [`GroupState`] trait) and some number of prior
56/// group states (represented by the [`EpochRecord`] trait).
57/// This trait implements reading and writing group data as requested by the protocol
58/// implementation.
59///
60/// # Cleaning up records
61///
62/// Group state will not be purged when the local member is removed from the
63/// group. It is up to the implementer of this trait to provide a mechanism
64/// to delete records that can be used by an application.
65///
66
67#[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
68#[cfg_attr(mls_build_async, maybe_async::must_be_async)]
69pub trait GroupStateStorage: Send + Sync {
70    type Error: IntoAnyError;
71
72    /// Fetch a group state from storage.
73    async fn state(&self, group_id: &[u8]) -> Result<Option<Vec<u8>>, Self::Error>;
74
75    /// Lazy load cached epoch data from a particular group.
76    async fn epoch(&self, group_id: &[u8], epoch_id: u64) -> Result<Option<Vec<u8>>, Self::Error>;
77
78    /// Write pending state updates.
79    ///
80    /// The group id that this update belongs to can be retrieved with
81    /// [`GroupState::id`]. Prior epoch id values can be retrieved with
82    /// [`EpochRecord::id`].
83    ///
84    /// The protocol implementation handles managing the max size of a prior epoch
85    /// cache and the deleting of prior states based on group activity.
86    /// The maximum number of prior epochs that will be stored is controlled by the
87    /// `Preferences::max_epoch_retention` function in `mls_rs`.
88    /// value. Requested deletes are communicated by the `delete_epoch_under`
89    /// parameter being set to `Some`.
90    ///
91    /// # Warning
92    ///
93    /// It is important to consider error recovery when creating an implementation
94    /// of this trait. Calls to [`write`](GroupStateStorage::write) should
95    /// optimally be a single atomic transaction in order to avoid partial writes
96    /// that may corrupt the group state.
97    async fn write(
98        &mut self,
99        state: GroupState,
100        epoch_inserts: Vec<EpochRecord>,
101        epoch_updates: Vec<EpochRecord>,
102    ) -> Result<(), Self::Error>;
103
104    /// The [`EpochRecord::id`] value that is associated with a stored
105    /// prior epoch for a particular group.
106    async fn max_epoch_id(&self, group_id: &[u8]) -> Result<Option<u64>, Self::Error>;
107}