openmls/group/mls_group/migration_import.rs
1use super::*;
2
3/// A group and all of the group-associated data it owns, deserialized from a
4/// previous OpenMLS version for migration. The mirror of the older version's
5/// `MlsGroup::export_for_migration`.
6#[cfg_attr(
7 all(feature = "test-utils", feature = "migration-import"),
8 derive(serde::Serialize)
9)]
10#[derive(serde::Deserialize)]
11pub struct GroupMigrationBundle {
12 group: MlsGroup,
13 epoch_encryption_key_pairs: Vec<EncryptionKeyPair>,
14 update_encryption_key_pairs: Vec<EncryptionKeyPair>,
15}
16
17impl MlsGroup {
18 /// Store a group as part of a storage migration.
19 ///
20 /// This replaces the group's persisted state for its [`GroupId`]: the
21 /// single-value tables are overwritten, and the accumulate-style tables (the
22 /// own leaf nodes and the proposal queue) are cleared before being rewritten.
23 ///
24 /// **NOTE**: This helper only addresses entries under the current key encoding. When the key
25 /// encoding differs between versions (e.g. postcard → JSON), old-format entries
26 /// live under different keys and are not changed; remove those with the
27 /// older version's `MlsGroup::delete`.
28 #[openmls_traits::maybe_async]
29 pub(crate) async fn store_for_migration<Storage: crate::storage::StorageProvider>(
30 &self,
31 storage: &Storage,
32 ) -> Result<(), Storage::Error> {
33 self.public_group.store_for_migration(storage).await?;
34 storage
35 .write_group_epoch_secrets(self.group_id(), &self.group_epoch_secrets)
36 .await?;
37 storage
38 .write_own_leaf_index(self.group_id(), &self.own_leaf_index)
39 .await?;
40 storage
41 .write_message_secrets(self.group_id(), &self.message_secrets_store)
42 .await?;
43 storage
44 .write_resumption_psk_store(self.group_id(), &self.resumption_psk_store)
45 .await?;
46 storage
47 .write_mls_join_config(self.group_id(), &self.mls_group_config)
48 .await?;
49
50 // clear `own_leaf_nodes`, and rewrite one-by-one
51 storage.delete_own_leaf_nodes(self.group_id()).await?;
52 for leaf_node in self.own_leaf_nodes.iter() {
53 storage
54 .append_own_leaf_node(self.group_id(), leaf_node)
55 .await?;
56 }
57
58 storage
59 .write_group_state(self.group_id(), &self.group_state)
60 .await?;
61 #[cfg(feature = "extensions-draft")]
62 match &self.application_export_tree {
63 Some(application_export_tree) => {
64 storage
65 .write_application_export_tree(self.group_id(), application_export_tree)
66 .await?;
67 }
68 // No tree to write (migrating in from a version/config without
69 // `extensions-draft`, or a migration whose source has none). Delete
70 // any stale entry so the group's persisted state is fully replaced
71 // rather than left pointing at a previously stored tree.
72 None => {
73 storage
74 .delete_application_export_tree::<_, ApplicationExportTree>(self.group_id())
75 .await?;
76 }
77 }
78
79 Ok(())
80 }
81}
82
83impl GroupMigrationBundle {
84 /// Store the migrated group and all of its group-associated data in the
85 /// current storage format.
86 ///
87 /// **NOTE**: This replaces any existing state for the group's [`GroupId`], under the same [`GroupId`] encoding.
88 ///
89 /// Single-value tables are overwritten and the accumulate-style data
90 /// (own leaf nodes, proposal queue) are cleared before writing.
91 ///
92 /// Old-format entries under a different key encoding are not changed.
93 #[openmls_traits::maybe_async]
94 pub async fn store<Storage: crate::storage::StorageProvider>(
95 &self,
96 storage: &Storage,
97 ) -> Result<(), Storage::Error> {
98 // Group state (config, tree, context, proposals, secrets, ...).
99 self.group.store_for_migration(storage).await?;
100
101 // The group's own encryption key pairs for the current epoch.
102 storage
103 .write_encryption_epoch_key_pairs(
104 self.group.group_id(),
105 &self.group.epoch(),
106 self.group.own_leaf_index().u32(),
107 &self.epoch_encryption_key_pairs,
108 )
109 .await?;
110
111 // Encryption key pairs for pending (uncommitted) update leaf nodes.
112 for key_pair in &self.update_encryption_key_pairs {
113 key_pair.write(storage).await?;
114 }
115
116 Ok(())
117 }
118}