mithril_common/entities/
protocol_message.rs

1use serde::{Deserialize, Serialize};
2use sha2::{Digest, Sha256};
3use std::{collections::BTreeMap, fmt::Display};
4
5/// The key of a ProtocolMessage
6#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
7pub enum ProtocolMessagePartKey {
8    /// The ProtocolMessage part key associated to the Snapshot Digest
9    #[serde(rename = "snapshot_digest")]
10    SnapshotDigest,
11
12    /// The ProtocolMessage part key associated to the Cardano Transactions Merkle Root
13    #[serde(rename = "cardano_transactions_merkle_root")]
14    CardanoTransactionsMerkleRoot,
15
16    /// The ProtocolMessage part key associated to the Cardano Blocks and Transactions Merkle Root
17    #[serde(rename = "cardano_blocks_transactions_merkle_root")]
18    CardanoBlocksTransactionsMerkleRoot,
19
20    /// The ProtocolMessage part key associated to the Next epoch aggregate verification key
21    ///
22    /// The AVK that will be allowed to be used to sign during the next epoch
23    /// aka AVK(n-1)
24    #[serde(rename = "next_aggregate_verification_key")]
25    NextAggregateVerificationKey,
26
27    /// The ProtocolMessage part key associated to the Next epoch protocol parameters
28    ///
29    /// The protocol parameters that will be allowed to be used to sign during the next epoch
30    /// aka PPARAMS(n-1)
31    #[serde(rename = "next_protocol_parameters")]
32    NextProtocolParameters,
33
34    /// The ProtocolMessage part key associated to the current epoch
35    ///
36    /// aka EPOCH(n)
37    #[serde(rename = "current_epoch")]
38    CurrentEpoch,
39
40    /// The ProtocolMessage part key associated to the latest block number signed
41    #[serde(rename = "latest_block_number")]
42    LatestBlockNumber,
43
44    /// The ProtocolMessage part key associated to the epoch for which the Cardano stake distribution is computed
45    #[serde(rename = "cardano_stake_distribution_epoch")]
46    CardanoStakeDistributionEpoch,
47
48    /// The ProtocolMessage part key associated to the Cardano stake distribution Merkle root
49    #[serde(rename = "cardano_stake_distribution_merkle_root")]
50    CardanoStakeDistributionMerkleRoot,
51
52    /// The ProtocolMessage part key associated to the Cardano database Merkle root
53    #[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
78/// The value of a ProtocolMessage
79pub type ProtocolMessagePartValue = String;
80
81/// ProtocolMessage represents a message that is signed (or verified) by the Mithril protocol
82#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
83pub struct ProtocolMessage {
84    /// Map of the messages combined into the digest
85    /// aka MSG(p,n)
86    pub message_parts: BTreeMap<ProtocolMessagePartKey, ProtocolMessagePartValue>,
87}
88
89impl ProtocolMessage {
90    /// ProtocolMessage factory
91    pub fn new() -> ProtocolMessage {
92        ProtocolMessage {
93            message_parts: BTreeMap::new(),
94        }
95    }
96
97    /// Set the message part associated with a key
98    /// Returns previously set value if it exists
99    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    /// Get the message part associated with a key
108    pub fn get_message_part(
109        &self,
110        key: &ProtocolMessagePartKey,
111    ) -> Option<&ProtocolMessagePartValue> {
112        self.message_parts.get(key)
113    }
114
115    /// Computes the hash of the protocol message
116    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}