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 '{:?}'",
209 error
210 );
211 }
212
213 #[test]
214 fn verify_no_certified_transaction_fail() {
215 let txs_proofs =
216 CardanoTransactionsProofsMessage::new("whatever", vec![], vec![], BlockNumber(99999));
217
218 let error = txs_proofs
219 .verify()
220 .expect_err("Proofs without certified transactions should fail to verify itself");
221 assert!(
222 matches!(
223 error,
224 VerifyCardanoTransactionsProofsError::NoCertifiedTransaction
225 ),
226 "Expected 'NoCertifiedTransactions' error but got '{:?}'",
227 error
228 );
229 }
230
231 #[test]
232 fn verify_valid_proofs() {
233 let set_proof = CardanoTransactionsSetProof::dummy();
234 let expected = VerifiedCardanoTransactions {
235 certificate_hash: "whatever".to_string(),
236 merkle_root: set_proof.merkle_root(),
237 certified_transactions: set_proof.transactions_hashes().to_vec(),
238 latest_block_number: BlockNumber(99999),
239 };
240 let txs_proofs = CardanoTransactionsProofsMessage::new(
241 "whatever",
242 vec![set_proof.try_into().unwrap()],
243 vec![],
244 BlockNumber(99999),
245 );
246
247 let verified_txs = txs_proofs
248 .verify()
249 .expect("Valid txs proofs should verify itself");
250
251 assert_eq!(expected, verified_txs);
252 }
253
254 #[test]
255 fn verify_invalid_proofs() {
256 let set_proof = CardanoTransactionsSetProof::new(
257 vec!["invalid1".to_string()],
258 MKProof::from_leaves(&["invalid2"]).unwrap(),
259 );
260 let txs_proofs = CardanoTransactionsProofsMessage::new(
261 "whatever",
262 vec![set_proof.try_into().unwrap()],
263 vec![],
264 BlockNumber(99999),
265 );
266
267 let error = txs_proofs
268 .verify()
269 .expect_err("Invalid txs proofs should fail to verify itself");
270
271 assert!(
272 matches!(
273 error,
274 VerifyCardanoTransactionsProofsError::InvalidSetProof { .. },
275 ),
276 "Expected 'InvalidSetProof' error but got '{:?}'",
277 error
278 );
279 }
280
281 #[test]
282 fn verify_valid_proof_with_different_merkle_root_fail() {
283 let set_proofs = vec![
284 CardanoTransactionsSetProof::new(
285 vec!["tx-1".to_string()],
286 MKProof::from_leaves(&["tx-1"]).unwrap(),
287 ),
288 CardanoTransactionsSetProof::new(
289 vec!["tx-2".to_string()],
290 MKProof::from_leaves(&["tx-2"]).unwrap(),
291 ),
292 ];
293 let txs_proofs = CardanoTransactionsProofsMessage::new(
294 "whatever",
295 set_proofs
296 .into_iter()
297 .map(|p| p.try_into().unwrap())
298 .collect(),
299 vec![],
300 BlockNumber(99999),
301 );
302
303 let error = txs_proofs
304 .verify()
305 .expect_err("Txs proofs with non matching merkle root should fail to verify itself");
306
307 assert!(
308 matches!(
309 error,
310 VerifyCardanoTransactionsProofsError::NonMatchingMerkleRoot,
311 ),
312 "Expected 'NonMatchingMerkleRoot' error but got '{:?}'",
313 error
314 );
315 }
316
317 #[cfg(feature = "fs")]
318 mod fs_only {
319 use crate::crypto_helper::{MKMap, MKMapNode, MKTreeStoreInMemory};
320 use crate::entities::{BlockNumber, BlockRange, CardanoTransaction, SlotNumber};
321 use crate::signable_builder::{
322 CardanoTransactionsSignableBuilder, MockBlockRangeRootRetriever,
323 MockTransactionsImporter, SignableBuilder,
324 };
325 use std::sync::Arc;
326
327 use super::*;
328
329 #[tokio::test]
330 async fn verify_hashes_from_verified_cardano_transaction_and_from_signable_builder_are_equals(
331 ) {
332 let transactions = vec![
333 CardanoTransaction::new(
334 "tx-hash-123",
335 BlockNumber(10),
336 SlotNumber(1),
337 "block_hash",
338 ),
339 CardanoTransaction::new(
340 "tx-hash-456",
341 BlockNumber(20),
342 SlotNumber(2),
343 "block_hash",
344 ),
345 ];
346
347 assert_eq!(
348 from_verified_cardano_transaction(&transactions, 99999).compute_hash(),
349 from_signable_builder(&transactions, BlockNumber(99999))
350 .await
351 .compute_hash()
352 );
353
354 assert_ne!(
355 from_verified_cardano_transaction(&transactions, 99999).compute_hash(),
356 from_signable_builder(&transactions, BlockNumber(123456))
357 .await
358 .compute_hash()
359 );
360 }
361
362 fn from_verified_cardano_transaction(
363 transactions: &[CardanoTransaction],
364 block_number: u64,
365 ) -> ProtocolMessage {
366 let set_proof = CardanoTransactionsSetProof::from_leaves::<MKTreeStoreInMemory>(
367 transactions
368 .iter()
369 .map(|t| (t.block_number, t.transaction_hash.clone()))
370 .collect::<Vec<_>>()
371 .as_slice(),
372 )
373 .unwrap();
374
375 let verified_transactions_fake = VerifiedCardanoTransactions {
376 certificate_hash: "whatever".to_string(),
377 merkle_root: set_proof.merkle_root(),
378 certified_transactions: set_proof.transactions_hashes().to_vec(),
379 latest_block_number: BlockNumber(block_number),
380 };
381
382 let mut message = ProtocolMessage::new();
383 verified_transactions_fake.fill_protocol_message(&mut message);
384
385 message
386 }
387
388 async fn from_signable_builder(
389 transactions: &[CardanoTransaction],
390 block_number: BlockNumber,
391 ) -> ProtocolMessage {
392 let mut transaction_importer = MockTransactionsImporter::new();
393 transaction_importer
394 .expect_import()
395 .return_once(move |_| Ok(()));
396 let mut block_range_root_retriever = MockBlockRangeRootRetriever::new();
397
398 let transactions_imported = transactions.to_vec();
399 block_range_root_retriever
400 .expect_compute_merkle_map_from_block_range_roots()
401 .return_once(move |_| {
402 MKMap::<
403 BlockRange,
404 MKMapNode<BlockRange, MKTreeStoreInMemory>,
405 MKTreeStoreInMemory,
406 >::new_from_iter(transactions_imported.into_iter().map(
407 |tx| {
408 (
409 BlockRange::from_block_number(tx.block_number),
410 MKMapNode::TreeNode(tx.transaction_hash.clone().into()),
411 )
412 },
413 ))
414 });
415 let cardano_transaction_signable_builder = CardanoTransactionsSignableBuilder::new(
416 Arc::new(transaction_importer),
417 Arc::new(block_range_root_retriever),
418 );
419 cardano_transaction_signable_builder
420 .compute_protocol_message(block_number)
421 .await
422 .unwrap()
423 }
424 }
425}