autonomi/client/high_level/vault/
user_data.rs

1// Copyright 2024 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use std::collections::HashMap;
10
11use super::{VaultContentType, VaultError, VaultSecretKey, vault_content_type_from_app_name};
12use crate::chunk::DataMapChunk;
13use crate::client::Client;
14use crate::client::GetError;
15use crate::client::high_level::files::archive_private::PrivateArchiveDataMap;
16use crate::client::high_level::files::archive_public::ArchiveAddress;
17use crate::client::payment::PaymentOption;
18use crate::data::DataAddress;
19use crate::register::RegisterAddress;
20use ant_evm::AttoTokens;
21use ant_protocol::Bytes;
22use serde::{Deserialize, Serialize};
23use std::sync::LazyLock;
24
25/// Vault content type for UserDataVault
26pub static USER_DATA_VAULT_CONTENT_IDENTIFIER: LazyLock<VaultContentType> =
27    LazyLock::new(|| vault_content_type_from_app_name("UserData"));
28
29pub type RegisterSecretKeyHex = String;
30pub type ScratchpadSecretKeyHex = String;
31pub type PointerSecretKeyHex = String;
32
33/// UserData is stored in Vaults and contains most of a user's private data:
34/// It allows users to keep track of only the key to their User Data Vault
35/// while having the rest kept on the Network encrypted in a Vault for them
36/// Using User Data Vault is optional, one can decide to keep all their data locally instead.
37#[derive(Debug, Clone, Serialize, Default, PartialEq, Eq, Deserialize)]
38pub struct UserData {
39    /// Owned file archive addresses, along with their names (can be empty)
40    pub file_archives: HashMap<ArchiveAddress, String>,
41    /// Owned private file archives, along with their names (can be empty)
42    pub private_file_archives: HashMap<PrivateArchiveDataMap, String>,
43    /// Owned register addresses, along with their names (can be empty)
44    pub register_addresses: HashMap<RegisterAddress, String>,
45    /// Register key
46    #[serde(default)]
47    // This makes the field optional to support old versions without that field
48    pub register_key: Option<RegisterSecretKeyHex>,
49    /// Scratchpads key
50    #[serde(default)]
51    // This makes the field optional to support old versions without that field
52    pub scratchpad_key: Option<ScratchpadSecretKeyHex>,
53    /// Pointer key
54    #[serde(default)]
55    // This makes the field optional to support old versions without that field
56    pub pointer_key: Option<PointerSecretKeyHex>,
57    /// Individual public files (non-archive), along with their names
58    #[serde(default)]
59    // This makes the field optional to support old versions without that field
60    pub public_files: HashMap<DataAddress, String>,
61    /// Individual private files (non-archive), along with their names
62    #[serde(default)]
63    // This makes the field optional to support old versions without that field
64    pub private_files: HashMap<DataMapChunk, String>,
65}
66
67/// Errors that can occur during the get operation.
68#[derive(Debug, thiserror::Error)]
69pub enum UserDataVaultError {
70    #[error("Vault error: {0}")]
71    Vault(#[from] VaultError),
72    #[error("Unsupported vault content type: {0}")]
73    UnsupportedVaultContentType(VaultContentType),
74    #[error("Serialization error: {0}")]
75    Serialization(String),
76    #[error("Get error: {0}")]
77    GetError(#[from] GetError),
78}
79
80impl UserData {
81    /// Create a new empty UserData
82    pub fn new() -> Self {
83        Self::default()
84    }
85
86    /// Add a register. Returning `Option::Some` with the old name if the register was already in the set.
87    pub fn add_register(&mut self, register: RegisterAddress, name: String) -> Option<String> {
88        self.register_addresses.insert(register, name)
89    }
90
91    /// Add an archive. Returning `Option::Some` with the old name if the archive was already in the set.
92    pub fn add_file_archive(&mut self, archive: ArchiveAddress) -> Option<String> {
93        self.file_archives.insert(archive, "".into())
94    }
95
96    /// Add an archive. Returning `Option::Some` with the old name if the archive was already in the set.
97    pub fn add_file_archive_with_name(
98        &mut self,
99        archive: ArchiveAddress,
100        name: String,
101    ) -> Option<String> {
102        self.file_archives.insert(archive, name)
103    }
104
105    /// Add a private archive. Returning `Option::Some` with the old name if the archive was already in the set.
106    pub fn add_private_file_archive(&mut self, archive: PrivateArchiveDataMap) -> Option<String> {
107        self.private_file_archives.insert(archive, "".into())
108    }
109
110    /// Add a private archive with a name. Returning `Option::Some` with the old name if the archive was already in the set.
111    pub fn add_private_file_archive_with_name(
112        &mut self,
113        archive: PrivateArchiveDataMap,
114        name: String,
115    ) -> Option<String> {
116        self.private_file_archives.insert(archive, name)
117    }
118
119    /// Remove an archive. Returning `Option::Some` with the old name if the archive was already in the set.
120    pub fn remove_file_archive(&mut self, archive: ArchiveAddress) -> Option<String> {
121        self.file_archives.remove(&archive)
122    }
123
124    /// Remove a private archive. Returning `Option::Some` with the old name if the archive was already in the set.
125    pub fn remove_private_file_archive(
126        &mut self,
127        archive: PrivateArchiveDataMap,
128    ) -> Option<String> {
129        self.private_file_archives.remove(&archive)
130    }
131
132    /// To bytes
133    pub fn to_bytes(&self) -> Result<Bytes, rmp_serde::encode::Error> {
134        let bytes = rmp_serde::to_vec(&self)?;
135        Ok(Bytes::from(bytes))
136    }
137
138    /// From bytes
139    pub fn from_bytes(bytes: Bytes) -> Result<Self, rmp_serde::decode::Error> {
140        let vault_content = rmp_serde::from_slice(&bytes)?;
141        Ok(vault_content)
142    }
143
144    /// Display content
145    pub fn display_stats(&self) {
146        let file_archives_len = self.file_archives.len();
147        let private_file_archives_len = self.private_file_archives.len();
148        let public_files_len = self.public_files.len();
149        let private_files_len = self.private_files.len();
150        let registers_len = self.register_addresses.len();
151        let register_key = match self.register_key.is_some() {
152            true => "1",
153            false => "0",
154        };
155        let scratchpad_key = match self.scratchpad_key.is_some() {
156            true => "1",
157            false => "0",
158        };
159        let pointer_key = match self.pointer_key.is_some() {
160            true => "1",
161            false => "0",
162        };
163
164        println!("{file_archives_len} public file archive(s)");
165        println!("{private_file_archives_len} private file archive(s)");
166        println!("{public_files_len} public file(s)");
167        println!("{private_files_len} private file(s)");
168        println!("{registers_len} register(s)");
169        println!("{register_key} register key");
170        println!("{scratchpad_key} scratchpad key");
171        println!("{pointer_key} pointer key");
172    }
173}
174
175impl Client {
176    /// Get the user data from the vault
177    pub async fn vault_get_user_data(
178        &self,
179        secret_key: &VaultSecretKey,
180    ) -> Result<UserData, UserDataVaultError> {
181        let (bytes, content_type) = self.vault_get(secret_key).await?;
182
183        if content_type != *USER_DATA_VAULT_CONTENT_IDENTIFIER {
184            return Err(UserDataVaultError::UnsupportedVaultContentType(
185                content_type,
186            ));
187        }
188
189        let vault = UserData::from_bytes(bytes).map_err(|e| {
190            UserDataVaultError::Serialization(format!("Failed to deserialize vault content: {e}"))
191        })?;
192
193        Ok(vault)
194    }
195
196    /// Put the user data to the vault
197    ///
198    /// Returns the total cost of the put operation
199    pub async fn vault_put_user_data(
200        &self,
201        secret_key: &VaultSecretKey,
202        payment_option: PaymentOption,
203        user_data: UserData,
204    ) -> Result<AttoTokens, UserDataVaultError> {
205        let bytes = user_data.to_bytes().map_err(|e| {
206            UserDataVaultError::Serialization(format!("Failed to serialize user data: {e}"))
207        })?;
208        let total_cost = self
209            .vault_put(
210                bytes,
211                payment_option,
212                secret_key,
213                *USER_DATA_VAULT_CONTENT_IDENTIFIER,
214            )
215            .await?;
216        Ok(total_cost)
217    }
218
219    /// @deprecated Use `vault_get_user_data` instead. This function will be removed in a future version.
220    #[deprecated(since = "0.2.0", note = "Use `vault_get_user_data` instead")]
221    pub async fn get_user_data_from_vault(
222        &self,
223        secret_key: &VaultSecretKey,
224    ) -> Result<UserData, UserDataVaultError> {
225        self.vault_get_user_data(secret_key).await
226    }
227
228    /// @deprecated Use `vault_put_user_data` instead. This function will be removed in a future version.
229    #[deprecated(since = "0.2.0", note = "Use `vault_put_user_data` instead")]
230    pub async fn put_user_data_to_vault(
231        &self,
232        secret_key: &VaultSecretKey,
233        payment_option: PaymentOption,
234        user_data: UserData,
235    ) -> Result<AttoTokens, UserDataVaultError> {
236        self.vault_put_user_data(secret_key, payment_option, user_data)
237            .await
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use crate::XorName;
244    use bls::SecretKey;
245
246    use super::*;
247
248    // simulate how the previous version of UserData looked like
249    #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
250    struct UserDataV1 {
251        pub file_archives: HashMap<ArchiveAddress, String>,
252        pub private_file_archives: HashMap<PrivateArchiveDataMap, String>,
253        pub register_addresses: HashMap<RegisterAddress, String>,
254    }
255
256    #[test]
257    fn test_user_data_v1_deserialization() {
258        // Create a V1 instance with some test data
259        let v1_data = UserDataV1 {
260            file_archives: HashMap::from([(ArchiveAddress::new(XorName::random(&mut rand::thread_rng())), "test_archive".to_string())]),
261            private_file_archives: HashMap::from([(
262                PrivateArchiveDataMap::from_hex("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(),
263                "test_private".to_string(),
264            )]),
265            register_addresses: HashMap::from([(RegisterAddress::new(SecretKey::random().public_key()), "test_register".to_string())]),
266        };
267
268        // Serialize V1 data
269        let serialized = rmp_serde::to_vec(&v1_data).unwrap();
270
271        // Deserialize into current UserData
272        let deserialized: UserData = rmp_serde::from_slice(&serialized).unwrap();
273
274        // Verify the conversion was successful
275        assert_eq!(deserialized.file_archives, v1_data.file_archives);
276        assert_eq!(
277            deserialized.private_file_archives,
278            v1_data.private_file_archives
279        );
280        assert_eq!(deserialized.register_addresses, v1_data.register_addresses);
281        assert_eq!(deserialized.register_key, None);
282        assert_eq!(deserialized.scratchpad_key, None);
283        assert_eq!(deserialized.pointer_key, None);
284        assert_eq!(deserialized.public_files, HashMap::new());
285        assert_eq!(deserialized.private_files, HashMap::new());
286
287        // Test current version serialization/deserialization
288        let current_data = UserData {
289            file_archives: v1_data.file_archives.clone(),
290            private_file_archives: v1_data.private_file_archives.clone(),
291            register_addresses: v1_data.register_addresses.clone(),
292            register_key: Some("test_key".to_string()),
293            scratchpad_key: Some("test_scratchpad_key".to_string()),
294            pointer_key: Some("test_pointer_key".to_string()),
295            public_files: HashMap::new(),
296            private_files: HashMap::new(),
297        };
298
299        let serialized = rmp_serde::to_vec(&current_data).unwrap();
300        let deserialized: UserData = rmp_serde::from_slice(&serialized).unwrap();
301
302        // Verify current version maintains all fields
303        assert_eq!(deserialized, current_data);
304    }
305}