mithril_client/
cardano_transaction_client.rs1use crate::aggregator_client::{AggregatorClient, AggregatorClientError, AggregatorRequest};
78use crate::{
79 CardanoTransactionSnapshot, CardanoTransactionSnapshotListItem, CardanoTransactionsProofs,
80 MithrilResult,
81};
82use anyhow::Context;
83use std::sync::Arc;
84
85pub struct CardanoTransactionClient {
87 aggregator_client: Arc<dyn AggregatorClient>,
88}
89
90impl CardanoTransactionClient {
91 pub fn new(aggregator_client: Arc<dyn AggregatorClient>) -> Self {
93 Self { aggregator_client }
94 }
95
96 pub async fn get_proofs<T: ToString>(
98 &self,
99 transactions_hashes: &[T],
100 ) -> MithrilResult<CardanoTransactionsProofs> {
101 match self
102 .aggregator_client
103 .get_content(AggregatorRequest::GetTransactionsProofs {
104 transactions_hashes: transactions_hashes.iter().map(|h| h.to_string()).collect(),
105 })
106 .await
107 {
108 Ok(content) => {
109 let transactions_proofs: CardanoTransactionsProofs = serde_json::from_str(&content)
110 .with_context(
111 || "CardanoTransactionProof Client can not deserialize transactions proofs",
112 )?;
113
114 Ok(transactions_proofs)
115 }
116 Err(e) => Err(e.into()),
117 }
118 }
119
120 pub async fn list_snapshots(&self) -> MithrilResult<Vec<CardanoTransactionSnapshotListItem>> {
122 let response = self
123 .aggregator_client
124 .get_content(AggregatorRequest::ListCardanoTransactionSnapshots)
125 .await
126 .with_context(|| "CardanoTransactionClient Client can not get the artifact list")?;
127 let items = serde_json::from_str::<Vec<CardanoTransactionSnapshotListItem>>(&response)
128 .with_context(|| "CardanoTransactionClient Client can not deserialize artifact list")?;
129
130 Ok(items)
131 }
132
133 pub async fn get_snapshot(
135 &self,
136 hash: &str,
137 ) -> MithrilResult<Option<CardanoTransactionSnapshot>> {
138 match self
139 .aggregator_client
140 .get_content(AggregatorRequest::GetCardanoTransactionSnapshot {
141 hash: hash.to_string(),
142 })
143 .await
144 {
145 Ok(content) => {
146 let cardano_transaction_snapshot: CardanoTransactionSnapshot =
147 serde_json::from_str(&content).with_context(
148 || "CardanoTransactionClient Client can not deserialize artifact",
149 )?;
150
151 Ok(Some(cardano_transaction_snapshot))
152 }
153 Err(AggregatorClientError::RemoteServerLogical(_)) => Ok(None),
154 Err(e) => Err(e.into()),
155 }
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use std::sync::Arc;
162
163 use anyhow::anyhow;
164 use chrono::{DateTime, Utc};
165 use mockall::predicate::eq;
166
167 use mithril_common::test::double::Dummy;
168
169 use crate::aggregator_client::{AggregatorClientError, MockAggregatorClient};
170 use crate::common::{BlockNumber, Epoch};
171 use crate::{
172 CardanoTransactionSnapshot, CardanoTransactionSnapshotListItem, CardanoTransactionsProofs,
173 CardanoTransactionsSetProof,
174 };
175
176 use super::*;
177
178 fn fake_messages() -> Vec<CardanoTransactionSnapshotListItem> {
179 vec![
180 CardanoTransactionSnapshotListItem {
181 merkle_root: "mk-123".to_string(),
182 epoch: Epoch(1),
183 block_number: BlockNumber(24),
184 hash: "hash-123".to_string(),
185 certificate_hash: "cert-hash-123".to_string(),
186 created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
187 .unwrap()
188 .with_timezone(&Utc),
189 },
190 CardanoTransactionSnapshotListItem {
191 merkle_root: "mk-456".to_string(),
192 epoch: Epoch(1),
193 block_number: BlockNumber(24),
194 hash: "hash-456".to_string(),
195 certificate_hash: "cert-hash-456".to_string(),
196 created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
197 .unwrap()
198 .with_timezone(&Utc),
199 },
200 ]
201 }
202
203 #[tokio::test]
204 async fn get_cardano_transactions_snapshot_list() {
205 let message = fake_messages();
206 let mut http_client = MockAggregatorClient::new();
207 http_client
208 .expect_get_content()
209 .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
210 let client = CardanoTransactionClient::new(Arc::new(http_client));
211 let items = client.list_snapshots().await.unwrap();
212
213 assert_eq!(2, items.len());
214 assert_eq!("hash-123".to_string(), items[0].hash);
215 assert_eq!("hash-456".to_string(), items[1].hash);
216 }
217
218 #[tokio::test]
219 async fn get_cardano_transactions_snapshot() {
220 let mut http_client = MockAggregatorClient::new();
221 let message = CardanoTransactionSnapshot {
222 merkle_root: "mk-123".to_string(),
223 epoch: Epoch(1),
224 block_number: BlockNumber(24),
225 hash: "hash-123".to_string(),
226 certificate_hash: "cert-hash-123".to_string(),
227 created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
228 .unwrap()
229 .with_timezone(&Utc),
230 };
231 let expected = message.clone();
232 http_client
233 .expect_get_content()
234 .with(eq(AggregatorRequest::GetCardanoTransactionSnapshot {
235 hash: "hash-123".to_string(),
236 }))
237 .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
238 let client = CardanoTransactionClient::new(Arc::new(http_client));
239 let cardano_transaction_snapshot = client
240 .get_snapshot("hash-123")
241 .await
242 .unwrap()
243 .expect("This test returns a cardano transaction snapshot");
244
245 assert_eq!(expected, cardano_transaction_snapshot);
246 }
247
248 #[tokio::test]
249 async fn test_get_proof_ok() {
250 let mut aggregator_client = MockAggregatorClient::new();
251 let certificate_hash = "cert-hash-123".to_string();
252 let set_proof = CardanoTransactionsSetProof::dummy();
253 let transactions_proofs = CardanoTransactionsProofs::new(
254 &certificate_hash,
255 vec![set_proof.clone()],
256 vec![],
257 BlockNumber(99999),
258 );
259 let expected_transactions_proofs = transactions_proofs.clone();
260 aggregator_client
261 .expect_get_content()
262 .return_once(move |_| Ok(serde_json::to_string(&transactions_proofs).unwrap()))
263 .times(1);
264
265 let cardano_tx_client = CardanoTransactionClient::new(Arc::new(aggregator_client));
266 let transactions_proofs = cardano_tx_client
267 .get_proofs(
268 &set_proof
269 .transactions_hashes
270 .iter()
271 .map(|h| h.as_str())
272 .collect::<Vec<_>>(),
273 )
274 .await
275 .unwrap();
276
277 assert_eq!(expected_transactions_proofs, transactions_proofs);
278 }
279
280 #[tokio::test]
281 async fn test_get_proof_ko() {
282 let mut aggregator_client = MockAggregatorClient::new();
283 aggregator_client
284 .expect_get_content()
285 .return_once(move |_| {
286 Err(AggregatorClientError::RemoteServerTechnical(anyhow!(
287 "an error"
288 )))
289 })
290 .times(1);
291
292 let cardano_tx_client = CardanoTransactionClient::new(Arc::new(aggregator_client));
293 cardano_tx_client
294 .get_proofs(&["tx-123"])
295 .await
296 .expect_err("The certificate client should fail here.");
297 }
298}