Skip to main content

blvm_protocol/
bip157.rs

1//! BIP157: Client-Side Block Filtering Network Protocol
2//!
3//! Specification: https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki
4//!
5//! Defines network messages for requesting and serving compact block filters.
6//! Enables efficient transaction discovery for light clients.
7
8use super::bip158::CompactBlockFilter;
9use crate::Hash;
10use sha2::{Digest, Sha256};
11
12/// Filter header - commits to previous filter header and current filter
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct FilterHeader {
15    /// Filter hash (double SHA256 of filter data)
16    pub filter_hash: Hash,
17    /// Previous filter header hash
18    pub prev_header_hash: Hash,
19}
20
21impl FilterHeader {
22    /// Calculate filter header from filter and previous header
23    pub fn new(filter: &CompactBlockFilter, prev_header: Option<&FilterHeader>) -> Self {
24        // Filter hash = SHA256(SHA256(filter_data))
25        let mut hasher = Sha256::new();
26        hasher.update(&filter.filter_data);
27        let first_hash = hasher.finalize();
28
29        let mut hasher2 = Sha256::new();
30        hasher2.update(first_hash);
31        let filter_hash_bytes = hasher2.finalize();
32
33        let mut filter_hash = [0u8; 32];
34        filter_hash.copy_from_slice(&filter_hash_bytes);
35
36        // Previous header hash
37        let prev_header_hash = if let Some(prev) = prev_header {
38            // Header hash = SHA256(SHA256(filter_hash || prev_header_hash))
39            let mut combined = Vec::new();
40            combined.extend_from_slice(&prev.filter_hash);
41            combined.extend_from_slice(&prev.prev_header_hash);
42
43            let mut hasher = Sha256::new();
44            hasher.update(&combined);
45            let first_hash = hasher.finalize();
46
47            let mut hasher2 = Sha256::new();
48            hasher2.update(first_hash);
49            let header_hash_bytes = hasher2.finalize();
50
51            let mut header_hash = [0u8; 32];
52            header_hash.copy_from_slice(&header_hash_bytes);
53            header_hash
54        } else {
55            // Genesis filter header (all zeros or block hash)
56            [0u8; 32]
57        };
58
59        FilterHeader {
60            filter_hash,
61            prev_header_hash,
62        }
63    }
64
65    /// Calculate header hash (double SHA256 of filter_hash || prev_header_hash)
66    pub fn header_hash(&self) -> Hash {
67        let mut combined = Vec::new();
68        combined.extend_from_slice(&self.filter_hash);
69        combined.extend_from_slice(&self.prev_header_hash);
70
71        let mut hasher = Sha256::new();
72        hasher.update(&combined);
73        let first_hash = hasher.finalize();
74
75        let mut hasher2 = Sha256::new();
76        hasher2.update(first_hash);
77        let hash_bytes = hasher2.finalize();
78
79        let mut header_hash = [0u8; 32];
80        header_hash.copy_from_slice(&hash_bytes);
81        header_hash
82    }
83}
84
85/// Filter type (currently only Basic Compact Filters)
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum FilterType {
88    /// Basic compact filters (BIP158)
89    Basic = 0,
90}
91
92impl FilterType {
93    pub fn from_u8(value: u8) -> Option<Self> {
94        match value {
95            0 => Some(FilterType::Basic),
96            _ => None,
97        }
98    }
99
100    pub fn to_u8(self) -> u8 {
101        self as u8
102    }
103}
104
105/// getcfilters message - request filters for block range
106#[derive(Debug, Clone)]
107pub struct GetCfilters {
108    /// Filter type
109    pub filter_type: FilterType,
110    /// Start block height
111    pub start_height: u32,
112    /// Stop block hash
113    pub stop_hash: Hash,
114}
115
116/// cfilter message - compact block filter response
117#[derive(Debug, Clone)]
118pub struct CFilter {
119    /// Filter type
120    pub filter_type: FilterType,
121    /// Block hash
122    pub block_hash: Hash,
123    /// Compact block filter
124    pub filter: CompactBlockFilter,
125}
126
127/// getcfheaders message - request filter headers
128#[derive(Debug, Clone)]
129pub struct GetCfheaders {
130    /// Filter type
131    pub filter_type: FilterType,
132    /// Start block height
133    pub start_height: u32,
134    /// Stop block hash
135    pub stop_hash: Hash,
136}
137
138/// cfheaders message - filter headers response
139#[derive(Debug, Clone)]
140pub struct Cfheaders {
141    /// Filter type
142    pub filter_type: FilterType,
143    /// Stop block hash
144    pub stop_hash: Hash,
145    /// Previous filter header
146    pub prev_header: FilterHeader,
147    /// Filter headers (one per block in range)
148    pub filter_headers: Vec<Hash>,
149}
150
151/// getcfcheckpt message - request filter checkpoints
152#[derive(Debug, Clone)]
153pub struct GetCfcheckpt {
154    /// Filter type
155    pub filter_type: FilterType,
156    /// Stop block hash
157    pub stop_hash: Hash,
158}
159
160/// cfcheckpt message - filter checkpoint response
161#[derive(Debug, Clone)]
162pub struct Cfcheckpt {
163    /// Filter type
164    pub filter_type: FilterType,
165    /// Stop block hash
166    pub stop_hash: Hash,
167    /// Filter header hashes at checkpoint intervals
168    pub filter_header_hashes: Vec<Hash>,
169}
170
171/// BIP157 service flag bit
172pub const NODE_COMPACT_FILTERS: u64 = 1 << 6;
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177
178    #[test]
179    fn test_filter_header() {
180        let empty_filter = CompactBlockFilter {
181            filter_data: vec![1, 2, 3],
182            num_elements: 0,
183        };
184
185        let header1 = FilterHeader::new(&empty_filter, None);
186        let header2 = FilterHeader::new(&empty_filter, Some(&header1));
187
188        // Filter hashes should be the same (same filter data)
189        assert_eq!(header1.filter_hash, header2.filter_hash);
190        // Header hashes should be different when chained
191        assert_ne!(header1.header_hash(), header2.header_hash());
192        assert_eq!(header2.prev_header_hash, header1.header_hash());
193    }
194}