bitcoinkernel/notifications/
validation.rs

1use 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
10/// Exposes the result after validating a block.
11pub 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
24/// Callback for when a new PoW valid block is found.
25pub 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
38/// Callback for when a block is connected to the chain.
39pub 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
52/// Callback for when a block is disconnected from the chain.
53pub 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/// Registry for managing validation interface callback handlers.
67#[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}