fedimint_core/
txoproof.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use std::borrow::Cow;
use std::hash::Hash;
use std::io::Cursor;

use bitcoin::block::Header as BlockHeader;
use bitcoin::merkle_tree::PartialMerkleTree;
use bitcoin::{BlockHash, Txid};
use hex::{FromHex, ToHex};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::encoding::{Decodable, DecodeError, Encodable};
use crate::module::registry::ModuleDecoderRegistry;

#[derive(Clone, Debug)]
pub struct TxOutProof {
    pub block_header: BlockHeader,
    pub merkle_proof: PartialMerkleTree,
}

impl TxOutProof {
    pub fn block(&self) -> BlockHash {
        self.block_header.block_hash()
    }

    pub fn contains_tx(&self, tx_id: Txid) -> bool {
        let mut transactions = Vec::new();
        let mut indices = Vec::new();
        let root = self
            .merkle_proof
            .extract_matches(&mut transactions, &mut indices)
            .expect("Checked at construction time");

        debug_assert_eq!(root, self.block_header.merkle_root);

        transactions.contains(&tx_id)
    }
}

impl Decodable for TxOutProof {
    fn consensus_decode<D: std::io::Read>(
        d: &mut D,
        modules: &ModuleDecoderRegistry,
    ) -> Result<Self, DecodeError> {
        let block_header = BlockHeader::consensus_decode(d, modules)?;
        let merkle_proof = PartialMerkleTree::consensus_decode(d, modules)?;

        let mut transactions = Vec::new();
        let mut indices = Vec::new();
        let root = merkle_proof
            .extract_matches(&mut transactions, &mut indices)
            .map_err(|_| DecodeError::from_str("Invalid partial merkle tree"))?;

        if block_header.merkle_root == root {
            Ok(Self {
                block_header,
                merkle_proof,
            })
        } else {
            Err(DecodeError::from_str(
                "Partial merkle tree does not belong to block header",
            ))
        }
    }
}

impl Encodable for TxOutProof {
    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
        let mut written = 0;

        written += self.block_header.consensus_encode(writer)?;
        written += self.merkle_proof.consensus_encode(writer)?;

        Ok(written)
    }
}

impl Serialize for TxOutProof {
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
    where
        S: Serializer,
    {
        let mut bytes = Vec::new();
        self.consensus_encode(&mut bytes).unwrap();

        if serializer.is_human_readable() {
            serializer.serialize_str(&bytes.encode_hex::<String>())
        } else {
            serializer.serialize_bytes(&bytes)
        }
    }
}

impl<'de> Deserialize<'de> for TxOutProof {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let empty_module_registry = ModuleDecoderRegistry::default();
        if deserializer.is_human_readable() {
            let hex_str: Cow<str> = Deserialize::deserialize(deserializer)?;
            let bytes = Vec::from_hex(hex_str.as_ref()).map_err(D::Error::custom)?;
            Ok(
                Self::consensus_decode(&mut Cursor::new(bytes), &empty_module_registry)
                    .map_err(D::Error::custom)?,
            )
        } else {
            let bytes: &[u8] = Deserialize::deserialize(deserializer)?;
            Ok(
                Self::consensus_decode(&mut Cursor::new(bytes), &empty_module_registry)
                    .map_err(D::Error::custom)?,
            )
        }
    }
}

// TODO: upstream
impl Hash for TxOutProof {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        let mut bytes = Vec::new();
        self.consensus_encode(&mut bytes).unwrap();
        state.write(&bytes);
    }
}

impl PartialEq for TxOutProof {
    fn eq(&self, other: &Self) -> bool {
        self.block_header == other.block_header && self.merkle_proof == other.merkle_proof
    }
}

impl Eq for TxOutProof {}