mithril_common/messages/
snapshot.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::entities::{CardanoDbBeacon, CompressionAlgorithm, Epoch};
5
6/// Message structure of a snapshot
7#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
8pub struct SnapshotMessage {
9    /// Digest that is signed by the signer participants
10    pub digest: String,
11
12    /// Cardano network
13    pub network: String,
14
15    /// Mithril beacon on the Cardano chain
16    pub beacon: CardanoDbBeacon,
17
18    /// Hash of the associated certificate
19    pub certificate_hash: String,
20
21    /// Size of the immutables snapshot file in Bytes
22    pub size: u64,
23
24    /// Size of the ancillary files in Bytes
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub ancillary_size: Option<u64>,
27
28    /// Date and time at which the snapshot was created
29    pub created_at: DateTime<Utc>,
30
31    /// Locations where the snapshot of the immutable files can be retrieved
32    pub locations: Vec<String>,
33
34    /// Locations where the snapshot of the ancillary files can be retrieved
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub ancillary_locations: Option<Vec<String>>,
37
38    /// Compression algorithm of the snapshot archive
39    pub compression_algorithm: CompressionAlgorithm,
40
41    /// Cardano node version
42    pub cardano_node_version: String,
43}
44
45impl SnapshotMessage {
46    /// Return a dummy test entity (test-only).
47    pub fn dummy() -> Self {
48        Self {
49            digest: "0b9f5ad7f33cc523775c82249294eb8a1541d54f08eb3107cafc5638403ec7c6".to_string(),
50            network: "preview".to_string(),
51            beacon: CardanoDbBeacon {
52                epoch: Epoch(86),
53                immutable_file_number: 1728,
54            },
55            certificate_hash: "d5daf6c03ace4a9c074e951844075b9b373bafc4e039160e3e2af01823e9abfb"
56                .to_string(),
57            size: 807803196,
58            ancillary_size: Some(123456789),
59            created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
60                .unwrap()
61                .with_timezone(&Utc),
62            locations: vec!["https://host/certificate.tar.gz".to_string()],
63            ancillary_locations: Some(vec!["https://host/ancillary.tar.gz".to_string()]),
64            compression_algorithm: CompressionAlgorithm::Gzip,
65            cardano_node_version: "0.0.1".to_string(),
66        }
67    }
68
69    /// Compute the total size of the snapshot including ancillary files
70    pub fn compute_total_size(&self) -> u64 {
71        self.size + self.ancillary_size.unwrap_or(0)
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
80    pub struct SnapshotMessageUntilV0_1_47 {
81        pub digest: String,
82        pub network: String,
83        pub beacon: CardanoDbBeacon,
84        pub certificate_hash: String,
85        pub size: u64,
86        pub created_at: DateTime<Utc>,
87        pub locations: Vec<String>,
88        pub compression_algorithm: CompressionAlgorithm,
89        pub cardano_node_version: String,
90    }
91
92    const CURRENT_JSON: &str = r#"{
93        "digest": "0b9f5ad7f33cc523775c82249294eb8a1541d54f08eb3107cafc5638403ec7c6",
94        "network": "preview",
95        "beacon": {
96            "epoch": 86,
97            "immutable_file_number": 1728
98        },
99        "certificate_hash": "d5daf6c03ace4a9c074e951844075b9b373bafc4e039160e3e2af01823e9abfb",
100        "size": 807803196,
101        "ancillary_size": 123456789,
102        "created_at": "2023-01-19T13:43:05.618857482Z",
103        "locations": [
104            "https://host/certificate.tar.gz"
105        ],
106        "ancillary_locations": [
107            "https://host/ancillary.tar.gz"
108        ],
109        "compression_algorithm": "gzip",
110        "cardano_node_version": "0.0.1"
111    }"#;
112
113    fn golden_message_until_open_api_0_1_47() -> SnapshotMessageUntilV0_1_47 {
114        SnapshotMessageUntilV0_1_47 {
115            digest: "0b9f5ad7f33cc523775c82249294eb8a1541d54f08eb3107cafc5638403ec7c6".to_string(),
116            network: "preview".to_string(),
117            beacon: CardanoDbBeacon {
118                epoch: Epoch(86),
119                immutable_file_number: 1728,
120            },
121            certificate_hash: "d5daf6c03ace4a9c074e951844075b9b373bafc4e039160e3e2af01823e9abfb"
122                .to_string(),
123            size: 807803196,
124            created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
125                .unwrap()
126                .with_timezone(&Utc),
127            locations: vec!["https://host/certificate.tar.gz".to_string()],
128            compression_algorithm: CompressionAlgorithm::Gzip,
129            cardano_node_version: "0.0.1".to_string(),
130        }
131    }
132
133    fn golden_message_current() -> SnapshotMessage {
134        SnapshotMessage {
135            digest: "0b9f5ad7f33cc523775c82249294eb8a1541d54f08eb3107cafc5638403ec7c6".to_string(),
136            network: "preview".to_string(),
137            beacon: CardanoDbBeacon {
138                epoch: Epoch(86),
139                immutable_file_number: 1728,
140            },
141            certificate_hash: "d5daf6c03ace4a9c074e951844075b9b373bafc4e039160e3e2af01823e9abfb"
142                .to_string(),
143            size: 807803196,
144            ancillary_size: Some(123456789),
145            created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
146                .unwrap()
147                .with_timezone(&Utc),
148            locations: vec!["https://host/certificate.tar.gz".to_string()],
149            ancillary_locations: Some(vec!["https://host/ancillary.tar.gz".to_string()]),
150            compression_algorithm: CompressionAlgorithm::Gzip,
151            cardano_node_version: "0.0.1".to_string(),
152        }
153    }
154
155    #[test]
156    fn test_current_json_deserialized_into_message_supported_until_open_api_0_1_47() {
157        let json = CURRENT_JSON;
158        let message: SnapshotMessageUntilV0_1_47 = serde_json::from_str(json).unwrap();
159
160        assert_eq!(golden_message_until_open_api_0_1_47(), message);
161    }
162
163    #[test]
164    fn test_current_json_deserialized_into_current_message() {
165        let json = CURRENT_JSON;
166        let message: SnapshotMessage = serde_json::from_str(json).unwrap();
167
168        assert_eq!(golden_message_current(), message);
169    }
170
171    #[test]
172    fn compute_total_size_with_ancillary_size() {
173        let message = SnapshotMessage {
174            size: 12,
175            ancillary_size: Some(33),
176            ..SnapshotMessage::dummy()
177        };
178
179        assert_eq!(message.compute_total_size(), 45);
180    }
181
182    #[test]
183    fn compute_total_size_without_ancillary_size() {
184        let message = SnapshotMessage {
185            size: 12,
186            ancillary_size: None,
187            ..SnapshotMessage::dummy()
188        };
189
190        assert_eq!(message.compute_total_size(), 12);
191    }
192}