fedimint_core/
txoproof.rs

1use 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;
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_partial<D: std::io::Read>(
42        d: &mut D,
43        modules: &ModuleDecoderRegistry,
44    ) -> Result<Self, DecodeError> {
45        let block_header = BlockHeader::consensus_decode_partial(d, modules)?;
46        let merkle_proof = PartialMerkleTree::consensus_decode_partial(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        if serializer.is_human_readable() {
84            serializer.serialize_str(&self.consensus_encode_to_hex())
85        } else {
86            serializer.serialize_bytes(&self.consensus_encode_to_vec())
87        }
88    }
89}
90
91impl<'de> Deserialize<'de> for TxOutProof {
92    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
93    where
94        D: Deserializer<'de>,
95    {
96        let empty_module_registry = ModuleDecoderRegistry::default();
97        if deserializer.is_human_readable() {
98            let hex_str: Cow<str> = Deserialize::deserialize(deserializer)?;
99            let bytes = Vec::from_hex(hex_str.as_ref()).map_err(D::Error::custom)?;
100            Ok(
101                Self::consensus_decode_partial(&mut Cursor::new(bytes), &empty_module_registry)
102                    .map_err(D::Error::custom)?,
103            )
104        } else {
105            let bytes: &[u8] = Deserialize::deserialize(deserializer)?;
106            Ok(
107                Self::consensus_decode_partial(&mut Cursor::new(bytes), &empty_module_registry)
108                    .map_err(D::Error::custom)?,
109            )
110        }
111    }
112}
113
114// TODO: upstream
115impl Hash for TxOutProof {
116    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
117        state.write(&self.consensus_encode_to_vec());
118    }
119}
120
121impl PartialEq for TxOutProof {
122    fn eq(&self, other: &Self) -> bool {
123        self.block_header == other.block_header && self.merkle_proof == other.merkle_proof
124    }
125}
126
127impl Eq for TxOutProof {}