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 crate::aggregator_client::{AggregatorClientError, MockAggregatorClient};
168 use crate::common::{BlockNumber, Epoch};
169 use crate::{
170 CardanoTransactionSnapshot, CardanoTransactionSnapshotListItem, CardanoTransactionsProofs,
171 CardanoTransactionsSetProof,
172 };
173
174 use super::*;
175
176 fn fake_messages() -> Vec<CardanoTransactionSnapshotListItem> {
177 vec![
178 CardanoTransactionSnapshotListItem {
179 merkle_root: "mk-123".to_string(),
180 epoch: Epoch(1),
181 block_number: BlockNumber(24),
182 hash: "hash-123".to_string(),
183 certificate_hash: "cert-hash-123".to_string(),
184 created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
185 .unwrap()
186 .with_timezone(&Utc),
187 },
188 CardanoTransactionSnapshotListItem {
189 merkle_root: "mk-456".to_string(),
190 epoch: Epoch(1),
191 block_number: BlockNumber(24),
192 hash: "hash-456".to_string(),
193 certificate_hash: "cert-hash-456".to_string(),
194 created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
195 .unwrap()
196 .with_timezone(&Utc),
197 },
198 ]
199 }
200
201 #[tokio::test]
202 async fn get_cardano_transactions_snapshot_list() {
203 let message = fake_messages();
204 let mut http_client = MockAggregatorClient::new();
205 http_client
206 .expect_get_content()
207 .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
208 let client = CardanoTransactionClient::new(Arc::new(http_client));
209 let items = client.list_snapshots().await.unwrap();
210
211 assert_eq!(2, items.len());
212 assert_eq!("hash-123".to_string(), items[0].hash);
213 assert_eq!("hash-456".to_string(), items[1].hash);
214 }
215
216 #[tokio::test]
217 async fn get_cardano_transactions_snapshot() {
218 let mut http_client = MockAggregatorClient::new();
219 let message = CardanoTransactionSnapshot {
220 merkle_root: "mk-123".to_string(),
221 epoch: Epoch(1),
222 block_number: BlockNumber(24),
223 hash: "hash-123".to_string(),
224 certificate_hash: "cert-hash-123".to_string(),
225 created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
226 .unwrap()
227 .with_timezone(&Utc),
228 };
229 let expected = message.clone();
230 http_client
231 .expect_get_content()
232 .with(eq(AggregatorRequest::GetCardanoTransactionSnapshot {
233 hash: "hash-123".to_string(),
234 }))
235 .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
236 let client = CardanoTransactionClient::new(Arc::new(http_client));
237 let cardano_transaction_snapshot = client
238 .get_snapshot("hash-123")
239 .await
240 .unwrap()
241 .expect("This test returns a cardano transaction snapshot");
242
243 assert_eq!(expected, cardano_transaction_snapshot);
244 }
245
246 #[tokio::test]
247 async fn test_get_proof_ok() {
248 let mut aggregator_client = MockAggregatorClient::new();
249 let certificate_hash = "cert-hash-123".to_string();
250 let set_proof = CardanoTransactionsSetProof::dummy();
251 let transactions_proofs = CardanoTransactionsProofs::new(
252 &certificate_hash,
253 vec![set_proof.clone()],
254 vec![],
255 BlockNumber(99999),
256 );
257 let expected_transactions_proofs = transactions_proofs.clone();
258 aggregator_client
259 .expect_get_content()
260 .return_once(move |_| Ok(serde_json::to_string(&transactions_proofs).unwrap()))
261 .times(1);
262
263 let cardano_tx_client = CardanoTransactionClient::new(Arc::new(aggregator_client));
264 let transactions_proofs = cardano_tx_client
265 .get_proofs(
266 &set_proof
267 .transactions_hashes
268 .iter()
269 .map(|h| h.as_str())
270 .collect::<Vec<_>>(),
271 )
272 .await
273 .unwrap();
274
275 assert_eq!(expected_transactions_proofs, transactions_proofs);
276 }
277
278 #[tokio::test]
279 async fn test_get_proof_ko() {
280 let mut aggregator_client = MockAggregatorClient::new();
281 aggregator_client
282 .expect_get_content()
283 .return_once(move |_| {
284 Err(AggregatorClientError::RemoteServerTechnical(anyhow!(
285 "an error"
286 )))
287 })
288 .times(1);
289
290 let cardano_tx_client = CardanoTransactionClient::new(Arc::new(aggregator_client));
291 cardano_tx_client
292 .get_proofs(&["tx-123"])
293 .await
294 .expect_err("The certificate client should fail here.");
295 }
296}