calimero_context_primitives/client/
crypto.rs1use calimero_primitives::context::ContextId;
2use calimero_primitives::identity::{PrivateKey, PublicKey};
3use calimero_store::{key, types};
4use eyre::bail;
5
6use super::ContextClient;
7
8#[derive(Debug)]
13pub struct ContextIdentity {
14 pub public_key: PublicKey,
16 pub private_key: Option<PrivateKey>,
20 pub sender_key: Option<PrivateKey>,
23}
24
25impl ContextIdentity {
26 pub fn private_key(&self) -> eyre::Result<&PrivateKey> {
32 let Some(private_key) = &self.private_key else {
33 bail!(
34 "the identity '{}' is not managed by this node",
35 self.public_key
36 );
37 };
38
39 Ok(private_key)
40 }
41}
42
43impl ContextClient {
44 pub fn new_identity(&self) -> eyre::Result<PublicKey> {
63 let mut handle = self.datastore.handle();
64
65 let private_key = PrivateKey::random(&mut rand::thread_rng());
66 let public_key = private_key.public_key();
67
68 handle.put(
69 &key::ContextIdentity::new(ContextId::zero(), public_key),
70 &types::ContextIdentity {
71 private_key: Some(*private_key),
72 sender_key: None,
73 },
74 )?;
75
76 Ok(public_key)
77 }
78
79 pub fn get_identity(
94 &self,
95 context_id: &ContextId,
96 public_key: &PublicKey,
97 ) -> eyre::Result<Option<ContextIdentity>> {
98 let handle = self.datastore.handle();
99
100 let key = key::ContextIdentity::new(*context_id, *public_key);
101
102 let Some(identity) = handle.get(&key)? else {
103 return Ok(None);
104 };
105
106 let identity = ContextIdentity {
107 public_key: *public_key,
108 private_key: identity.private_key.map(PrivateKey::from),
109 sender_key: identity.sender_key.map(PrivateKey::from),
110 };
111
112 Ok(Some(identity))
113 }
114
115 pub fn update_identity(
129 &self,
130 context_id: &ContextId,
131 new_identity: &ContextIdentity,
132 ) -> eyre::Result<()> {
133 let mut handle = self.datastore.handle();
134
135 let key = key::ContextIdentity::new(*context_id, new_identity.public_key);
136
137 let Some(mut identity) = handle.get(&key)? else {
138 bail!(
139 "the identity '{}' is not managed on this node for context '{}'",
140 new_identity.public_key,
141 context_id
142 );
143 };
144
145 identity.sender_key = new_identity.sender_key.as_deref().copied();
146 identity.private_key = new_identity.private_key.as_deref().copied();
149
150 handle.put(&key, &identity)?;
151
152 Ok(())
153 }
154
155 pub fn delete_identity(
166 &self,
167 context_id: &ContextId,
168 public_key: &PublicKey,
169 ) -> eyre::Result<()> {
170 let mut handle = self.datastore.handle();
171
172 let key = key::ContextIdentity::new(*context_id, *public_key);
173
174 handle.delete(&key)?;
175
176 Ok(())
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183 use crate::client::ContextClient;
184 use calimero_blobstore::{config::BlobStoreConfig, BlobManager, FileSystem};
185 use calimero_context_config::client::{
186 config::{ClientConfig, ClientRelayerSigner, ClientSigner, LocalConfig},
187 Client as ExternalClient,
188 };
189 use calimero_network_primitives::client::NetworkClient;
190 use calimero_node_primitives::{client::NodeClient, messages::NodeMessage};
191 use calimero_primitives::common::DIGEST_SIZE;
192 use calimero_store::{db::InMemoryDB, key, types, Store};
193 use calimero_utils_actix::LazyRecipient;
194 use std::collections::BTreeMap;
195 use std::sync::Arc;
196 use tokio;
197 use tokio::sync::{broadcast, mpsc};
198
199 async fn setup_context_client() -> ContextClient {
201 let db = InMemoryDB::owned();
203 let store = Store::new(Arc::new(db));
204
205 let tmp_dir = tempfile::tempdir().unwrap();
207 let blob_store_config =
208 BlobStoreConfig::new(tmp_dir.path().to_path_buf().try_into().unwrap());
209 let file_system = FileSystem::new(&blob_store_config).await.unwrap();
210 let blob_manager = BlobManager::new(store.clone(), file_system);
211
212 let network_client = NetworkClient::new(LazyRecipient::new());
214 let (event_sender, _) = broadcast::channel(16);
215 let (ctx_sync_tx, _) = mpsc::channel(16);
216 let node_manager = LazyRecipient::<NodeMessage>::new();
217
218 let node_client = NodeClient::new(
220 store.clone(),
221 blob_manager,
222 network_client,
223 node_manager,
224 event_sender,
225 ctx_sync_tx,
226 String::new(), );
228
229 let client_config = ClientConfig {
231 params: BTreeMap::new(),
232 signer: ClientSigner {
233 relayer: ClientRelayerSigner {
234 url: "http://127.0.0.1:3030".parse().unwrap(),
235 },
236 local: LocalConfig {
237 protocols: BTreeMap::new(),
238 },
239 },
240 };
241
242 let external_client = ExternalClient::from_config(&client_config);
245
246 let context_manager = LazyRecipient::new();
248 ContextClient::new(store, node_client, external_client, context_manager)
249 }
250
251 #[tokio::test]
252 async fn test_new_and_get_identity() {
253 let client = setup_context_client().await;
254 let public_key = client.new_identity().expect("Should create identity");
255 let context_id = ContextId::zero();
256 let identity = client
257 .get_identity(&context_id, &public_key)
258 .unwrap()
259 .expect("Identity should be found in the datastore");
260
261 assert_eq!(identity.public_key, public_key);
262 assert!(identity.private_key.is_some(), "Identity should be owned");
263 assert!(identity.sender_key.is_none());
264 }
265
266 #[tokio::test]
267 async fn test_update_and_delete_identity() {
268 let client = setup_context_client().await;
269 let context_id = ContextId::from([1; DIGEST_SIZE]);
270 let public_key = client.new_identity().unwrap();
271
272 let private_key_bytes: [u8; DIGEST_SIZE] = [1; DIGEST_SIZE];
273 let mut handle = client.datastore.handle();
274 let key = key::ContextIdentity::new(context_id, public_key);
275 let id_data = types::ContextIdentity {
276 private_key: Some(private_key_bytes.into()),
277 sender_key: None,
278 };
279 handle.put(&key, &id_data).unwrap();
280
281 let sender_private_key = PrivateKey::from([2; DIGEST_SIZE]);
282 let mut identity_to_update = client
283 .get_identity(&context_id, &public_key)
284 .unwrap()
285 .unwrap();
286 identity_to_update.sender_key = Some(sender_private_key);
287
288 client
289 .update_identity(&context_id, &identity_to_update)
290 .unwrap();
291
292 let updated_identity = client
293 .get_identity(&context_id, &public_key)
294 .unwrap()
295 .unwrap();
296 assert!(
297 updated_identity.sender_key.is_some(),
298 "Sender key should have been updated"
299 );
300
301 client.delete_identity(&context_id, &public_key).unwrap();
302
303 let final_identity = client.get_identity(&context_id, &public_key).unwrap();
304 assert!(
305 final_identity.is_none(),
306 "Identity should be None after deletion"
307 );
308 }
309}