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