calimero_node_primitives/sync/
delta.rs1use borsh::{BorshDeserialize, BorshSerialize};
6
7pub const DEFAULT_DELTA_SYNC_THRESHOLD: usize = 128;
20
21#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
32pub struct DeltaSyncRequest {
33 pub missing_delta_ids: Vec<[u8; 32]>,
35}
36
37impl DeltaSyncRequest {
38 #[must_use]
40 pub fn new(missing_delta_ids: Vec<[u8; 32]>) -> Self {
41 Self { missing_delta_ids }
42 }
43
44 #[must_use]
46 pub fn is_within_threshold(&self) -> bool {
47 self.missing_delta_ids.len() <= DEFAULT_DELTA_SYNC_THRESHOLD
48 }
49
50 #[must_use]
52 pub fn count(&self) -> usize {
53 self.missing_delta_ids.len()
54 }
55}
56
57#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
62pub struct DeltaSyncResponse {
63 pub deltas: Vec<DeltaPayload>,
66
67 pub not_found: Vec<[u8; 32]>,
69}
70
71impl DeltaSyncResponse {
72 #[must_use]
74 pub fn new(deltas: Vec<DeltaPayload>, not_found: Vec<[u8; 32]>) -> Self {
75 Self { deltas, not_found }
76 }
77
78 #[must_use]
80 pub fn empty(not_found: Vec<[u8; 32]>) -> Self {
81 Self {
82 deltas: vec![],
83 not_found,
84 }
85 }
86
87 #[must_use]
89 pub fn is_complete(&self) -> bool {
90 self.not_found.is_empty()
91 }
92
93 #[must_use]
95 pub fn count(&self) -> usize {
96 self.deltas.len()
97 }
98}
99
100#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
108pub struct DeltaPayload {
109 pub id: [u8; 32],
111
112 pub parents: Vec<[u8; 32]>,
114
115 pub payload: Vec<u8>,
117
118 pub hlc_timestamp: u64,
120
121 pub expected_root_hash: [u8; 32],
136}
137
138impl DeltaPayload {
139 #[must_use]
141 pub fn is_genesis(&self) -> bool {
142 self.parents.is_empty()
143 }
144}
145
146#[derive(Clone, Debug)]
155pub enum DeltaApplyResult {
156 Success {
158 applied_count: usize,
160 new_root_hash: [u8; 32],
162 },
163
164 MissingParents {
167 missing_parent_deltas: Vec<[u8; 32]>,
169 applied_before_failure: usize,
171 },
172
173 Failed {
175 reason: String,
177 },
178}
179
180impl DeltaApplyResult {
181 #[must_use]
183 pub fn is_success(&self) -> bool {
184 matches!(self, Self::Success { .. })
185 }
186
187 #[must_use]
189 pub fn needs_state_sync(&self) -> bool {
190 matches!(self, Self::MissingParents { .. })
191 }
192}
193
194#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_delta_sync_request_roundtrip() {
204 let request = DeltaSyncRequest::new(vec![[1; 32], [2; 32], [3; 32]]);
205
206 let encoded = borsh::to_vec(&request).expect("serialize");
207 let decoded: DeltaSyncRequest = borsh::from_slice(&encoded).expect("deserialize");
208
209 assert_eq!(request, decoded);
210 assert_eq!(decoded.count(), 3);
211 }
212
213 #[test]
214 fn test_delta_sync_request_threshold() {
215 let small_request = DeltaSyncRequest::new(vec![[1; 32]; 10]);
217 assert!(small_request.is_within_threshold());
218
219 let at_threshold = DeltaSyncRequest::new(vec![[1; 32]; DEFAULT_DELTA_SYNC_THRESHOLD]);
221 assert!(at_threshold.is_within_threshold());
222
223 let large_request = DeltaSyncRequest::new(vec![[1; 32]; DEFAULT_DELTA_SYNC_THRESHOLD + 1]);
225 assert!(!large_request.is_within_threshold());
226 }
227
228 #[test]
229 fn test_delta_payload_roundtrip() {
230 let payload = DeltaPayload {
231 id: [1; 32],
232 parents: vec![[2; 32], [3; 32]],
233 payload: vec![4, 5, 6, 7],
234 hlc_timestamp: 12345678,
235 expected_root_hash: [8; 32],
236 };
237
238 let encoded = borsh::to_vec(&payload).expect("serialize");
239 let decoded: DeltaPayload = borsh::from_slice(&encoded).expect("deserialize");
240
241 assert_eq!(payload, decoded);
242 assert!(!decoded.is_genesis());
243 }
244
245 #[test]
246 fn test_delta_payload_genesis() {
247 let genesis = DeltaPayload {
248 id: [1; 32],
249 parents: vec![], payload: vec![1, 2, 3],
251 hlc_timestamp: 0,
252 expected_root_hash: [2; 32],
253 };
254
255 assert!(genesis.is_genesis());
256
257 let non_genesis = DeltaPayload {
258 id: [2; 32],
259 parents: vec![[1; 32]], payload: vec![4, 5, 6],
261 hlc_timestamp: 1,
262 expected_root_hash: [3; 32],
263 };
264
265 assert!(!non_genesis.is_genesis());
266 }
267
268 #[test]
269 fn test_delta_sync_response_roundtrip() {
270 let delta1 = DeltaPayload {
271 id: [1; 32],
272 parents: vec![],
273 payload: vec![1, 2, 3],
274 hlc_timestamp: 100,
275 expected_root_hash: [10; 32],
276 };
277 let delta2 = DeltaPayload {
278 id: [2; 32],
279 parents: vec![[1; 32]],
280 payload: vec![4, 5, 6],
281 hlc_timestamp: 200,
282 expected_root_hash: [20; 32],
283 };
284
285 let response = DeltaSyncResponse::new(vec![delta1, delta2], vec![[99; 32]]);
286
287 let encoded = borsh::to_vec(&response).expect("serialize");
288 let decoded: DeltaSyncResponse = borsh::from_slice(&encoded).expect("deserialize");
289
290 assert_eq!(response, decoded);
291 assert_eq!(decoded.count(), 2);
292 assert!(!decoded.is_complete()); }
294
295 #[test]
296 fn test_delta_sync_response_complete() {
297 let delta = DeltaPayload {
298 id: [1; 32],
299 parents: vec![],
300 payload: vec![1, 2, 3],
301 hlc_timestamp: 100,
302 expected_root_hash: [10; 32],
303 };
304
305 let complete_response = DeltaSyncResponse::new(vec![delta], vec![]); assert!(complete_response.is_complete());
307
308 let incomplete_response = DeltaSyncResponse::empty(vec![[1; 32]]);
309 assert!(!incomplete_response.is_complete());
310 assert_eq!(incomplete_response.count(), 0);
311 }
312
313 #[test]
314 fn test_delta_apply_result_success() {
315 let success = DeltaApplyResult::Success {
316 applied_count: 5,
317 new_root_hash: [1; 32],
318 };
319 assert!(success.is_success());
320 assert!(!success.needs_state_sync());
321 }
322
323 #[test]
324 fn test_delta_apply_result_missing_parents() {
325 let missing = DeltaApplyResult::MissingParents {
326 missing_parent_deltas: vec![[1; 32]],
327 applied_before_failure: 3,
328 };
329 assert!(!missing.is_success());
330 assert!(missing.needs_state_sync());
331 }
332
333 #[test]
334 fn test_delta_apply_result_failed() {
335 let failed = DeltaApplyResult::Failed {
336 reason: "hash mismatch".to_string(),
337 };
338 assert!(!failed.is_success());
339 assert!(!failed.needs_state_sync());
340 }
341}