mithril_common/messages/
certificate_list.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::fmt::{Debug, Formatter};
4
5use crate::entities::{
6    Epoch, ProtocolMessage, ProtocolMessagePartKey, ProtocolParameters, ProtocolVersion,
7    SignedEntityType,
8};
9
10/// Message structure of a certificate list
11pub type CertificateListMessage = Vec<CertificateListItemMessage>;
12
13/// CertificateListItemMessage represents the metadata associated to a CertificateListItemMessage
14#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
15pub struct CertificateListItemMessageMetadata {
16    /// Cardano network
17    /// part of METADATA(p,n)
18    pub network: String,
19
20    /// Protocol Version (semver)
21    /// Useful to achieve backward compatibility of the certificates (including of the multi signature)
22    /// part of METADATA(p,n)
23    #[serde(rename = "version")]
24    pub protocol_version: ProtocolVersion,
25
26    /// Protocol parameters
27    /// part of METADATA(p,n)
28    #[serde(rename = "parameters")]
29    pub protocol_parameters: ProtocolParameters,
30
31    /// Date and time when the certificate was initiated
32    /// Represents the time at which the single signatures registration is opened
33    /// part of METADATA(p,n)
34    pub initiated_at: DateTime<Utc>,
35
36    /// Date and time when the certificate was sealed
37    /// Represents the time at which the quorum of single signatures was reached so that they were aggregated into a multi signature
38    /// part of METADATA(p,n)
39    pub sealed_at: DateTime<Utc>,
40
41    /// The number of signers that contributed to the certificate
42    /// part of METADATA(p,n)
43    pub total_signers: usize,
44}
45
46/// Message structure of a certificate list item
47#[derive(Clone, PartialEq, Serialize, Deserialize)]
48pub struct CertificateListItemMessage {
49    /// Hash of the current certificate
50    /// Computed from the other fields of the certificate
51    /// aka H(Cp,n))
52    pub hash: String,
53
54    /// Hash of the previous certificate in the chain
55    /// This is either the hash of the first certificate of the epoch in the chain
56    /// Or the first certificate of the previous epoch in the chain (if the certificate is the first of its epoch)
57    /// aka H(FC(n))
58    pub previous_hash: String,
59
60    /// Epoch of the Cardano chain
61    pub epoch: Epoch,
62
63    /// The signed entity type of the message.
64    /// aka BEACON(p,n)
65    pub signed_entity_type: SignedEntityType,
66
67    /// Certificate metadata
68    /// aka METADATA(p,n)
69    pub metadata: CertificateListItemMessageMetadata,
70
71    /// Structured message that is used to create the signed message
72    /// aka MSG(p,n) U AVK(n-1)
73    pub protocol_message: ProtocolMessage,
74
75    /// Message that is signed by the signers
76    /// aka H(MSG(p,n) || AVK(n-1))
77    pub signed_message: String,
78
79    /// Aggregate verification key
80    /// The AVK used to sign during the current epoch
81    /// aka AVK(n-2)
82    pub aggregate_verification_key: String,
83}
84
85impl CertificateListItemMessage {
86    /// Return a dummy test entity (test-only).
87    pub fn dummy() -> Self {
88        let mut protocol_message = ProtocolMessage::new();
89        protocol_message.set_message_part(
90            ProtocolMessagePartKey::SnapshotDigest,
91            "snapshot-digest-123".to_string(),
92        );
93        protocol_message.set_message_part(
94            ProtocolMessagePartKey::NextAggregateVerificationKey,
95            "next-avk-123".to_string(),
96        );
97        let epoch = Epoch(10);
98
99        Self {
100            hash: "hash".to_string(),
101            previous_hash: "previous_hash".to_string(),
102            epoch,
103            signed_entity_type: SignedEntityType::MithrilStakeDistribution(epoch),
104            metadata: CertificateListItemMessageMetadata {
105                network: "testnet".to_string(),
106                protocol_version: "0.1.0".to_string(),
107                protocol_parameters: ProtocolParameters::new(1000, 100, 0.123),
108                initiated_at: DateTime::parse_from_rfc3339("2024-02-12T13:11:47Z")
109                    .unwrap()
110                    .with_timezone(&Utc),
111                sealed_at: DateTime::parse_from_rfc3339("2024-02-12T13:12:57Z")
112                    .unwrap()
113                    .with_timezone(&Utc),
114                total_signers: 2,
115            },
116            protocol_message: protocol_message.clone(),
117            signed_message: "signed_message".to_string(),
118            aggregate_verification_key: "aggregate_verification_key".to_string(),
119        }
120    }
121}
122
123impl Debug for CertificateListItemMessage {
124    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
125        let should_be_exhaustive = f.alternate();
126        let mut debug = f.debug_struct("Certificate");
127        debug
128            .field("hash", &self.hash)
129            .field("previous_hash", &self.previous_hash)
130            .field("epoch", &format_args!("{:?}", self.epoch))
131            .field(
132                "signed_entity_type",
133                &format_args!("{:?}", self.signed_entity_type),
134            )
135            .field("metadata", &format_args!("{:?}", self.metadata))
136            .field(
137                "protocol_message",
138                &format_args!("{:?}", self.protocol_message),
139            )
140            .field("signed_message", &self.signed_message);
141
142        match should_be_exhaustive {
143            true => debug
144                .field(
145                    "aggregate_verification_key",
146                    &self.aggregate_verification_key,
147                )
148                .finish(),
149            false => debug.finish_non_exhaustive(),
150        }
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use crate::entities::CardanoDbBeacon;
157
158    use super::*;
159
160    const CURRENT_JSON: &str = r#"[{
161            "hash": "hash",
162            "previous_hash": "previous_hash",
163            "epoch": 10,
164            "signed_entity_type": {
165                "CardanoImmutableFilesFull": {
166                    "epoch": 10,
167                    "immutable_file_number": 1728
168                }
169            },
170            "metadata": {
171                "network": "testnet",
172                "version": "0.1.0",
173                "parameters": {
174                    "k": 1000,
175                    "m": 100,
176                    "phi_f": 0.123
177                },
178                "initiated_at": "2024-02-12T13:11:47Z",
179                "sealed_at": "2024-02-12T13:12:57Z",
180                "total_signers": 2
181            },
182            "protocol_message": {
183                "message_parts": {
184                    "snapshot_digest": "snapshot-digest-123",
185                    "next_aggregate_verification_key": "next-avk-123"
186                }
187            },
188            "signed_message": "signed_message",
189            "aggregate_verification_key": "aggregate_verification_key"
190        }]"#;
191
192    fn golden_current_message() -> CertificateListItemMessage {
193        let mut protocol_message = ProtocolMessage::new();
194        protocol_message.set_message_part(
195            ProtocolMessagePartKey::SnapshotDigest,
196            "snapshot-digest-123".to_string(),
197        );
198        protocol_message.set_message_part(
199            ProtocolMessagePartKey::NextAggregateVerificationKey,
200            "next-avk-123".to_string(),
201        );
202        let epoch = Epoch(10);
203
204        CertificateListItemMessage {
205            hash: "hash".to_string(),
206            previous_hash: "previous_hash".to_string(),
207            epoch,
208            signed_entity_type: SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::new(
209                *epoch, 1728,
210            )),
211            metadata: CertificateListItemMessageMetadata {
212                network: "testnet".to_string(),
213                protocol_version: "0.1.0".to_string(),
214                protocol_parameters: ProtocolParameters::new(1000, 100, 0.123),
215                initiated_at: DateTime::parse_from_rfc3339("2024-02-12T13:11:47Z")
216                    .unwrap()
217                    .with_timezone(&Utc),
218                sealed_at: DateTime::parse_from_rfc3339("2024-02-12T13:12:57Z")
219                    .unwrap()
220                    .with_timezone(&Utc),
221                total_signers: 2,
222            },
223            protocol_message: protocol_message.clone(),
224            signed_message: "signed_message".to_string(),
225            aggregate_verification_key: "aggregate_verification_key".to_string(),
226        }
227    }
228
229    #[test]
230    fn test_current_json_deserialized_into_current_message() {
231        let json = CURRENT_JSON;
232        let message: CertificateListMessage = serde_json::from_str(json).unwrap();
233
234        assert_eq!(vec![golden_current_message()], message);
235    }
236}