calimero_context/handlers/
delete_context.rs1use core::error::Error;
2
3use actix::{ActorResponse, ActorTryFutureExt, Handler, Message, WrapFuture};
4use calimero_context_primitives::messages::{DeleteContextRequest, DeleteContextResponse};
5use calimero_node_primitives::client::NodeClient;
6use calimero_primitives::context::ContextId;
7use calimero_store::key::Key;
8use calimero_store::layer::{ReadLayer, WriteLayer};
9use calimero_store::{key, Store};
10use either::Either;
11use eyre::bail;
12
13use crate::ContextManager;
14
15impl Handler<DeleteContextRequest> for ContextManager {
16 type Result = ActorResponse<Self, <DeleteContextRequest as Message>::Result>;
17
18 fn handle(
19 &mut self,
20 DeleteContextRequest { context_id }: DeleteContextRequest,
21 _ctx: &mut Self::Context,
22 ) -> Self::Result {
23 let context = self.contexts.get(&context_id);
24
25 let mut guard = None;
26
27 if let Some(context) = context {
28 guard = Some(context.lock());
29 } else {
30 match self.context_client.has_context(&context_id) {
31 Ok(true) => {}
32 Ok(false) => {
33 return ActorResponse::reply(Ok(DeleteContextResponse { deleted: false }))
34 }
35 Err(err) => return ActorResponse::reply(Err(err)),
36 }
37 }
38
39 let datastore = self.datastore.clone();
40 let node_client = self.node_client.clone();
41
42 let task = async move {
43 let _guard = match guard {
44 Some(Either::Left(guard)) => Some(guard),
45 Some(Either::Right(task)) => Some(task.await),
46 None => None,
47 };
48
49 delete_context(datastore, node_client, context_id).await?;
50
51 Ok(DeleteContextResponse { deleted: true })
52 };
53
54 ActorResponse::r#async(task.into_actor(self).map_ok(move |res, act, _ctx| {
55 let _ignored = act.contexts.remove(&context_id);
56
57 res
58 }))
59 }
60}
61
62async fn delete_context(
63 datastore: Store,
64 node_client: NodeClient,
65 context_id: ContextId,
66) -> eyre::Result<()> {
67 node_client.unsubscribe(&context_id).await?;
68
69 let mut handle = datastore.handle();
70
71 let key = key::ContextMeta::new(context_id);
72
73 handle.delete(&key)?;
74 handle.delete(&key::ContextConfig::new(context_id))?;
75
76 let mut datastore = handle.into_inner();
78
79 delete_context_scoped::<key::ContextIdentity, 32>(&mut datastore, &context_id, [0; 32], None)?;
80
81 delete_context_scoped::<key::ContextState, 32>(&mut datastore, &context_id, [0; 32], None)?;
82
83 Ok(())
93}
94
95#[expect(clippy::unwrap_in_result, reason = "pre-validated")]
96fn delete_context_scoped<K, const N: usize>(
97 datastore: &mut Store,
98 context_id: &ContextId,
99 offset: [u8; N],
100 end: Option<[u8; N]>,
101) -> eyre::Result<()>
102where
103 K: key::FromKeyParts<Error: Error + Send + Sync>,
104{
105 let expected_length = Key::<K::Components>::len();
106
107 if context_id.len().saturating_add(N) != expected_length {
108 bail!(
109 "key length mismatch, expected: {}, got: {}",
110 Key::<K::Components>::len() - N,
111 N
112 )
113 }
114
115 let mut keys = vec![];
116
117 let mut key = context_id.to_vec();
118
119 let end = end
120 .map(|end| {
121 key.extend_from_slice(&end);
122
123 let end = Key::<K::Components>::try_from_slice(&key).expect("length pre-matched");
124
125 K::try_from_parts(end)
126 })
127 .transpose()?;
128
129 'outer: loop {
130 key.truncate(context_id.len());
131 key.extend_from_slice(&offset);
132
133 let offset = Key::<K::Components>::try_from_slice(&key).expect("length pre-matched");
134
135 let mut iter = datastore.iter()?;
136
137 let first = iter.seek(K::try_from_parts(offset)?).transpose();
138
139 if first.is_none() {
140 break;
141 }
142
143 for k in first.into_iter().chain(iter.keys()) {
144 let k = k?;
145
146 let key = k.as_key();
147
148 if let Some(end) = end {
149 if key == end.as_key() {
150 break 'outer;
151 }
152 }
153
154 if !key.as_bytes().starts_with(&**context_id) {
155 break 'outer;
156 }
157
158 keys.push(k);
159
160 if keys.len() == 100 {
161 break;
162 }
163 }
164
165 drop(iter);
166
167 #[expect(clippy::iter_with_drain, reason = "reallocation would be a bad idea")]
168 for k in keys.drain(..) {
169 datastore.delete(&k)?;
170 }
171 }
172
173 for k in keys {
174 datastore.delete(&k)?;
175 }
176
177 Ok(())
178}