bitcoinkernel/notifications/
validation.rs1use std::ffi::c_void;
2
3use libbitcoinkernel_sys::{btck_Block, btck_BlockTreeEntry, btck_BlockValidationState};
4
5use crate::{
6 ffi::sealed::{FromMutPtr, FromPtr},
7 Block, BlockTreeEntry, BlockValidationStateRef,
8};
9
10pub trait BlockCheckedCallback: Send + Sync {
12 fn on_block_checked(&self, block: Block, state: BlockValidationStateRef);
13}
14
15impl<F> BlockCheckedCallback for F
16where
17 F: Fn(Block, BlockValidationStateRef) + Send + Sync + 'static,
18{
19 fn on_block_checked(&self, block: Block, state: BlockValidationStateRef) {
20 self(block, state)
21 }
22}
23
24pub trait NewPoWValidBlockCallback: Send + Sync {
26 fn on_new_pow_valid_block<'a>(&self, block: Block, entry: BlockTreeEntry<'a>);
27}
28
29impl<F> NewPoWValidBlockCallback for F
30where
31 F: for<'a> Fn(BlockTreeEntry<'a>, Block) + Send + Sync + 'static,
32{
33 fn on_new_pow_valid_block<'a>(&self, block: Block, entry: BlockTreeEntry<'a>) {
34 self(entry, block)
35 }
36}
37
38pub trait BlockConnectedCallback: Send + Sync {
40 fn on_block_connected<'a>(&self, block: Block, entry: BlockTreeEntry<'a>);
41}
42
43impl<F> BlockConnectedCallback for F
44where
45 F: for<'a> Fn(Block, BlockTreeEntry<'a>) + Send + Sync + 'static,
46{
47 fn on_block_connected<'a>(&self, block: Block, entry: BlockTreeEntry<'a>) {
48 self(block, entry)
49 }
50}
51
52pub trait BlockDisconnectedCallback: Send + Sync {
54 fn on_block_disconnected<'a>(&self, block: Block, entry: BlockTreeEntry<'a>);
55}
56
57impl<F> BlockDisconnectedCallback for F
58where
59 F: for<'a> Fn(Block, BlockTreeEntry<'a>) + Send + Sync + 'static,
60{
61 fn on_block_disconnected<'a>(&self, block: Block, entry: BlockTreeEntry<'a>) {
62 self(block, entry)
63 }
64}
65
66#[derive(Default)]
68pub struct ValidationCallbackRegistry {
69 block_checked_handler: Option<Box<dyn BlockCheckedCallback>>,
70 new_pow_valid_block_handler: Option<Box<dyn NewPoWValidBlockCallback>>,
71 block_connected_handler: Option<Box<dyn BlockConnectedCallback>>,
72 block_disconnected_handler: Option<Box<dyn BlockDisconnectedCallback>>,
73}
74
75impl ValidationCallbackRegistry {
76 pub fn new() -> Self {
77 Self::default()
78 }
79
80 pub fn register_block_checked<T>(&mut self, handler: T) -> &mut Self
81 where
82 T: BlockCheckedCallback + 'static,
83 {
84 self.block_checked_handler = Some(Box::new(handler) as Box<dyn BlockCheckedCallback>);
85 self
86 }
87
88 pub fn register_new_pow_valid_block<T>(&mut self, handler: T) -> &mut Self
89 where
90 T: NewPoWValidBlockCallback + 'static,
91 {
92 self.new_pow_valid_block_handler =
93 Some(Box::new(handler) as Box<dyn NewPoWValidBlockCallback>);
94 self
95 }
96
97 pub fn register_block_connected<T>(&mut self, handler: T) -> &mut Self
98 where
99 T: BlockConnectedCallback + 'static,
100 {
101 self.block_connected_handler = Some(Box::new(handler) as Box<dyn BlockConnectedCallback>);
102 self
103 }
104
105 pub fn register_block_disconnected<T>(&mut self, handler: T) -> &mut Self
106 where
107 T: BlockDisconnectedCallback + 'static,
108 {
109 self.block_disconnected_handler =
110 Some(Box::new(handler) as Box<dyn BlockDisconnectedCallback>);
111 self
112 }
113}
114
115pub(crate) unsafe extern "C" fn validation_user_data_destroy_wrapper(user_data: *mut c_void) {
116 if !user_data.is_null() {
117 let _ = Box::from_raw(user_data as *mut ValidationCallbackRegistry);
118 }
119}
120
121pub(crate) unsafe extern "C" fn validation_block_checked_wrapper(
122 user_data: *mut c_void,
123 block: *mut btck_Block,
124 state: *const btck_BlockValidationState,
125) {
126 let block = Block::from_ptr(block);
127 let registry = &*(user_data as *mut ValidationCallbackRegistry);
128
129 if let Some(ref handler) = registry.block_checked_handler {
130 handler.on_block_checked(block, BlockValidationStateRef::from_ptr(state));
131 }
132}
133
134pub(crate) unsafe extern "C" fn validation_new_pow_valid_block_wrapper(
135 user_data: *mut c_void,
136 block: *mut btck_Block,
137 entry: *const btck_BlockTreeEntry,
138) {
139 let block = Block::from_ptr(block);
140 let registry = &*(user_data as *mut ValidationCallbackRegistry);
141
142 if let Some(ref handler) = registry.new_pow_valid_block_handler {
143 handler.on_new_pow_valid_block(
144 block,
145 BlockTreeEntry::from_ptr(entry as *mut btck_BlockTreeEntry),
146 );
147 }
148}
149
150pub(crate) unsafe extern "C" fn validation_block_connected_wrapper(
151 user_data: *mut c_void,
152 block: *mut btck_Block,
153 entry: *const btck_BlockTreeEntry,
154) {
155 let block = Block::from_ptr(block);
156 let registry = &*(user_data as *mut ValidationCallbackRegistry);
157
158 if let Some(ref handler) = registry.block_connected_handler {
159 handler.on_block_connected(
160 block,
161 BlockTreeEntry::from_ptr(entry as *mut btck_BlockTreeEntry),
162 );
163 }
164}
165
166pub(crate) unsafe extern "C" fn validation_block_disconnected_wrapper(
167 user_data: *mut c_void,
168 block: *mut btck_Block,
169 entry: *const btck_BlockTreeEntry,
170) {
171 let block = Block::from_ptr(block);
172 let registry = &*(user_data as *mut ValidationCallbackRegistry);
173
174 if let Some(ref handler) = registry.block_disconnected_handler {
175 handler.on_block_disconnected(
176 block,
177 BlockTreeEntry::from_ptr(entry as *mut btck_BlockTreeEntry),
178 );
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use std::sync::{Arc, Mutex};
185
186 use crate::{prelude::*, BlockValidationResult};
187
188 use super::*;
189
190 #[test]
191 fn test_registry_stores_single_handler() {
192 let mut registry = ValidationCallbackRegistry::new();
193
194 registry.register_block_checked(|_block, state: BlockValidationStateRef| {
195 assert_eq!(state.result(), BlockValidationResult::Consensus);
196 });
197
198 assert!(registry.block_checked_handler.is_some());
199 }
200
201 #[test]
202 fn test_closure_trait_implementation() {
203 let handler = |_block, _state: BlockValidationStateRef<'_>| {};
204 let _: Box<dyn BlockCheckedCallback> = Box::new(handler);
205 }
206
207 #[test]
208 fn test_block_checked_registration() {
209 let mut registry = ValidationCallbackRegistry::new();
210 registry.register_block_checked(|_block, _state: BlockValidationStateRef<'_>| {});
211 assert!(registry.block_checked_handler.is_some());
212 }
213
214 #[test]
215 fn test_new_pow_valid_block_registration() {
216 fn handler(_entry: BlockTreeEntry, _block: Block) {}
217
218 let mut registry = ValidationCallbackRegistry::new();
219 registry.register_new_pow_valid_block(handler);
220 assert!(registry.new_pow_valid_block_handler.is_some());
221 }
222
223 #[test]
224 fn test_block_connected_registration() {
225 fn handler(_block: Block, _entry: BlockTreeEntry) {}
226
227 let mut registry = ValidationCallbackRegistry::new();
228 registry.register_block_connected(handler);
229 assert!(registry.block_connected_handler.is_some());
230 }
231
232 #[test]
233 fn test_block_disconnected_registration() {
234 fn handler(_block: Block, _entry: BlockTreeEntry) {}
235
236 let mut registry = ValidationCallbackRegistry::new();
237 registry.register_block_disconnected(handler);
238 assert!(registry.block_disconnected_handler.is_some());
239 }
240
241 #[test]
242 fn test_registry_default() {
243 let registry = ValidationCallbackRegistry::default();
244 assert!(registry.block_checked_handler.is_none());
245 assert!(registry.new_pow_valid_block_handler.is_none());
246 assert!(registry.block_connected_handler.is_none());
247 assert!(registry.block_disconnected_handler.is_none());
248 }
249
250 #[test]
251 fn test_block_checked_invocation() {
252 let called = Arc::new(Mutex::new(false));
253 let called_clone = Arc::clone(&called);
254
255 let mut registry = ValidationCallbackRegistry::new();
256 registry.register_block_checked(move |_block, _state: BlockValidationStateRef<'_>| {
257 *called_clone.lock().unwrap() = true;
258 });
259
260 if let Some(ref handler) = registry.block_checked_handler {
261 let block = unsafe { Block::from_ptr(std::ptr::null_mut()) };
262 let state = unsafe { BlockValidationStateRef::from_ptr(std::ptr::null()) };
263 handler.on_block_checked(block, state);
264 }
265
266 assert!(*called.lock().unwrap());
267 }
268
269 #[test]
270 fn test_new_pow_valid_block_invocation() {
271 let called = Arc::new(Mutex::new(false));
272 let called_clone = Arc::clone(&called);
273
274 let mut registry = ValidationCallbackRegistry::new();
275 registry.register_new_pow_valid_block(move |_entry: BlockTreeEntry, _block: Block| {
276 *called_clone.lock().unwrap() = true;
277 });
278
279 if let Some(ref handler) = registry.new_pow_valid_block_handler {
280 let block = unsafe { Block::from_ptr(std::ptr::null_mut()) };
281 let entry = unsafe { BlockTreeEntry::from_ptr(std::ptr::null_mut()) };
282 handler.on_new_pow_valid_block(block, entry);
283 }
284
285 assert!(*called.lock().unwrap());
286 }
287
288 #[test]
289 fn test_block_connected_invocation() {
290 let called = Arc::new(Mutex::new(false));
291 let called_clone = Arc::clone(&called);
292
293 let mut registry = ValidationCallbackRegistry::new();
294 registry.register_block_connected(move |_block: Block, _entry: BlockTreeEntry| {
295 *called_clone.lock().unwrap() = true;
296 });
297
298 if let Some(ref handler) = registry.block_connected_handler {
299 let block = unsafe { Block::from_ptr(std::ptr::null_mut()) };
300 let entry = unsafe { BlockTreeEntry::from_ptr(std::ptr::null_mut()) };
301 handler.on_block_connected(block, entry);
302 }
303
304 assert!(*called.lock().unwrap());
305 }
306
307 #[test]
308 fn test_block_disconnected_invocation() {
309 let called = Arc::new(Mutex::new(false));
310 let called_clone = Arc::clone(&called);
311
312 let mut registry = ValidationCallbackRegistry::new();
313 registry.register_block_disconnected(move |_block: Block, _entry: BlockTreeEntry| {
314 *called_clone.lock().unwrap() = true;
315 });
316
317 if let Some(ref handler) = registry.block_disconnected_handler {
318 let block = unsafe { Block::from_ptr(std::ptr::null_mut()) };
319 let entry = unsafe { BlockTreeEntry::from_ptr(std::ptr::null_mut()) };
320 handler.on_block_disconnected(block, entry);
321 }
322
323 assert!(*called.lock().unwrap());
324 }
325}