1use crate::entities::{
2 BlockNumber, CardanoTransactionsSetProof, ProtocolMessage, ProtocolMessagePartKey,
3 TransactionHash,
4};
5use crate::messages::CardanoTransactionsSetProofMessagePart;
6use crate::StdError;
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10#[cfg(target_family = "wasm")]
11use wasm_bindgen::prelude::*;
12
13#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
15#[cfg_attr(
16 target_family = "wasm",
17 wasm_bindgen(getter_with_clone, js_name = "CardanoTransactionsProofs")
18)]
19pub struct CardanoTransactionsProofsMessage {
20 pub certificate_hash: String,
22
23 pub certified_transactions: Vec<CardanoTransactionsSetProofMessagePart>,
25
26 pub non_certified_transactions: Vec<TransactionHash>,
28
29 pub latest_block_number: BlockNumber,
31}
32
33#[cfg_attr(
34 target_family = "wasm",
35 wasm_bindgen(js_class = "CardanoTransactionsProofs")
36)]
37impl CardanoTransactionsProofsMessage {
38 #[cfg_attr(target_family = "wasm", wasm_bindgen(getter))]
40 pub fn transactions_hashes(&self) -> Vec<TransactionHash> {
41 self.certified_transactions
42 .iter()
43 .flat_map(|ct| ct.transactions_hashes.clone())
44 .collect::<Vec<_>>()
45 }
46}
47
48#[derive(Debug, Clone, PartialEq)]
53pub struct VerifiedCardanoTransactions {
54 certificate_hash: String,
55 merkle_root: String,
56 certified_transactions: Vec<TransactionHash>,
57 latest_block_number: BlockNumber,
58}
59
60impl VerifiedCardanoTransactions {
61 pub fn certificate_hash(&self) -> &str {
63 &self.certificate_hash
64 }
65
66 pub fn certified_transactions(&self) -> &[TransactionHash] {
68 &self.certified_transactions
69 }
70
71 pub fn fill_protocol_message(&self, message: &mut ProtocolMessage) {
74 message.set_message_part(
75 ProtocolMessagePartKey::CardanoTransactionsMerkleRoot,
76 self.merkle_root.clone(),
77 );
78
79 message.set_message_part(
80 ProtocolMessagePartKey::LatestBlockNumber,
81 self.latest_block_number.to_string(),
82 );
83 }
84}
85
86#[derive(Error, Debug)]
88pub enum VerifyCardanoTransactionsProofsError {
89 #[error("Invalid set proof for transactions hashes: {transactions_hashes:?}")]
91 InvalidSetProof {
92 transactions_hashes: Vec<TransactionHash>,
94 source: StdError,
96 },
97
98 #[error("There's no certified transaction to verify")]
100 NoCertifiedTransaction,
101
102 #[error("All certified transactions set proofs must share the same Merkle root")]
107 NonMatchingMerkleRoot,
108
109 #[error("Malformed data or unknown Cardano Set Proof format")]
112 MalformedData(#[source] StdError),
113}
114
115impl CardanoTransactionsProofsMessage {
116 pub fn new(
118 certificate_hash: &str,
119 certified_transactions: Vec<CardanoTransactionsSetProofMessagePart>,
120 non_certified_transactions: Vec<TransactionHash>,
121 latest_block_number: BlockNumber,
122 ) -> Self {
123 Self {
124 certificate_hash: certificate_hash.to_string(),
125 certified_transactions,
126 non_certified_transactions,
127 latest_block_number,
128 }
129 }
130
131 pub fn verify(
143 &self,
144 ) -> Result<VerifiedCardanoTransactions, VerifyCardanoTransactionsProofsError> {
145 let mut merkle_root = None;
146
147 for certified_transaction in &self.certified_transactions {
148 let certified_transaction: CardanoTransactionsSetProof = certified_transaction
149 .clone()
150 .try_into()
151 .map_err(VerifyCardanoTransactionsProofsError::MalformedData)?;
152 certified_transaction.verify().map_err(|e| {
153 VerifyCardanoTransactionsProofsError::InvalidSetProof {
154 transactions_hashes: certified_transaction.transactions_hashes().to_vec(),
155 source: e,
156 }
157 })?;
158
159 let tx_merkle_root = Some(certified_transaction.merkle_root());
160
161 if merkle_root.is_none() {
162 merkle_root = tx_merkle_root;
163 } else if merkle_root != tx_merkle_root {
164 return Err(VerifyCardanoTransactionsProofsError::NonMatchingMerkleRoot);
165 }
166 }
167
168 Ok(VerifiedCardanoTransactions {
169 certificate_hash: self.certificate_hash.clone(),
170 merkle_root: merkle_root
171 .ok_or(VerifyCardanoTransactionsProofsError::NoCertifiedTransaction)?,
172 certified_transactions: self
173 .certified_transactions
174 .iter()
175 .flat_map(|c| c.transactions_hashes.clone())
176 .collect(),
177 latest_block_number: self.latest_block_number,
178 })
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use crate::crypto_helper::MKProof;
185
186 use super::*;
187
188 #[test]
189 fn verify_malformed_proofs_fail() {
190 let txs_proofs = CardanoTransactionsProofsMessage::new(
191 "whatever",
192 vec![CardanoTransactionsSetProofMessagePart {
193 transactions_hashes: vec![],
194 proof: "invalid".to_string(),
195 }],
196 vec![],
197 BlockNumber(99999),
198 );
199
200 let error = txs_proofs
201 .verify()
202 .expect_err("Malformed txs proofs should fail to verify itself");
203 assert!(
204 matches!(
205 error,
206 VerifyCardanoTransactionsProofsError::MalformedData(_)
207 ),
208 "Expected 'MalformedData' error but got '{error:?}'"
209 );
210 }
211
212 #[test]
213 fn verify_no_certified_transaction_fail() {
214 let txs_proofs =
215 CardanoTransactionsProofsMessage::new("whatever", vec![], vec![], BlockNumber(99999));
216
217 let error = txs_proofs
218 .verify()
219 .expect_err("Proofs without certified transactions should fail to verify itself");
220 assert!(
221 matches!(
222 error,
223 VerifyCardanoTransactionsProofsError::NoCertifiedTransaction
224 ),
225 "Expected 'NoCertifiedTransactions' error but got '{error:?}'"
226 );
227 }
228
229 #[test]
230 fn verify_valid_proofs() {
231 let set_proof = CardanoTransactionsSetProof::dummy();
232 let expected = VerifiedCardanoTransactions {
233 certificate_hash: "whatever".to_string(),
234 merkle_root: set_proof.merkle_root(),
235 certified_transactions: set_proof.transactions_hashes().to_vec(),
236 latest_block_number: BlockNumber(99999),
237 };
238 let txs_proofs = CardanoTransactionsProofsMessage::new(
239 "whatever",
240 vec![set_proof.try_into().unwrap()],
241 vec![],
242 BlockNumber(99999),
243 );
244
245 let verified_txs = txs_proofs
246 .verify()
247 .expect("Valid txs proofs should verify itself");
248
249 assert_eq!(expected, verified_txs);
250 }
251
252 #[test]
253 fn verify_invalid_proofs() {
254 let set_proof = CardanoTransactionsSetProof::new(
255 vec!["invalid1".to_string()],
256 MKProof::from_leaves(&["invalid2"]).unwrap(),
257 );
258 let txs_proofs = CardanoTransactionsProofsMessage::new(
259 "whatever",
260 vec![set_proof.try_into().unwrap()],
261 vec![],
262 BlockNumber(99999),
263 );
264
265 let error = txs_proofs
266 .verify()
267 .expect_err("Invalid txs proofs should fail to verify itself");
268
269 assert!(
270 matches!(
271 error,
272 VerifyCardanoTransactionsProofsError::InvalidSetProof { .. },
273 ),
274 "Expected 'InvalidSetProof' error but got '{error:?}'"
275 );
276 }
277
278 #[test]
279 fn verify_valid_proof_with_different_merkle_root_fail() {
280 let set_proofs = vec![
281 CardanoTransactionsSetProof::new(
282 vec!["tx-1".to_string()],
283 MKProof::from_leaves(&["tx-1"]).unwrap(),
284 ),
285 CardanoTransactionsSetProof::new(
286 vec!["tx-2".to_string()],
287 MKProof::from_leaves(&["tx-2"]).unwrap(),
288 ),
289 ];
290 let txs_proofs = CardanoTransactionsProofsMessage::new(
291 "whatever",
292 set_proofs
293 .into_iter()
294 .map(|p| p.try_into().unwrap())
295 .collect(),
296 vec![],
297 BlockNumber(99999),
298 );
299
300 let error = txs_proofs
301 .verify()
302 .expect_err("Txs proofs with non matching merkle root should fail to verify itself");
303
304 assert!(
305 matches!(
306 error,
307 VerifyCardanoTransactionsProofsError::NonMatchingMerkleRoot,
308 ),
309 "Expected 'NonMatchingMerkleRoot' error but got '{error:?}'"
310 );
311 }
312
313 #[cfg(feature = "fs")]
314 mod fs_only {
315 use crate::crypto_helper::{MKMap, MKMapNode, MKTreeStoreInMemory};
316 use crate::entities::{BlockNumber, BlockRange, CardanoTransaction, SlotNumber};
317 use crate::signable_builder::{
318 CardanoTransactionsSignableBuilder, MockBlockRangeRootRetriever,
319 MockTransactionsImporter, SignableBuilder,
320 };
321 use std::sync::Arc;
322
323 use super::*;
324
325 #[tokio::test]
326 async fn verify_hashes_from_verified_cardano_transaction_and_from_signable_builder_are_equals(
327 ) {
328 let transactions = vec![
329 CardanoTransaction::new(
330 "tx-hash-123",
331 BlockNumber(10),
332 SlotNumber(1),
333 "block_hash",
334 ),
335 CardanoTransaction::new(
336 "tx-hash-456",
337 BlockNumber(20),
338 SlotNumber(2),
339 "block_hash",
340 ),
341 ];
342
343 assert_eq!(
344 from_verified_cardano_transaction(&transactions, 99999).compute_hash(),
345 from_signable_builder(&transactions, BlockNumber(99999))
346 .await
347 .compute_hash()
348 );
349
350 assert_ne!(
351 from_verified_cardano_transaction(&transactions, 99999).compute_hash(),
352 from_signable_builder(&transactions, BlockNumber(123456))
353 .await
354 .compute_hash()
355 );
356 }
357
358 fn from_verified_cardano_transaction(
359 transactions: &[CardanoTransaction],
360 block_number: u64,
361 ) -> ProtocolMessage {
362 let set_proof = CardanoTransactionsSetProof::from_leaves::<MKTreeStoreInMemory>(
363 transactions
364 .iter()
365 .map(|t| (t.block_number, t.transaction_hash.clone()))
366 .collect::<Vec<_>>()
367 .as_slice(),
368 )
369 .unwrap();
370
371 let verified_transactions_fake = VerifiedCardanoTransactions {
372 certificate_hash: "whatever".to_string(),
373 merkle_root: set_proof.merkle_root(),
374 certified_transactions: set_proof.transactions_hashes().to_vec(),
375 latest_block_number: BlockNumber(block_number),
376 };
377
378 let mut message = ProtocolMessage::new();
379 verified_transactions_fake.fill_protocol_message(&mut message);
380
381 message
382 }
383
384 async fn from_signable_builder(
385 transactions: &[CardanoTransaction],
386 block_number: BlockNumber,
387 ) -> ProtocolMessage {
388 let mut transaction_importer = MockTransactionsImporter::new();
389 transaction_importer
390 .expect_import()
391 .return_once(move |_| Ok(()));
392 let mut block_range_root_retriever = MockBlockRangeRootRetriever::new();
393
394 let transactions_imported = transactions.to_vec();
395 block_range_root_retriever
396 .expect_compute_merkle_map_from_block_range_roots()
397 .return_once(move |_| {
398 MKMap::<
399 BlockRange,
400 MKMapNode<BlockRange, MKTreeStoreInMemory>,
401 MKTreeStoreInMemory,
402 >::new_from_iter(transactions_imported.into_iter().map(
403 |tx| {
404 (
405 BlockRange::from_block_number(tx.block_number),
406 MKMapNode::TreeNode(tx.transaction_hash.clone().into()),
407 )
408 },
409 ))
410 });
411 let cardano_transaction_signable_builder = CardanoTransactionsSignableBuilder::new(
412 Arc::new(transaction_importer),
413 Arc::new(block_range_root_retriever),
414 );
415 cardano_transaction_signable_builder
416 .compute_protocol_message(block_number)
417 .await
418 .unwrap()
419 }
420 }
421}