calimero_node_primitives/sync/
handshake.rs1use borsh::{BorshDeserialize, BorshSerialize};
6
7use super::protocol::{SyncProtocol, SyncProtocolKind};
8
9pub const SYNC_PROTOCOL_VERSION: u32 = 1;
17
18#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
26pub struct SyncCapabilities {
27 pub supports_compression: bool,
29 pub max_batch_size: u64,
31 pub supported_protocols: Vec<SyncProtocolKind>,
33}
34
35impl Default for SyncCapabilities {
36 fn default() -> Self {
37 Self {
38 supports_compression: true,
39 max_batch_size: 1000,
40 supported_protocols: vec![
41 SyncProtocolKind::None,
42 SyncProtocolKind::DeltaSync,
43 SyncProtocolKind::HashComparison,
44 SyncProtocolKind::Snapshot,
45 SyncProtocolKind::LevelWise,
46 ],
47 }
48 }
49}
50
51#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
61pub struct SyncHandshake {
62 pub version: u32,
64 pub root_hash: [u8; 32],
66 pub entity_count: u64,
68 pub max_depth: u32,
70 pub dag_heads: Vec<[u8; 32]>,
72 pub has_state: bool,
74 pub supported_protocols: Vec<SyncProtocolKind>,
76}
77
78impl SyncHandshake {
79 #[must_use]
81 pub fn new(
82 root_hash: [u8; 32],
83 entity_count: u64,
84 max_depth: u32,
85 dag_heads: Vec<[u8; 32]>,
86 ) -> Self {
87 let has_state = root_hash != [0; 32];
88 Self {
89 version: SYNC_PROTOCOL_VERSION,
90 root_hash,
91 entity_count,
92 max_depth,
93 dag_heads,
94 has_state,
95 supported_protocols: SyncCapabilities::default().supported_protocols,
96 }
97 }
98
99 #[must_use]
101 pub fn is_version_compatible(&self, other: &Self) -> bool {
102 self.version == other.version
103 }
104
105 #[must_use]
107 pub fn is_in_sync(&self, other: &Self) -> bool {
108 self.root_hash == other.root_hash
109 }
110}
111
112impl Default for SyncHandshake {
113 fn default() -> Self {
114 Self::new([0; 32], 0, 0, vec![])
115 }
116}
117
118#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
124pub struct SyncHandshakeResponse {
125 pub selected_protocol: SyncProtocol,
127 pub root_hash: [u8; 32],
129 pub entity_count: u64,
131 pub capabilities: SyncCapabilities,
133}
134
135impl SyncHandshakeResponse {
136 #[must_use]
138 pub fn already_synced(root_hash: [u8; 32], entity_count: u64) -> Self {
139 Self {
140 selected_protocol: SyncProtocol::None,
141 root_hash,
142 entity_count,
143 capabilities: SyncCapabilities::default(),
144 }
145 }
146
147 #[must_use]
149 pub fn with_protocol(
150 selected_protocol: SyncProtocol,
151 root_hash: [u8; 32],
152 entity_count: u64,
153 ) -> Self {
154 Self {
155 selected_protocol,
156 root_hash,
157 entity_count,
158 capabilities: SyncCapabilities::default(),
159 }
160 }
161}
162
163#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_sync_capabilities_roundtrip() {
173 let caps = SyncCapabilities::default();
174 let encoded = borsh::to_vec(&caps).expect("serialize");
175 let decoded: SyncCapabilities = borsh::from_slice(&encoded).expect("deserialize");
176 assert_eq!(caps, decoded);
177 }
178
179 #[test]
180 fn test_sync_handshake_roundtrip() {
181 let handshake = SyncHandshake::new([42; 32], 100, 5, vec![[1; 32], [2; 32]]);
182
183 let encoded = borsh::to_vec(&handshake).expect("serialize");
184 let decoded: SyncHandshake = borsh::from_slice(&encoded).expect("deserialize");
185
186 assert_eq!(handshake, decoded);
187 assert_eq!(decoded.version, SYNC_PROTOCOL_VERSION);
188 assert!(decoded.has_state); }
190
191 #[test]
192 fn test_sync_handshake_response_roundtrip() {
193 let response = SyncHandshakeResponse::with_protocol(
194 SyncProtocol::HashComparison {
195 root_hash: [7; 32],
196 divergent_subtrees: vec![],
197 },
198 [8; 32],
199 500,
200 );
201
202 let encoded = borsh::to_vec(&response).expect("serialize");
203 let decoded: SyncHandshakeResponse = borsh::from_slice(&encoded).expect("deserialize");
204
205 assert_eq!(response, decoded);
206 }
207
208 #[test]
209 fn test_sync_handshake_version_compatibility() {
210 let local = SyncHandshake::new([1; 32], 10, 2, vec![]);
211 let compatible = SyncHandshake::new([2; 32], 20, 3, vec![]);
212 let incompatible = SyncHandshake {
213 version: SYNC_PROTOCOL_VERSION + 1,
214 ..SyncHandshake::default()
215 };
216
217 assert!(local.is_version_compatible(&compatible));
218 assert!(!local.is_version_compatible(&incompatible));
219 }
220
221 #[test]
222 fn test_sync_handshake_in_sync_detection() {
223 let local = SyncHandshake::new([42; 32], 100, 5, vec![]);
224 let same_hash = SyncHandshake::new([42; 32], 200, 6, vec![[1; 32]]);
225 let different_hash = SyncHandshake::new([99; 32], 100, 5, vec![]);
226
227 assert!(local.is_in_sync(&same_hash));
228 assert!(!local.is_in_sync(&different_hash));
229 }
230
231 #[test]
232 fn test_sync_handshake_fresh_node() {
233 let fresh = SyncHandshake::new([0; 32], 0, 0, vec![]);
234 assert!(!fresh.has_state);
235
236 let initialized = SyncHandshake::new([1; 32], 1, 1, vec![]);
237 assert!(initialized.has_state);
238 }
239
240 #[test]
241 fn test_sync_handshake_response_already_synced() {
242 let response = SyncHandshakeResponse::already_synced([42; 32], 100);
243 assert_eq!(response.selected_protocol, SyncProtocol::None);
244 }
245}