bdk_bitcoind_rpc/
bip158.rs1use bdk_core::bitcoin;
10use bdk_core::{BlockId, CheckPoint};
11use bitcoin::{bip158::BlockFilter, Block, ScriptBuf};
12use bitcoincore_rpc;
13use bitcoincore_rpc::{json::GetBlockHeaderResult, RpcApi};
14
15#[derive(Debug)]
31pub struct FilterIter<'a> {
32 client: &'a bitcoincore_rpc::Client,
34 spks: Vec<ScriptBuf>,
36 cp: CheckPoint,
38 header: Option<GetBlockHeaderResult>,
40}
41
42impl<'a> FilterIter<'a> {
43 pub fn new(
45 client: &'a bitcoincore_rpc::Client,
46 cp: CheckPoint,
47 spks: impl IntoIterator<Item = ScriptBuf>,
48 ) -> Self {
49 Self {
50 client,
51 spks: spks.into_iter().collect(),
52 cp,
53 header: None,
54 }
55 }
56
57 fn find_base(&self) -> Result<GetBlockHeaderResult, Error> {
61 for cp in self.cp.iter() {
62 match self.client.get_block_header_info(&cp.hash()) {
63 Err(e) if is_not_found(&e) => continue,
64 Ok(header) if header.confirmations <= 0 => continue,
65 Ok(header) => return Ok(header),
66 Err(e) => return Err(Error::Rpc(e)),
67 }
68 }
69 Err(Error::ReorgDepthExceeded)
70 }
71}
72
73#[derive(Debug, Clone)]
75pub struct Event {
76 pub cp: CheckPoint,
78 pub block: Option<Block>,
80}
81
82impl Event {
83 pub fn is_match(&self) -> bool {
85 self.block.is_some()
86 }
87
88 pub fn height(&self) -> u32 {
90 self.cp.height()
91 }
92}
93
94impl Iterator for FilterIter<'_> {
95 type Item = Result<Event, Error>;
96
97 fn next(&mut self) -> Option<Self::Item> {
98 (|| -> Result<Option<_>, Error> {
99 let mut cp = self.cp.clone();
100
101 let header = match self.header.take() {
102 Some(header) => header,
103 None => self.find_base()?,
106 };
107
108 let mut next_hash = match header.next_block_hash {
109 Some(hash) => hash,
110 None => return Ok(None),
111 };
112
113 let mut next_header = self.client.get_block_header_info(&next_hash)?;
114
115 while next_header.confirmations < 0 {
118 let prev_hash = next_header
119 .previous_block_hash
120 .ok_or(Error::ReorgDepthExceeded)?;
121 let prev_header = self.client.get_block_header_info(&prev_hash)?;
122 next_header = prev_header;
123 }
124
125 next_hash = next_header.hash;
126 let next_height: u32 = next_header.height.try_into()?;
127
128 cp = cp.insert(BlockId {
129 height: next_height,
130 hash: next_hash,
131 });
132
133 let mut block = None;
134 let filter =
135 BlockFilter::new(self.client.get_block_filter(&next_hash)?.filter.as_slice());
136 if filter
137 .match_any(&next_hash, self.spks.iter().map(ScriptBuf::as_ref))
138 .map_err(Error::Bip158)?
139 {
140 block = Some(self.client.get_block(&next_hash)?);
141 }
142
143 self.header = Some(next_header);
145 self.cp = cp.clone();
147
148 Ok(Some(Event { cp, block }))
149 })()
150 .transpose()
151 }
152}
153
154#[derive(Debug)]
156pub enum Error {
157 Rpc(bitcoincore_rpc::Error),
159 Bip158(bitcoin::bip158::Error),
161 ReorgDepthExceeded,
163 TryFromInt(core::num::TryFromIntError),
165}
166
167impl core::fmt::Display for Error {
168 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 match self {
170 Self::Rpc(e) => write!(f, "{e}"),
171 Self::Bip158(e) => write!(f, "{e}"),
172 Self::ReorgDepthExceeded => write!(f, "maximum reorg depth exceeded"),
173 Self::TryFromInt(e) => write!(f, "{e}"),
174 }
175 }
176}
177
178#[cfg(feature = "std")]
179impl std::error::Error for Error {}
180
181impl From<bitcoincore_rpc::Error> for Error {
182 fn from(e: bitcoincore_rpc::Error) -> Self {
183 Self::Rpc(e)
184 }
185}
186
187impl From<core::num::TryFromIntError> for Error {
188 fn from(e: core::num::TryFromIntError) -> Self {
189 Self::TryFromInt(e)
190 }
191}
192
193fn is_not_found(e: &bitcoincore_rpc::Error) -> bool {
195 matches!(
196 e,
197 bitcoincore_rpc::Error::JsonRpc(bitcoincore_rpc::jsonrpc::Error::Rpc(e))
198 if e.code == -5
199 )
200}