1use serde::{Deserialize, Serialize};
2use sha2::{Digest, Sha256};
3use std::{collections::BTreeMap, fmt::Display};
4
5#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
7pub enum ProtocolMessagePartKey {
8 #[serde(rename = "snapshot_digest")]
10 SnapshotDigest,
11
12 #[serde(rename = "cardano_transactions_merkle_root")]
14 CardanoTransactionsMerkleRoot,
15
16 #[serde(rename = "cardano_blocks_transactions_merkle_root")]
18 CardanoBlocksTransactionsMerkleRoot,
19
20 #[serde(rename = "next_aggregate_verification_key")]
25 NextAggregateVerificationKey,
26
27 #[serde(rename = "next_protocol_parameters")]
32 NextProtocolParameters,
33
34 #[serde(rename = "current_epoch")]
38 CurrentEpoch,
39
40 #[serde(rename = "latest_block_number")]
42 LatestBlockNumber,
43
44 #[serde(rename = "cardano_stake_distribution_epoch")]
46 CardanoStakeDistributionEpoch,
47
48 #[serde(rename = "cardano_stake_distribution_merkle_root")]
50 CardanoStakeDistributionMerkleRoot,
51
52 #[serde(rename = "cardano_database_merkle_root")]
54 CardanoDatabaseMerkleRoot,
55}
56
57impl Display for ProtocolMessagePartKey {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 match *self {
60 Self::SnapshotDigest => write!(f, "snapshot_digest"),
61 Self::NextAggregateVerificationKey => write!(f, "next_aggregate_verification_key"),
62 Self::NextProtocolParameters => write!(f, "next_protocol_parameters"),
63 Self::CurrentEpoch => write!(f, "current_epoch"),
64 Self::CardanoTransactionsMerkleRoot => write!(f, "cardano_transactions_merkle_root"),
65 Self::CardanoBlocksTransactionsMerkleRoot => {
66 write!(f, "cardano_blocks_transactions_merkle_root")
67 }
68 Self::LatestBlockNumber => write!(f, "latest_block_number"),
69 Self::CardanoStakeDistributionEpoch => write!(f, "cardano_stake_distribution_epoch"),
70 Self::CardanoStakeDistributionMerkleRoot => {
71 write!(f, "cardano_stake_distribution_merkle_root")
72 }
73 Self::CardanoDatabaseMerkleRoot => write!(f, "cardano_database_merkle_root"),
74 }
75 }
76}
77
78pub type ProtocolMessagePartValue = String;
80
81#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
83pub struct ProtocolMessage {
84 pub message_parts: BTreeMap<ProtocolMessagePartKey, ProtocolMessagePartValue>,
87}
88
89impl ProtocolMessage {
90 pub fn new() -> ProtocolMessage {
92 ProtocolMessage {
93 message_parts: BTreeMap::new(),
94 }
95 }
96
97 pub fn set_message_part(
100 &mut self,
101 key: ProtocolMessagePartKey,
102 value: ProtocolMessagePartValue,
103 ) -> Option<ProtocolMessagePartValue> {
104 self.message_parts.insert(key, value)
105 }
106
107 pub fn get_message_part(
109 &self,
110 key: &ProtocolMessagePartKey,
111 ) -> Option<&ProtocolMessagePartValue> {
112 self.message_parts.get(key)
113 }
114
115 pub fn compute_hash(&self) -> String {
117 let mut hasher = Sha256::new();
118 self.message_parts.iter().for_each(|(k, v)| {
119 hasher.update(k.to_string().as_bytes());
120 hasher.update(v.as_bytes());
121 });
122 hex::encode(hasher.finalize())
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_protocol_message_compute_hash_include_next_aggregate_verification_key() {
132 let protocol_message = ProtocolMessage::new();
133 let hash_before_change = protocol_message.compute_hash();
134
135 let mut protocol_message_modified = protocol_message.clone();
136 protocol_message_modified.set_message_part(
137 ProtocolMessagePartKey::NextAggregateVerificationKey,
138 "next-avk-456".to_string(),
139 );
140
141 assert_ne!(hash_before_change, protocol_message_modified.compute_hash());
142 }
143
144 #[test]
145 fn test_protocol_message_compute_hash_include_snapshot_digest() {
146 let protocol_message = ProtocolMessage::new();
147 let hash_before_change = protocol_message.compute_hash();
148
149 let mut protocol_message_modified = protocol_message.clone();
150 protocol_message_modified.set_message_part(
151 ProtocolMessagePartKey::SnapshotDigest,
152 "snapshot-digest-456".to_string(),
153 );
154
155 assert_ne!(hash_before_change, protocol_message_modified.compute_hash());
156 }
157
158 #[test]
159 fn test_protocol_message_compute_hash_include_cardano_transactions_merkle_root() {
160 let protocol_message = ProtocolMessage::new();
161 let hash_before_change = protocol_message.compute_hash();
162
163 let mut protocol_message_modified = protocol_message.clone();
164 protocol_message_modified.set_message_part(
165 ProtocolMessagePartKey::CardanoTransactionsMerkleRoot,
166 "ctx-merke-root-456".to_string(),
167 );
168
169 assert_ne!(hash_before_change, protocol_message_modified.compute_hash());
170 }
171
172 #[test]
173 fn test_protocol_message_compute_hash_include_cardano_blocks_transactions_merkle_root() {
174 let protocol_message = ProtocolMessage::new();
175 let hash_before_change = protocol_message.compute_hash();
176
177 let mut protocol_message_modified = protocol_message.clone();
178 protocol_message_modified.set_message_part(
179 ProtocolMessagePartKey::CardanoBlocksTransactionsMerkleRoot,
180 "cardano-blocks-tx-merkle-root-456".to_string(),
181 );
182
183 assert_ne!(hash_before_change, protocol_message_modified.compute_hash());
184 }
185
186 #[test]
187 fn test_protocol_message_compute_hash_include_cardano_stake_distribution_epoch() {
188 let protocol_message = ProtocolMessage::new();
189 let hash_before_change = protocol_message.compute_hash();
190
191 let mut protocol_message_modified = protocol_message.clone();
192 protocol_message_modified.set_message_part(
193 ProtocolMessagePartKey::CardanoStakeDistributionEpoch,
194 "cardano-stake-distribution-epoch-456".to_string(),
195 );
196
197 assert_ne!(hash_before_change, protocol_message_modified.compute_hash());
198 }
199
200 #[test]
201 fn test_protocol_message_compute_hash_include_cardano_stake_distribution_merkle_root() {
202 let protocol_message = ProtocolMessage::new();
203 let hash_before_change = protocol_message.compute_hash();
204
205 let mut protocol_message_modified = protocol_message.clone();
206 protocol_message_modified.set_message_part(
207 ProtocolMessagePartKey::CardanoStakeDistributionMerkleRoot,
208 "cardano-stake-distribution-merkle-root-456".to_string(),
209 );
210
211 assert_ne!(hash_before_change, protocol_message_modified.compute_hash());
212 }
213
214 #[test]
215 fn test_protocol_message_compute_hash_include_lastest_immutable_file_number() {
216 let protocol_message = ProtocolMessage::new();
217 let hash_before_change = protocol_message.compute_hash();
218
219 let mut protocol_message_modified = protocol_message.clone();
220 protocol_message_modified.set_message_part(
221 ProtocolMessagePartKey::LatestBlockNumber,
222 "latest-immutable-file-number-456".to_string(),
223 );
224
225 assert_ne!(hash_before_change, protocol_message_modified.compute_hash());
226 }
227
228 #[test]
229 fn test_protocol_message_compute_hash_include_cardano_database_merkle_root() {
230 let protocol_message = ProtocolMessage::new();
231 let hash_before_change = protocol_message.compute_hash();
232
233 let mut protocol_message_modified = protocol_message.clone();
234 protocol_message_modified.set_message_part(
235 ProtocolMessagePartKey::CardanoDatabaseMerkleRoot,
236 "cardano-database-merkle-root-456".to_string(),
237 );
238
239 assert_ne!(hash_before_change, protocol_message_modified.compute_hash());
240 }
241
242 #[test]
243 fn test_protocol_message_compute_hash_include_next_protocol_parameters() {
244 let protocol_message = build_protocol_message_reference();
245 let hash_expected = protocol_message.compute_hash();
246
247 let mut protocol_message_modified = protocol_message.clone();
248 protocol_message_modified.set_message_part(
249 ProtocolMessagePartKey::NextProtocolParameters,
250 "latest-protocol-parameters-456".to_string(),
251 );
252
253 assert_ne!(hash_expected, protocol_message_modified.compute_hash());
254 }
255
256 #[test]
257 fn test_set_message_part_calling_order_have_no_influence_on_hash_computed() {
258 let mut protocol_message_a_b = build_protocol_message_reference();
259 protocol_message_a_b.set_message_part(
260 ProtocolMessagePartKey::CardanoBlocksTransactionsMerkleRoot,
261 "A".to_string(),
262 );
263 protocol_message_a_b.set_message_part(
264 ProtocolMessagePartKey::CardanoDatabaseMerkleRoot,
265 "B".to_string(),
266 );
267
268 let mut protocol_message_b_a = build_protocol_message_reference();
269 protocol_message_b_a.set_message_part(
270 ProtocolMessagePartKey::CardanoDatabaseMerkleRoot,
271 "B".to_string(),
272 );
273 protocol_message_b_a.set_message_part(
274 ProtocolMessagePartKey::CardanoBlocksTransactionsMerkleRoot,
275 "A".to_string(),
276 );
277
278 assert_eq!(
279 protocol_message_a_b.compute_hash(),
280 protocol_message_b_a.compute_hash()
281 );
282 }
283
284 #[test]
285 fn test_protocol_message_compute_hash_the_same_hash_with_same_protocol_message() {
286 assert_eq!(
287 build_protocol_message_reference().compute_hash(),
288 build_protocol_message_reference().compute_hash()
289 );
290 }
291
292 fn build_protocol_message_reference() -> ProtocolMessage {
293 let mut protocol_message = ProtocolMessage::new();
294 protocol_message.set_message_part(
295 ProtocolMessagePartKey::SnapshotDigest,
296 "snapshot-digest-123".to_string(),
297 );
298 protocol_message.set_message_part(
299 ProtocolMessagePartKey::NextAggregateVerificationKey,
300 "next-avk-123".to_string(),
301 );
302 protocol_message.set_message_part(
303 ProtocolMessagePartKey::NextProtocolParameters,
304 "next-protocol-parameters-123".to_string(),
305 );
306 protocol_message.set_message_part(
307 ProtocolMessagePartKey::CardanoTransactionsMerkleRoot,
308 "ctx-merkle-root-123".to_string(),
309 );
310 protocol_message.set_message_part(
311 ProtocolMessagePartKey::CardanoBlocksTransactionsMerkleRoot,
312 "cardano-blocks-tx-merkle-root-123".to_string(),
313 );
314 protocol_message.set_message_part(
315 ProtocolMessagePartKey::LatestBlockNumber,
316 "latest-immutable-file-number-123".to_string(),
317 );
318 protocol_message.set_message_part(
319 ProtocolMessagePartKey::CardanoStakeDistributionEpoch,
320 "cardano-stake-distribution-epoch-123".to_string(),
321 );
322 protocol_message.set_message_part(
323 ProtocolMessagePartKey::CardanoStakeDistributionMerkleRoot,
324 "cardano-stake-distribution-merkle-root-123".to_string(),
325 );
326 protocol_message.set_message_part(
327 ProtocolMessagePartKey::CardanoDatabaseMerkleRoot,
328 "cardano-database-merkle-root-123".to_string(),
329 );
330
331 protocol_message
332 }
333}