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 5 use core::fmt::{self, Debug}; 6 7 use crate::error::IntoAnyError; 8 #[cfg(mls_build_async)] 9 use alloc::boxed::Box; 10 use alloc::vec::Vec; 11 12 /// Generic representation of a group's state. 13 #[derive(Clone, PartialEq, Eq)] 14 pub struct GroupState { 15 /// A unique group identifier. 16 pub id: Vec<u8>, 17 pub data: Vec<u8>, 18 } 19 20 impl Debug for GroupState { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result21 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)] 31 pub struct EpochRecord { 32 /// A unique epoch identifier within a particular group. 33 pub id: u64, 34 pub data: Vec<u8>, 35 } 36 37 impl Debug for EpochRecord { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result38 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 46 impl EpochRecord { new(id: u64, data: Vec<u8>) -> Self47 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)] 69 pub trait GroupStateStorage: Send + Sync { 70 type Error: IntoAnyError; 71 72 /// Fetch a group state from storage. state(&self, group_id: &[u8]) -> Result<Option<Vec<u8>>, Self::Error>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. epoch(&self, group_id: &[u8], epoch_id: u64) -> Result<Option<Vec<u8>>, Self::Error>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. write( &mut self, state: GroupState, epoch_inserts: Vec<EpochRecord>, epoch_updates: Vec<EpochRecord>, ) -> Result<(), Self::Error>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. max_epoch_id(&self, group_id: &[u8]) -> Result<Option<u64>, Self::Error>106 async fn max_epoch_id(&self, group_id: &[u8]) -> Result<Option<u64>, Self::Error>; 107 } 108