/ fedimint-core / src / txoproof.rs
txoproof.rs
  1  use std::borrow::Cow;
  2  use std::hash::Hash;
  3  use std::io::Cursor;
  4  
  5  use bitcoin::block::Header as BlockHeader;
  6  use bitcoin::merkle_tree::PartialMerkleTree;
  7  use bitcoin::{BlockHash, Txid};
  8  use hex::{FromHex, ToHex};
  9  use serde::de::Error;
 10  use serde::{Deserialize, Deserializer, Serialize, Serializer};
 11  
 12  use crate::encoding::{Decodable, DecodeError, Encodable};
 13  use crate::module::registry::ModuleDecoderRegistry;
 14  
 15  #[derive(Clone, Debug)]
 16  pub struct TxOutProof {
 17      pub block_header: BlockHeader,
 18      pub merkle_proof: PartialMerkleTree,
 19  }
 20  
 21  impl 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  
 40  impl 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              Err(DecodeError::from_str(
 56                  "Partial merkle tree does not belong to block header",
 57              ))
 58          } else {
 59              Ok(TxOutProof {
 60                  block_header,
 61                  merkle_proof,
 62              })
 63          }
 64      }
 65  }
 66  
 67  impl 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  
 78  impl 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  
 94  impl<'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                  TxOutProof::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                  TxOutProof::consensus_decode(&mut Cursor::new(bytes), &empty_module_registry)
111                      .map_err(D::Error::custom)?,
112              )
113          }
114      }
115  }
116  
117  // TODO: upstream
118  impl 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  
126  impl PartialEq for TxOutProof {
127      fn eq(&self, other: &TxOutProof) -> bool {
128          self.block_header == other.block_header && self.merkle_proof == other.merkle_proof
129      }
130  }
131  
132  impl Eq for TxOutProof {}