Skip to main content

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