fedimint_core/
txoproof.rs1use std::borrow::Cow;
2use std::hash::Hash;
3use std::io::Cursor;
4
5use bitcoin::block::Header as BlockHeader;
6use bitcoin::merkle_tree::PartialMerkleTree;
7use bitcoin::{BlockHash, Txid};
8use hex::{FromHex, ToHex};
9use serde::de::Error;
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11
12use crate::encoding::{Decodable, DecodeError, Encodable};
13use crate::module::registry::ModuleDecoderRegistry;
14
15#[derive(Clone, Debug)]
16pub struct TxOutProof {
17 pub block_header: BlockHeader,
18 pub merkle_proof: PartialMerkleTree,
19}
20
21impl TxOutProof {
22 pub fn block(&self) -> BlockHash {
23 self.block_header.block_hash()
24 }
25
26 pub fn contains_tx(&self, tx_id: Txid) -> bool {
27 let mut transactions = Vec::new();
28 let mut indices = Vec::new();
29 let root = self
30 .merkle_proof
31 .extract_matches(&mut transactions, &mut indices)
32 .expect("Checked at construction time");
33
34 debug_assert_eq!(root, self.block_header.merkle_root);
35
36 transactions.contains(&tx_id)
37 }
38}
39
40impl Decodable for TxOutProof {
41 fn consensus_decode<D: std::io::Read>(
42 d: &mut D,
43 modules: &ModuleDecoderRegistry,
44 ) -> Result<Self, DecodeError> {
45 let block_header = BlockHeader::consensus_decode(d, modules)?;
46 let merkle_proof = PartialMerkleTree::consensus_decode(d, modules)?;
47
48 let mut transactions = Vec::new();
49 let mut indices = Vec::new();
50 let root = merkle_proof
51 .extract_matches(&mut transactions, &mut indices)
52 .map_err(|_| DecodeError::from_str("Invalid partial merkle tree"))?;
53
54 if block_header.merkle_root == root {
55 Ok(Self {
56 block_header,
57 merkle_proof,
58 })
59 } else {
60 Err(DecodeError::from_str(
61 "Partial merkle tree does not belong to block header",
62 ))
63 }
64 }
65}
66
67impl Encodable for TxOutProof {
68 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
69 let mut written = 0;
70
71 written += self.block_header.consensus_encode(writer)?;
72 written += self.merkle_proof.consensus_encode(writer)?;
73
74 Ok(written)
75 }
76}
77
78impl Serialize for TxOutProof {
79 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
80 where
81 S: Serializer,
82 {
83 let mut bytes = Vec::new();
84 self.consensus_encode(&mut bytes).unwrap();
85
86 if serializer.is_human_readable() {
87 serializer.serialize_str(&bytes.encode_hex::<String>())
88 } else {
89 serializer.serialize_bytes(&bytes)
90 }
91 }
92}
93
94impl<'de> Deserialize<'de> for TxOutProof {
95 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
96 where
97 D: Deserializer<'de>,
98 {
99 let empty_module_registry = ModuleDecoderRegistry::default();
100 if deserializer.is_human_readable() {
101 let hex_str: Cow<str> = Deserialize::deserialize(deserializer)?;
102 let bytes = Vec::from_hex(hex_str.as_ref()).map_err(D::Error::custom)?;
103 Ok(
104 Self::consensus_decode(&mut Cursor::new(bytes), &empty_module_registry)
105 .map_err(D::Error::custom)?,
106 )
107 } else {
108 let bytes: &[u8] = Deserialize::deserialize(deserializer)?;
109 Ok(
110 Self::consensus_decode(&mut Cursor::new(bytes), &empty_module_registry)
111 .map_err(D::Error::custom)?,
112 )
113 }
114 }
115}
116
117impl Hash for TxOutProof {
119 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
120 let mut bytes = Vec::new();
121 self.consensus_encode(&mut bytes).unwrap();
122 state.write(&bytes);
123 }
124}
125
126impl PartialEq for TxOutProof {
127 fn eq(&self, other: &Self) -> bool {
128 self.block_header == other.block_header && self.merkle_proof == other.merkle_proof
129 }
130}
131
132impl Eq for TxOutProof {}