/root/rust-bitcoinkernel/src/state/context.rs
Line | Count | Source |
1 | | //! Context initialization and configuration for the Bitcoin Kernel library. |
2 | | //! |
3 | | //! The [`Context`] holds the kernel library's logically global state and is |
4 | | //! passed to operations that need access to this state. It should be kept in |
5 | | //! scope for the duration of all operations that depend on it. |
6 | | //! |
7 | | //! # Overview |
8 | | //! |
9 | | //! The context is created using the builder pattern via [`ContextBuilder`], |
10 | | //! which allows configuration of: |
11 | | //! - Chain type (mainnet, testnet, regtest, etc.) |
12 | | //! - Notification callbacks for chain events |
13 | | //! - Validation callbacks for block processing |
14 | | //! |
15 | | //! # Examples |
16 | | //! |
17 | | //! ## Basic Usage |
18 | | //! |
19 | | //! ```no_run |
20 | | //! use bitcoinkernel::{Context, ChainType, KernelError}; |
21 | | //! |
22 | | //! // Create a default context (mainnet) |
23 | | //! let context = Context::new()?; |
24 | | //! # Ok::<(), KernelError>(()) |
25 | | //! ``` |
26 | | //! |
27 | | //! ## With Chain Type |
28 | | //! |
29 | | //! ```no_run |
30 | | //! use bitcoinkernel::{Context, ChainType, KernelError}; |
31 | | //! |
32 | | //! // Create a regtest context for testing |
33 | | //! let context = Context::builder() |
34 | | //! .chain_type(ChainType::Regtest) |
35 | | //! .build()?; |
36 | | //! # Ok::<(), KernelError>(()) |
37 | | //! ``` |
38 | | //! |
39 | | //! ## With Notifications |
40 | | //! |
41 | | //! ```no_run |
42 | | //! use bitcoinkernel::{Context, ChainType, KernelError}; |
43 | | //! |
44 | | //! let context = Context::builder() |
45 | | //! .chain_type(ChainType::Testnet) |
46 | | //! .with_progress_notification(|title, percent, _resume| { |
47 | | //! println!("{}: {}%", title, percent); |
48 | | //! }) |
49 | | //! .with_block_tip_notification(|_state, hash, _progress| { |
50 | | //! println!("New block tip: {}", hash); |
51 | | //! }) |
52 | | //! .build()?; |
53 | | //! # Ok::<(), KernelError>(()) |
54 | | //! ``` |
55 | | |
56 | | use std::ffi::c_void; |
57 | | |
58 | | use libbitcoinkernel_sys::{ |
59 | | btck_ChainParameters, btck_ChainType, btck_Context, btck_ContextOptions, |
60 | | btck_NotificationInterfaceCallbacks, btck_ValidationInterfaceCallbacks, |
61 | | btck_chain_parameters_create, btck_chain_parameters_destroy, btck_context_create, |
62 | | btck_context_destroy, btck_context_interrupt, btck_context_options_create, |
63 | | btck_context_options_destroy, btck_context_options_set_chainparams, |
64 | | btck_context_options_set_notifications, btck_context_options_set_validation_interface, |
65 | | }; |
66 | | |
67 | | use crate::{ |
68 | | ffi::{c_helpers, sealed::AsPtr}, |
69 | | notifications::{ |
70 | | notification::{ |
71 | | notification_block_tip_wrapper, notification_fatal_error_wrapper, |
72 | | notification_flush_error_wrapper, notification_header_tip_wrapper, |
73 | | notification_progress_wrapper, notification_user_data_destroy_wrapper, |
74 | | notification_warning_set_wrapper, notification_warning_unset_wrapper, BlockTipCallback, |
75 | | FatalErrorCallback, FlushErrorCallback, HeaderTipCallback, |
76 | | NotificationCallbackRegistry, ProgressCallback, WarningSetCallback, |
77 | | WarningUnsetCallback, |
78 | | }, |
79 | | validation::{ |
80 | | validation_block_checked_wrapper, validation_block_connected_wrapper, |
81 | | validation_block_disconnected_wrapper, validation_new_pow_valid_block_wrapper, |
82 | | validation_user_data_destroy_wrapper, BlockCheckedCallback, BlockConnectedCallback, |
83 | | BlockDisconnectedCallback, NewPoWValidBlockCallback, ValidationCallbackRegistry, |
84 | | }, |
85 | | }, |
86 | | KernelError, BTCK_CHAIN_TYPE_MAINNET, BTCK_CHAIN_TYPE_REGTEST, BTCK_CHAIN_TYPE_SIGNET, |
87 | | BTCK_CHAIN_TYPE_TESTNET, BTCK_CHAIN_TYPE_TESTNET_4, |
88 | | }; |
89 | | |
90 | | /// Chain parameters for configuring a [`Context`]. |
91 | | /// |
92 | | /// [`ChainParams`] encapsulates the consensus rules and network parameters |
93 | | /// for a specific Bitcoin network (mainnet, testnet, regtest, etc.). |
94 | | /// |
95 | | /// # Lifetime |
96 | | /// The chain parameters are automatically cleaned up when dropped. |
97 | | /// |
98 | | /// # Thread Safety |
99 | | /// [`ChainParams`] can be safely sent between threads and shared via `Arc`. |
100 | | /// |
101 | | /// # Examples |
102 | | /// ```no_run |
103 | | /// use bitcoinkernel::{ChainParams, ChainType}; |
104 | | /// |
105 | | /// let mainnet_params = ChainParams::new(ChainType::Mainnet); |
106 | | /// let regtest_params = ChainParams::new(ChainType::Regtest); |
107 | | /// ``` |
108 | | pub struct ChainParams { |
109 | | inner: *mut btck_ChainParameters, |
110 | | } |
111 | | |
112 | | unsafe impl Send for ChainParams {} |
113 | | unsafe impl Sync for ChainParams {} |
114 | | |
115 | | impl ChainParams { |
116 | | /// Creates new chain parameters for the specified chain type. |
117 | | /// |
118 | | /// # Arguments |
119 | | /// * `chain_type` - The Bitcoin network type to configure |
120 | | /// |
121 | | /// # Returns |
122 | | /// A new [`ChainParams`] instance configured for the specified chain. |
123 | | /// |
124 | | /// # Example |
125 | | /// ```no_run |
126 | | /// use bitcoinkernel::{ChainParams, ChainType}; |
127 | | /// |
128 | | /// let params = ChainParams::new(ChainType::Testnet); |
129 | | /// ``` |
130 | 0 | pub fn new(chain_type: ChainType) -> ChainParams { |
131 | 0 | let btck_chain_type = chain_type.into(); |
132 | 0 | ChainParams { |
133 | 0 | inner: unsafe { btck_chain_parameters_create(btck_chain_type) }, |
134 | 0 | } |
135 | 0 | } |
136 | | } |
137 | | |
138 | | impl Drop for ChainParams { |
139 | 0 | fn drop(&mut self) { |
140 | 0 | unsafe { |
141 | 0 | btck_chain_parameters_destroy(self.inner); |
142 | 0 | } |
143 | 0 | } |
144 | | } |
145 | | |
146 | | /// The main context for the Bitcoin Kernel library. |
147 | | /// |
148 | | /// The [`Context`] manages the global state of the Bitcoin Kernel library |
149 | | /// and should be kept in memory for the duration of all context-dependent operations. |
150 | | /// It is created via [`ContextBuilder`] and can be configured with various |
151 | | /// chain types and callbacks. |
152 | | /// |
153 | | /// # Lifetime |
154 | | /// It is recommended to outlive any objects that depend on it, such as |
155 | | /// [`ChainstateManager`](crate::ChainstateManager) instances. |
156 | | /// |
157 | | /// # Thread Safety |
158 | | /// [`Context`] can be safely sent between threads and shared via `Arc`. |
159 | | /// |
160 | | /// # Examples |
161 | | /// ```no_run |
162 | | /// use bitcoinkernel::{Context, ChainType, KernelError}; |
163 | | /// |
164 | | /// // Simple creation with defaults |
165 | | /// let context = Context::new()?; |
166 | | /// |
167 | | /// // Using the builder for configuration |
168 | | /// let context = Context::builder() |
169 | | /// .chain_type(ChainType::Regtest) |
170 | | /// .build()?; |
171 | | /// # Ok::<(), KernelError>(()) |
172 | | /// ``` |
173 | | pub struct Context { |
174 | | inner: *mut btck_Context, |
175 | | } |
176 | | |
177 | | unsafe impl Send for Context {} |
178 | | unsafe impl Sync for Context {} |
179 | | |
180 | | impl Context { |
181 | | /// Returns a new [`ContextBuilder`] for constructing a context. |
182 | | /// |
183 | | /// This is the recommended way to create a [`Context`] when you need |
184 | | /// to configure chain types or register callbacks. |
185 | | /// |
186 | | /// # Returns |
187 | | /// A new [`ContextBuilder`] instance. |
188 | | /// |
189 | | /// # Example |
190 | | /// ```no_run |
191 | | /// use bitcoinkernel::{Context, ChainType, KernelError}; |
192 | | /// |
193 | | /// let context = Context::builder() |
194 | | /// .chain_type(ChainType::Testnet) |
195 | | /// .build()?; |
196 | | /// # Ok::<(), KernelError>(()) |
197 | | /// ``` |
198 | 0 | pub fn builder() -> ContextBuilder { |
199 | 0 | ContextBuilder::new() |
200 | 0 | } |
201 | | |
202 | | /// Creates a new context with default settings (mainnet). |
203 | | /// |
204 | | /// This is a convenience method equivalent to calling |
205 | | /// `ContextBuilder::new().build()`. |
206 | | /// |
207 | | /// # Returns |
208 | | /// * `Ok(`[`Context`]`)` - On successful context creation |
209 | | /// * `Err(`[`KernelError::Internal`]`)` - If context creation fails |
210 | | /// |
211 | | /// # Example |
212 | | /// ```no_run |
213 | | /// use bitcoinkernel::{Context, KernelError}; |
214 | | /// |
215 | | /// let context = Context::new()?; |
216 | | /// # Ok::<(), KernelError>(()) |
217 | | /// ``` |
218 | 0 | pub fn new() -> Result<Context, KernelError> { |
219 | 0 | ContextBuilder::new().build() |
220 | 0 | } |
221 | | |
222 | | /// Interrupts any ongoing operations in the context. |
223 | | /// |
224 | | /// This signals the context to stop any long-running operations that |
225 | | /// support interruption, such as reindex, importing or processing blocks. |
226 | | /// |
227 | | /// # Returns |
228 | | /// * `Ok(())` - If the interrupt signal was successfully sent |
229 | | /// * `Err(`[`KernelError::Internal`]`)` - If the interrupt operation fails |
230 | | /// |
231 | | /// # Example |
232 | | /// ```no_run |
233 | | /// use bitcoinkernel::{Context, KernelError}; |
234 | | /// |
235 | | /// let context = Context::new()?; |
236 | | /// |
237 | | /// // Later, interrupt ongoing operations |
238 | | /// context.interrupt()?; |
239 | | /// # Ok::<(), KernelError>(()) |
240 | | /// ``` |
241 | 0 | pub fn interrupt(&self) -> Result<(), KernelError> { |
242 | 0 | let result = unsafe { btck_context_interrupt(self.inner) }; |
243 | 0 | if c_helpers::success(result) { |
244 | 0 | return Ok(()); |
245 | | } else { |
246 | 0 | return Err(KernelError::Internal( |
247 | 0 | "Context interrupt failed.".to_string(), |
248 | 0 | )); |
249 | | } |
250 | 0 | } |
251 | | } |
252 | | |
253 | | impl AsPtr<btck_Context> for Context { |
254 | 0 | fn as_ptr(&self) -> *const btck_Context { |
255 | 0 | self.inner as *const _ |
256 | 0 | } |
257 | | } |
258 | | |
259 | | impl Drop for Context { |
260 | 0 | fn drop(&mut self) { |
261 | 0 | unsafe { |
262 | 0 | btck_context_destroy(self.inner); |
263 | 0 | } |
264 | 0 | } |
265 | | } |
266 | | |
267 | | /// Builder for creating a [`Context`] with custom configuration. |
268 | | /// |
269 | | /// The builder pattern allows flexible configuration of the Bitcoin Kernel |
270 | | /// context before creation. By default, the builder configures for mainnet |
271 | | /// with no callbacks registered. |
272 | | /// |
273 | | /// # Configuration Options |
274 | | /// - **Chain type**: Set via [`chain_type`](ContextBuilder::chain_type) |
275 | | /// - **Notification callbacks**: Set via `with_*_notification` methods or [`notifications`](ContextBuilder::notifications) |
276 | | /// - **Validation callbacks**: Set via `with_*_validation` methods or [`validation`](ContextBuilder::validation) |
277 | | /// |
278 | | /// # Examples |
279 | | /// |
280 | | /// ## Basic Configuration |
281 | | /// ```no_run |
282 | | /// use bitcoinkernel::{ContextBuilder, ChainType, KernelError}; |
283 | | /// |
284 | | /// let context = ContextBuilder::new() |
285 | | /// .chain_type(ChainType::Regtest) |
286 | | /// .build()?; |
287 | | /// # Ok::<(), KernelError>(()) |
288 | | /// ``` |
289 | | /// |
290 | | /// ## With Individual Callbacks |
291 | | /// ```no_run |
292 | | /// use bitcoinkernel::{ContextBuilder, ChainType, KernelError}; |
293 | | /// |
294 | | /// let context = ContextBuilder::new() |
295 | | /// .chain_type(ChainType::Testnet) |
296 | | /// .with_progress_notification(|title, percent, _resume| { |
297 | | /// println!("Progress: {} - {}%", title, percent); |
298 | | /// }) |
299 | | /// .with_block_tip_notification(|_state, hash, _progress| { |
300 | | /// println!("New tip: {}", hash); |
301 | | /// }) |
302 | | /// .build()?; |
303 | | /// # Ok::<(), KernelError>(()) |
304 | | /// ``` |
305 | | /// |
306 | | /// ## With Advanced Configuration |
307 | | /// ```no_run |
308 | | /// use bitcoinkernel::{Block, BlockValidationStateRef, ContextBuilder, ChainType, KernelError}; |
309 | | /// |
310 | | /// let context = ContextBuilder::new() |
311 | | /// .chain_type(ChainType::Regtest) |
312 | | /// .notifications(|registry| { |
313 | | /// registry.register_progress(|title, percent, _resume| { |
314 | | /// println!("{}: {}%", title, percent); |
315 | | /// }); |
316 | | /// registry.register_warning_set(|warning, message| { |
317 | | /// eprintln!("Warning: {} - {}", warning, message); |
318 | | /// }); |
319 | | /// }) |
320 | | /// .validation(|registry| { |
321 | | /// registry.register_block_checked(|block: Block, _state: BlockValidationStateRef<'_>| { |
322 | | /// println!("Checked block: {}", block.hash()); |
323 | | /// }); |
324 | | /// }) |
325 | | /// .build()?; |
326 | | /// # Ok::<(), KernelError>(()) |
327 | | /// ``` |
328 | | pub struct ContextBuilder { |
329 | | inner: *mut btck_ContextOptions, |
330 | | notification_registry: Option<NotificationCallbackRegistry>, |
331 | | validation_registry: Option<ValidationCallbackRegistry>, |
332 | | } |
333 | | |
334 | | impl Default for ContextBuilder { |
335 | 0 | fn default() -> Self { |
336 | 0 | Self::new() |
337 | 0 | } |
338 | | } |
339 | | |
340 | | impl ContextBuilder { |
341 | | /// Creates a new context builder with default settings. |
342 | | /// |
343 | | /// The builder is initialized with mainnet configuration and no callbacks. |
344 | | /// |
345 | | /// # Returns |
346 | | /// A new [`ContextBuilder`] instance. |
347 | | /// |
348 | | /// # Example |
349 | | /// ```no_run |
350 | | /// use bitcoinkernel::ContextBuilder; |
351 | | /// |
352 | | /// let builder = ContextBuilder::new(); |
353 | | /// ``` |
354 | 0 | pub fn new() -> ContextBuilder { |
355 | 0 | ContextBuilder { |
356 | 0 | inner: unsafe { btck_context_options_create() }, |
357 | 0 | notification_registry: None, |
358 | 0 | validation_registry: None, |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | | /// Consumes the builder and creates a [`Context`]. |
363 | | /// |
364 | | /// This finalizes the configuration and creates the actual context instance. |
365 | | /// All registered callbacks are set up during this process. |
366 | | /// |
367 | | /// # Returns |
368 | | /// * `Ok(`[`Context`]`)` - On successful context creation |
369 | | /// * `Err(`[`KernelError::Internal`]`)` - If context creation fails |
370 | | /// |
371 | | /// # Example |
372 | | /// ```no_run |
373 | | /// use bitcoinkernel::{ContextBuilder, ChainType, KernelError}; |
374 | | /// |
375 | | /// let context = ContextBuilder::new() |
376 | | /// .chain_type(ChainType::Regtest) |
377 | | /// .build()?; |
378 | | /// # Ok::<(), KernelError>(()) |
379 | | /// ``` |
380 | 0 | pub fn build(mut self) -> Result<Context, KernelError> { |
381 | 0 | if let Some(registry) = self.notification_registry.take() { |
382 | 0 | self.setup_notification_interface(registry); |
383 | 0 | } |
384 | 0 | if let Some(registry) = self.validation_registry.take() { |
385 | 0 | self.setup_validation_interface(registry); |
386 | 0 | } |
387 | | |
388 | 0 | let inner = unsafe { btck_context_create(self.inner) }; |
389 | 0 | if inner.is_null() { |
390 | 0 | return Err(KernelError::Internal("Invalid context.".to_string())); |
391 | 0 | } |
392 | 0 | Ok(Context { inner }) |
393 | 0 | } |
394 | | |
395 | 0 | fn setup_notification_interface(&self, registry: NotificationCallbackRegistry) { |
396 | 0 | let registry_ptr = Box::into_raw(Box::new(registry)); |
397 | 0 | unsafe { |
398 | 0 | let holder = btck_NotificationInterfaceCallbacks { |
399 | 0 | user_data: registry_ptr as *mut c_void, |
400 | 0 | user_data_destroy: Some(notification_user_data_destroy_wrapper), |
401 | 0 | block_tip: Some(notification_block_tip_wrapper), |
402 | 0 | header_tip: Some(notification_header_tip_wrapper), |
403 | 0 | progress: Some(notification_progress_wrapper), |
404 | 0 | warning_set: Some(notification_warning_set_wrapper), |
405 | 0 | warning_unset: Some(notification_warning_unset_wrapper), |
406 | 0 | flush_error: Some(notification_flush_error_wrapper), |
407 | 0 | fatal_error: Some(notification_fatal_error_wrapper), |
408 | 0 | }; |
409 | 0 | btck_context_options_set_notifications(self.inner, holder); |
410 | 0 | } |
411 | 0 | } |
412 | | |
413 | 0 | fn setup_validation_interface(&self, registry: ValidationCallbackRegistry) { |
414 | 0 | let registry_ptr = Box::into_raw(Box::new(registry)); |
415 | 0 | unsafe { |
416 | 0 | let holder = btck_ValidationInterfaceCallbacks { |
417 | 0 | user_data: registry_ptr as *mut c_void, |
418 | 0 | user_data_destroy: Some(validation_user_data_destroy_wrapper), |
419 | 0 | block_checked: Some(validation_block_checked_wrapper), |
420 | 0 | pow_valid_block: Some(validation_new_pow_valid_block_wrapper), |
421 | 0 | block_connected: Some(validation_block_connected_wrapper), |
422 | 0 | block_disconnected: Some(validation_block_disconnected_wrapper), |
423 | 0 | }; |
424 | 0 | btck_context_options_set_validation_interface(self.inner, holder); |
425 | 0 | } |
426 | 0 | } |
427 | | |
428 | | /// Sets the Bitcoin network chain type. |
429 | | /// |
430 | | /// Configures the context to operate on the specified Bitcoin network. |
431 | | /// |
432 | | /// # Arguments |
433 | | /// * `chain_type` - The [`ChainType`] to configure (mainnet, testnet, etc.) |
434 | | /// |
435 | | /// # Returns |
436 | | /// The builder instance for method chaining. |
437 | | /// |
438 | | /// # Example |
439 | | /// ```no_run |
440 | | /// use bitcoinkernel::{ContextBuilder, ChainType, KernelError}; |
441 | | /// |
442 | | /// let context = ContextBuilder::new() |
443 | | /// .chain_type(ChainType::Regtest) |
444 | | /// .build()?; |
445 | | /// # Ok::<(), KernelError>(()) |
446 | | /// ``` |
447 | 0 | pub fn chain_type(self, chain_type: ChainType) -> ContextBuilder { |
448 | 0 | let chain_params = ChainParams::new(chain_type); |
449 | 0 | unsafe { btck_context_options_set_chainparams(self.inner, chain_params.inner) }; |
450 | 0 | self |
451 | 0 | } |
452 | | |
453 | | /// Registers a callback for block tip notifications. |
454 | | /// |
455 | | /// The callback is invoked when the chain's tip is updated to a new block. |
456 | | /// This happens during block validation and chain reorganizations. |
457 | | /// |
458 | | /// # Type Parameters |
459 | | /// * `T` - A type implementing [`BlockTipCallback`] |
460 | | /// |
461 | | /// # Arguments |
462 | | /// * `handler` - The callback function or closure that receives: |
463 | | /// - `state` - The [`SynchronizationState`](crate::SynchronizationState) (initial download, etc.) |
464 | | /// - `hash` - The [`BlockHash`](crate::BlockHash) of the new tip |
465 | | /// - `progress` - Verification progress as an `f64` (0.0 to 1.0) |
466 | | /// |
467 | | /// # Returns |
468 | | /// The builder instance for method chaining. |
469 | | /// |
470 | | /// # Example |
471 | | /// ```no_run |
472 | | /// use bitcoinkernel::{ContextBuilder, KernelError}; |
473 | | /// |
474 | | /// let context = ContextBuilder::new() |
475 | | /// .with_block_tip_notification(|_state, hash, progress| { |
476 | | /// println!("Chain tip updated to: {} ({})", hash, progress); |
477 | | /// }) |
478 | | /// .build()?; |
479 | | /// # Ok::<(), KernelError>(()) |
480 | | /// ``` |
481 | 0 | pub fn with_block_tip_notification<T>(mut self, handler: T) -> Self |
482 | 0 | where |
483 | 0 | T: BlockTipCallback + 'static, |
484 | 0 | { |
485 | 0 | self.get_or_create_notification_registry() |
486 | 0 | .register_block_tip(handler); |
487 | 0 | self |
488 | 0 | } |
489 | | |
490 | | /// Registers a callback for progress notifications. |
491 | | /// |
492 | | /// The callback is invoked to report on current block synchronization progress |
493 | | /// during operations such as initial block download or reindexing. |
494 | | /// |
495 | | /// # Type Parameters |
496 | | /// * `T` - A type implementing [`ProgressCallback`] |
497 | | /// |
498 | | /// # Arguments |
499 | | /// * `handler` - The callback function or closure that receives: |
500 | | /// - `title` - Description of the current operation as a [`String`] |
501 | | /// - `percent` - Progress percentage as an `i32` (0-100) |
502 | | /// - `resume` - Whether the operation can be resumed if interrupted (as a `bool`) |
503 | | /// |
504 | | /// # Returns |
505 | | /// The builder instance for method chaining. |
506 | | /// |
507 | | /// # Example |
508 | | /// ```no_run |
509 | | /// use bitcoinkernel::{ContextBuilder, KernelError}; |
510 | | /// |
511 | | /// let context = ContextBuilder::new() |
512 | | /// .with_progress_notification(|title, percent, resume| { |
513 | | /// println!("{}: {}% (resumable: {})", title, percent, resume); |
514 | | /// }) |
515 | | /// .build()?; |
516 | | /// # Ok::<(), KernelError>(()) |
517 | | /// ``` |
518 | 0 | pub fn with_progress_notification<T>(mut self, handler: T) -> Self |
519 | 0 | where |
520 | 0 | T: ProgressCallback + 'static, |
521 | 0 | { |
522 | 0 | self.get_or_create_notification_registry() |
523 | 0 | .register_progress(handler); |
524 | 0 | self |
525 | 0 | } |
526 | | |
527 | | /// Registers a callback for header tip notifications. |
528 | | /// |
529 | | /// The callback is invoked when a new best block header is added to the header |
530 | | /// chain. This typically occurs during the header synchronization phase, which |
531 | | /// happens before full block download. |
532 | | /// |
533 | | /// # Type Parameters |
534 | | /// * `T` - A type implementing [`HeaderTipCallback`] |
535 | | /// |
536 | | /// # Arguments |
537 | | /// * `handler` - The callback function or closure that receives: |
538 | | /// - `state` - The [`SynchronizationState`](crate::SynchronizationState) |
539 | | /// - `height` - The height of the new header tip as an `i64` |
540 | | /// - `timestamp` - The timestamp of the header as an `i64` |
541 | | /// - `presync` - Whether this is during pre-synchronization (as a `bool`) |
542 | | /// |
543 | | /// # Returns |
544 | | /// The builder instance for method chaining. |
545 | | /// |
546 | | /// # Example |
547 | | /// ```no_run |
548 | | /// use bitcoinkernel::{ContextBuilder, KernelError}; |
549 | | /// |
550 | | /// let context = ContextBuilder::new() |
551 | | /// .with_header_tip_notification(|_state, height, timestamp, _presync| { |
552 | | /// println!("New header at height {}, time={}", height, timestamp); |
553 | | /// }) |
554 | | /// .build()?; |
555 | | /// # Ok::<(), KernelError>(()) |
556 | | /// ``` |
557 | 0 | pub fn with_header_tip_notification<T>(mut self, handler: T) -> Self |
558 | 0 | where |
559 | 0 | T: HeaderTipCallback + 'static, |
560 | 0 | { |
561 | 0 | self.get_or_create_notification_registry() |
562 | 0 | .register_header_tip(handler); |
563 | 0 | self |
564 | 0 | } |
565 | | |
566 | | /// Registers a callback for warning set notifications. |
567 | | /// |
568 | | /// The callback is invoked when a warning is issued by the kernel library |
569 | | /// during validation. This can include warnings about chain forks or other |
570 | | /// consensus-related issues. |
571 | | /// |
572 | | /// # Type Parameters |
573 | | /// * `T` - A type implementing [`WarningSetCallback`] |
574 | | /// |
575 | | /// # Arguments |
576 | | /// * `handler` - The callback function or closure that receives: |
577 | | /// - `warning` - A [`Warning`](crate::Warning) identifier/category |
578 | | /// - `message` - A human-readable description as a [`String`] |
579 | | /// |
580 | | /// # Returns |
581 | | /// The builder instance for method chaining. |
582 | | /// |
583 | | /// # Example |
584 | | /// ```no_run |
585 | | /// use bitcoinkernel::{ContextBuilder, KernelError}; |
586 | | /// |
587 | | /// let context = ContextBuilder::new() |
588 | | /// .with_warning_set_notification(|warning, message| { |
589 | | /// eprintln!("Kernel Warning [{}]: {}", warning, message); |
590 | | /// }) |
591 | | /// .build()?; |
592 | | /// # Ok::<(), KernelError>(()) |
593 | 0 | pub fn with_warning_set_notification<T>(mut self, handler: T) -> Self |
594 | 0 | where |
595 | 0 | T: WarningSetCallback + 'static, |
596 | 0 | { |
597 | 0 | self.get_or_create_notification_registry() |
598 | 0 | .register_warning_set(handler); |
599 | 0 | self |
600 | 0 | } |
601 | | |
602 | | /// Registers a callback for warning unset notifications. |
603 | | /// |
604 | | /// The callback is invoked when a previous condition that led to the issuance |
605 | | /// of a warning is no longer present. This indicates that the warning condition |
606 | | /// has been resolved. |
607 | | /// |
608 | | /// # Type Parameters |
609 | | /// * `T` - A type implementing [`WarningUnsetCallback`] |
610 | | /// |
611 | | /// # Arguments |
612 | | /// * `handler` - The callback function or closure that receives: |
613 | | /// - `warning` - The [`Warning`](crate::Warning) identifier/category that was cleared |
614 | | /// |
615 | | /// # Returns |
616 | | /// The builder instance for method chaining. |
617 | | /// |
618 | | /// # Example |
619 | | /// ```no_run |
620 | | /// use bitcoinkernel::{ContextBuilder, KernelError}; |
621 | | /// |
622 | | /// let context = ContextBuilder::new() |
623 | | /// .with_warning_unset_notification(|warning| { |
624 | | /// println!("Warning [{}] has been cleared", warning); |
625 | | /// }) |
626 | | /// .build()?; |
627 | | /// # Ok::<(), KernelError>(()) |
628 | | /// ``` |
629 | 0 | pub fn with_warning_unset_notification<T>(mut self, handler: T) -> Self |
630 | 0 | where |
631 | 0 | T: WarningUnsetCallback + 'static, |
632 | 0 | { |
633 | 0 | self.get_or_create_notification_registry() |
634 | 0 | .register_warning_unset(handler); |
635 | 0 | self |
636 | 0 | } |
637 | | |
638 | | /// Registers a callback for flush error notifications. |
639 | | /// |
640 | | /// The callback is invoked when an error occurs while flushing data |
641 | | /// to disk. |
642 | | /// |
643 | | /// # Type Parameters |
644 | | /// * `T` - A type implementing [`FlushErrorCallback`] |
645 | | /// |
646 | | /// # Arguments |
647 | | /// * `handler` - The callback function or closure that receives: |
648 | | /// - `message` - The error message as a [`String`] |
649 | | /// |
650 | | /// # Returns |
651 | | /// The builder instance for method chaining. |
652 | | /// |
653 | | /// # Example |
654 | | /// ```no_run |
655 | | /// use bitcoinkernel::{ContextBuilder, KernelError}; |
656 | | /// |
657 | | /// let context = ContextBuilder::new() |
658 | | /// .with_flush_error_notification(|message| { |
659 | | /// eprintln!("Flush error: {}", message); |
660 | | /// }) |
661 | | /// .build()?; |
662 | | /// # Ok::<(), KernelError>(()) |
663 | | /// ``` |
664 | 0 | pub fn with_flush_error_notification<T>(mut self, handler: T) -> Self |
665 | 0 | where |
666 | 0 | T: FlushErrorCallback + 'static, |
667 | 0 | { |
668 | 0 | self.get_or_create_notification_registry() |
669 | 0 | .register_flush_error(handler); |
670 | 0 | self |
671 | 0 | } |
672 | | |
673 | | /// Registers a callback for fatal error notifications. |
674 | | /// |
675 | | /// The callback is invoked when an unrecoverable system error is encountered |
676 | | /// by the library. These are critical errors that typically require the |
677 | | /// application to shut down gracefully. |
678 | | /// |
679 | | /// # Type Parameters |
680 | | /// * `T` - A type implementing [`FatalErrorCallback`] |
681 | | /// |
682 | | /// # Arguments |
683 | | /// * `handler` - The callback function or closure that receives: |
684 | | /// - `message` - A description of the fatal error as a [`String`] |
685 | | /// |
686 | | /// # Returns |
687 | | /// The builder instance for method chaining. |
688 | | /// |
689 | | /// # Example |
690 | | /// ```no_run |
691 | | /// use bitcoinkernel::{ContextBuilder, KernelError}; |
692 | | /// |
693 | | /// let context = ContextBuilder::new() |
694 | | /// .with_fatal_error_notification(|message| { |
695 | | /// eprintln!("FATAL ERROR: {}", message); |
696 | | /// // Perform cleanup and shutdown |
697 | | /// std::process::exit(1); |
698 | | /// }) |
699 | | /// .build()?; |
700 | | /// # Ok::<(), KernelError>(()) |
701 | | /// ``` |
702 | 0 | pub fn with_fatal_error_notification<T>(mut self, handler: T) -> Self |
703 | 0 | where |
704 | 0 | T: FatalErrorCallback + 'static, |
705 | 0 | { |
706 | 0 | self.get_or_create_notification_registry() |
707 | 0 | .register_fatal_error(handler); |
708 | 0 | self |
709 | 0 | } |
710 | | |
711 | | /// Configures multiple notification callbacks at once. |
712 | | /// |
713 | | /// This method provides access to the [`NotificationCallbackRegistry`] |
714 | | /// for advanced configuration of multiple callbacks. |
715 | | /// |
716 | | /// # Type Parameters |
717 | | /// * `F` - A closure taking a mutable reference to the registry |
718 | | /// |
719 | | /// # Arguments |
720 | | /// * `configure` - A closure that configures the notification registry |
721 | | /// |
722 | | /// # Returns |
723 | | /// The builder instance for method chaining. |
724 | | /// |
725 | | /// # Example |
726 | | /// ```no_run |
727 | | /// use bitcoinkernel::{ContextBuilder, KernelError}; |
728 | | /// |
729 | | /// let context = ContextBuilder::new() |
730 | | /// .notifications(|registry| { |
731 | | /// registry.register_progress(|title, percent, _resume| { |
732 | | /// println!("{}: {}%", title, percent); |
733 | | /// }); |
734 | | /// registry.register_block_tip(|_state, hash, _progress| { |
735 | | /// println!("Tip: {}", hash); |
736 | | /// }); |
737 | | /// registry.register_warning_set(|_warning, msg| { |
738 | | /// eprintln!("Warning: {}", msg); |
739 | | /// }); |
740 | | /// }) |
741 | | /// .build()?; |
742 | | /// # Ok::<(), KernelError>(()) |
743 | | /// ``` |
744 | 0 | pub fn notifications<F>(mut self, configure: F) -> Self |
745 | 0 | where |
746 | 0 | F: FnOnce(&mut NotificationCallbackRegistry), |
747 | 0 | { |
748 | 0 | let registry = self.get_or_create_notification_registry(); |
749 | 0 | configure(registry); |
750 | 0 | self |
751 | 0 | } |
752 | | |
753 | 0 | fn get_or_create_notification_registry(&mut self) -> &mut NotificationCallbackRegistry { |
754 | 0 | if self.notification_registry.is_none() { |
755 | 0 | self.notification_registry = Some(NotificationCallbackRegistry::new()); |
756 | 0 | } |
757 | 0 | self.notification_registry.as_mut().unwrap() |
758 | 0 | } |
759 | | |
760 | | /// Registers a callback for block checked validation events. |
761 | | /// |
762 | | /// The callback is invoked when a new block has been fully validated. |
763 | | /// The validation state contains the result of the validation, including |
764 | | /// whether the block is valid and any rejection reasons if invalid. |
765 | | /// |
766 | | /// # Type Parameters |
767 | | /// * `T` - A type implementing [`BlockCheckedCallback`] |
768 | | /// |
769 | | /// # Arguments |
770 | | /// * `handler` - The callback function or closure that receives: |
771 | | /// - `block` - The [`Block`](crate::Block) that was validated |
772 | | /// - `state` - The [`BlockValidationStateRef`](crate::notifications::types::BlockValidationStateRef) containing the validation result |
773 | | /// |
774 | | /// # Returns |
775 | | /// The builder instance for method chaining. |
776 | | /// |
777 | | /// # Example |
778 | | /// ```no_run |
779 | | /// use bitcoinkernel::{ |
780 | | /// prelude::*, Block, BlockValidationStateRef, ContextBuilder, |
781 | | /// KernelError, ValidationMode, |
782 | | /// }; |
783 | | /// |
784 | | /// let context = ContextBuilder::new() |
785 | | /// .with_block_checked_validation(|block: Block, state: BlockValidationStateRef<'_>| { |
786 | | /// println!("Block validated: {}", block.hash()); |
787 | | /// if state.mode() != ValidationMode::Valid { |
788 | | /// eprintln!("Validation failed with result: {:?}", state.result()); |
789 | | /// } |
790 | | /// }) |
791 | | /// .build()?; |
792 | | /// # Ok::<(), KernelError>(()) |
793 | | /// ``` |
794 | 0 | pub fn with_block_checked_validation<T>(mut self, handler: T) -> Self |
795 | 0 | where |
796 | 0 | T: BlockCheckedCallback + 'static, |
797 | 0 | { |
798 | 0 | self.get_or_create_validation_registry() |
799 | 0 | .register_block_checked(handler); |
800 | 0 | self |
801 | 0 | } |
802 | | |
803 | | /// Registers a callback for new proof-of-work valid block events. |
804 | | /// |
805 | | /// The callback is invoked when a new block extends the header chain and |
806 | | /// has a valid transaction and segwit merkle root. |
807 | | /// |
808 | | /// # Type Parameters |
809 | | /// * `T` - A type implementing [`NewPoWValidBlockCallback`] |
810 | | /// |
811 | | /// # Arguments |
812 | | /// * `handler` - The callback function or closure that receives: |
813 | | /// - `entry` - The [`BlockTreeEntry`](crate::BlockTreeEntry) for the new block |
814 | | /// - `block` - The [`Block`](crate::Block) data |
815 | | /// |
816 | | /// # Returns |
817 | | /// The builder instance for method chaining. |
818 | | /// |
819 | | /// # Example |
820 | | /// ```no_run |
821 | | /// use bitcoinkernel::{Block, BlockTreeEntry, ContextBuilder, KernelError}; |
822 | | /// |
823 | | /// let context = ContextBuilder::new() |
824 | | /// .with_new_pow_valid_block_validation(|entry: BlockTreeEntry<'_>, block: Block| { |
825 | | /// println!("New PoW-valid block at height {}: {}", |
826 | | /// entry.height(), block.hash()); |
827 | | /// }) |
828 | | /// .build()?; |
829 | | /// # Ok::<(), KernelError>(()) |
830 | | /// ``` |
831 | 0 | pub fn with_new_pow_valid_block_validation<T>(mut self, handler: T) -> Self |
832 | 0 | where |
833 | 0 | T: NewPoWValidBlockCallback + 'static, |
834 | 0 | { |
835 | 0 | self.get_or_create_validation_registry() |
836 | 0 | .register_new_pow_valid_block(handler); |
837 | 0 | self |
838 | 0 | } |
839 | | |
840 | | /// Registers a callback for block connected events. |
841 | | /// |
842 | | /// The callback is invoked when a block is valid and has now been connected |
843 | | /// to the best chain. This happens after the block passes full validation |
844 | | /// and becomes part of the active chain. |
845 | | /// |
846 | | /// # Type Parameters |
847 | | /// * `T` - A type implementing [`BlockConnectedCallback`] |
848 | | /// |
849 | | /// # Arguments |
850 | | /// * `handler` - The callback function or closure that receives: |
851 | | /// - `block` - The [`Block`](crate::Block) that was connected |
852 | | /// - `entry` - The [`BlockTreeEntry`](crate::BlockTreeEntry) representing the block's position in the chain |
853 | | /// |
854 | | /// # Returns |
855 | | /// The builder instance for method chaining. |
856 | | /// |
857 | | /// # Example |
858 | | /// ```no_run |
859 | | /// use bitcoinkernel::{Block, BlockTreeEntry, ContextBuilder, KernelError}; |
860 | | /// |
861 | | /// let context = ContextBuilder::new() |
862 | | /// .with_block_connected_validation(|block: Block, entry: BlockTreeEntry<'_>| { |
863 | | /// println!("Block connected at height {}: {}", |
864 | | /// entry.height(), block.hash()); |
865 | | /// }) |
866 | | /// .build()?; |
867 | | /// # Ok::<(), KernelError>(()) |
868 | | /// ``` |
869 | 0 | pub fn with_block_connected_validation<T>(mut self, handler: T) -> Self |
870 | 0 | where |
871 | 0 | T: BlockConnectedCallback + 'static, |
872 | 0 | { |
873 | 0 | self.get_or_create_validation_registry() |
874 | 0 | .register_block_connected(handler); |
875 | 0 | self |
876 | 0 | } |
877 | | |
878 | | /// Registers a callback for block disconnected events. |
879 | | /// |
880 | | /// The callback is invoked during a reorganization when a block has been |
881 | | /// removed from the best chain. This occurs when a competing chain with |
882 | | /// more cumulative work becomes the new active chain, requiring blocks |
883 | | /// from the old chain to be disconnected. |
884 | | /// |
885 | | /// # Type Parameters |
886 | | /// * `T` - A type implementing [`BlockDisconnectedCallback`] |
887 | | /// |
888 | | /// # Arguments |
889 | | /// * `handler` - The callback function or closure that receives: |
890 | | /// - `block` - The [`Block`](crate::Block) that was disconnected |
891 | | /// - `entry` - The [`BlockTreeEntry`](crate::BlockTreeEntry) for the disconnected block |
892 | | /// |
893 | | /// # Returns |
894 | | /// The builder instance for method chaining. |
895 | | /// |
896 | | /// # Example |
897 | | /// ```no_run |
898 | | /// use bitcoinkernel::{Block, BlockTreeEntry, ContextBuilder, KernelError}; |
899 | | /// |
900 | | /// let context = ContextBuilder::new() |
901 | | /// .with_block_disconnected_validation(|block: Block, entry: BlockTreeEntry<'_>| { |
902 | | /// println!("Block disconnected from height {}: {} (reorg)", |
903 | | /// entry.height(), block.hash()); |
904 | | /// }) |
905 | | /// .build()?; |
906 | | /// # Ok::<(), KernelError>(()) |
907 | | /// ``` |
908 | 0 | pub fn with_block_disconnected_validation<T>(mut self, handler: T) -> Self |
909 | 0 | where |
910 | 0 | T: BlockDisconnectedCallback + 'static, |
911 | 0 | { |
912 | 0 | self.get_or_create_validation_registry() |
913 | 0 | .register_block_disconnected(handler); |
914 | 0 | self |
915 | 0 | } |
916 | | |
917 | | /// Configures multiple validation callbacks at once. |
918 | | /// |
919 | | /// This method provides access to the [`ValidationCallbackRegistry`] |
920 | | /// for advanced configuration of multiple validation callbacks. |
921 | | /// |
922 | | /// # Type Parameters |
923 | | /// * `F` - A closure taking a mutable reference to the registry |
924 | | /// |
925 | | /// # Arguments |
926 | | /// * `configure` - A closure that configures the validation registry |
927 | | /// |
928 | | /// # Returns |
929 | | /// The builder instance for method chaining. |
930 | | /// |
931 | | /// # Example |
932 | | /// ```no_run |
933 | | /// use bitcoinkernel::{Block, BlockTreeEntry, BlockValidationStateRef, ContextBuilder, KernelError}; |
934 | | /// |
935 | | /// let context = ContextBuilder::new() |
936 | | /// .validation(|registry| { |
937 | | /// registry.register_block_checked(|block: Block, _state: BlockValidationStateRef<'_>| { |
938 | | /// println!("Checked: {}", block.hash()); |
939 | | /// }); |
940 | | /// registry.register_block_connected(|_block, entry: BlockTreeEntry<'_>| { |
941 | | /// println!("Connected at height {}", entry.height()); |
942 | | /// }); |
943 | | /// registry.register_block_disconnected(|_block, entry: BlockTreeEntry<'_>| { |
944 | | /// println!("Disconnected from height {}", entry.height()); |
945 | | /// }); |
946 | | /// }) |
947 | | /// .build()?; |
948 | | /// # Ok::<(), KernelError>(()) |
949 | | /// ``` |
950 | 0 | pub fn validation<F>(mut self, configure: F) -> Self |
951 | 0 | where |
952 | 0 | F: FnOnce(&mut ValidationCallbackRegistry), |
953 | 0 | { |
954 | 0 | let registry = self.get_or_create_validation_registry(); |
955 | 0 | configure(registry); |
956 | 0 | self |
957 | 0 | } |
958 | | |
959 | 0 | fn get_or_create_validation_registry(&mut self) -> &mut ValidationCallbackRegistry { |
960 | 0 | if self.validation_registry.is_none() { |
961 | 0 | self.validation_registry = Some(ValidationCallbackRegistry::new()); |
962 | 0 | } |
963 | 0 | self.validation_registry.as_mut().unwrap() |
964 | 0 | } |
965 | | } |
966 | | |
967 | | impl Drop for ContextBuilder { |
968 | 0 | fn drop(&mut self) { |
969 | 0 | unsafe { |
970 | 0 | btck_context_options_destroy(self.inner); |
971 | 0 | } |
972 | 0 | } |
973 | | } |
974 | | |
975 | | /// Bitcoin network chain types. |
976 | | /// |
977 | | /// Specifies which Bitcoin network the kernel should operate on. |
978 | | /// Each chain type has different consensus rules and network parameters. |
979 | | /// |
980 | | /// # Variants |
981 | | /// * [`Mainnet`](ChainType::Mainnet) - Production network with economic value |
982 | | /// * [`Testnet`](ChainType::Testnet) - Test network for development and testing |
983 | | /// * [`Testnet4`](ChainType::Testnet4) - Newer test network with tweaked block production |
984 | | /// * [`Signet`](ChainType::Signet) - Test network with controlled, regular block production |
985 | | /// * [`Regtest`](ChainType::Regtest) - Regression test network for local development |
986 | | /// |
987 | | /// # Examples |
988 | | /// ```no_run |
989 | | /// use bitcoinkernel::{ChainType, ContextBuilder, KernelError}; |
990 | | /// |
991 | | /// // For production |
992 | | /// let mainnet_ctx = ContextBuilder::new() |
993 | | /// .chain_type(ChainType::Mainnet) |
994 | | /// .build()?; |
995 | | /// |
996 | | /// // For local testing |
997 | | /// let regtest_ctx = ContextBuilder::new() |
998 | | /// .chain_type(ChainType::Regtest) |
999 | | /// .build()?; |
1000 | | /// # Ok::<(), KernelError>(()) |
1001 | | /// ``` |
1002 | | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
1003 | | #[repr(u8)] |
1004 | | pub enum ChainType { |
1005 | | /// Bitcoin mainnet - production network with economic value |
1006 | | Mainnet = BTCK_CHAIN_TYPE_MAINNET, |
1007 | | /// Bitcoin testnet3 - test network for development and testing |
1008 | | Testnet = BTCK_CHAIN_TYPE_TESTNET, |
1009 | | /// Bitcoin testnet4 - newer test network with tweaked block production |
1010 | | Testnet4 = BTCK_CHAIN_TYPE_TESTNET_4, |
1011 | | /// Bitcoin signet - test network with controlled, regular block production |
1012 | | Signet = BTCK_CHAIN_TYPE_SIGNET, |
1013 | | /// Regression test network for local development |
1014 | | Regtest = BTCK_CHAIN_TYPE_REGTEST, |
1015 | | } |
1016 | | |
1017 | | impl From<ChainType> for btck_ChainType { |
1018 | 0 | fn from(chain_type: ChainType) -> Self { |
1019 | 0 | chain_type as btck_ChainType |
1020 | 0 | } |
1021 | | } |
1022 | | |
1023 | | impl From<btck_ChainType> for ChainType { |
1024 | 0 | fn from(value: btck_ChainType) -> Self { |
1025 | 0 | match value { |
1026 | 0 | BTCK_CHAIN_TYPE_MAINNET => ChainType::Mainnet, |
1027 | 0 | BTCK_CHAIN_TYPE_TESTNET => ChainType::Testnet, |
1028 | 0 | BTCK_CHAIN_TYPE_TESTNET_4 => ChainType::Testnet4, |
1029 | 0 | BTCK_CHAIN_TYPE_SIGNET => ChainType::Signet, |
1030 | 0 | BTCK_CHAIN_TYPE_REGTEST => ChainType::Regtest, |
1031 | 0 | _ => panic!("Unknown chain type: {}", value), |
1032 | | } |
1033 | 0 | } |
1034 | | } |
1035 | | |
1036 | | #[cfg(test)] |
1037 | | mod tests { |
1038 | | use crate::{BlockTreeEntry, BlockValidationStateRef}; |
1039 | | |
1040 | | use super::*; |
1041 | | |
1042 | | #[test] |
1043 | | fn test_chain_type_conversions() { |
1044 | | let mainnet = ChainType::Mainnet; |
1045 | | let btck_mainnet: btck_ChainType = mainnet.into(); |
1046 | | let back_to_mainnet: ChainType = btck_mainnet.into(); |
1047 | | assert_eq!(mainnet, back_to_mainnet); |
1048 | | |
1049 | | let testnet = ChainType::Testnet; |
1050 | | let btck_testnet: btck_ChainType = testnet.into(); |
1051 | | let back_to_testnet: ChainType = btck_testnet.into(); |
1052 | | assert_eq!(testnet, back_to_testnet); |
1053 | | |
1054 | | let testnet4 = ChainType::Testnet4; |
1055 | | let btck_testnet4: btck_ChainType = testnet4.into(); |
1056 | | let back_to_testnet4: ChainType = btck_testnet4.into(); |
1057 | | assert_eq!(testnet4, back_to_testnet4); |
1058 | | |
1059 | | let signet = ChainType::Signet; |
1060 | | let btck_signet: btck_ChainType = signet.into(); |
1061 | | let back_to_signet: ChainType = btck_signet.into(); |
1062 | | assert_eq!(signet, back_to_signet); |
1063 | | |
1064 | | let regtest = ChainType::Regtest; |
1065 | | let btck_regtest: btck_ChainType = regtest.into(); |
1066 | | let back_to_regtest: ChainType = btck_regtest.into(); |
1067 | | assert_eq!(regtest, back_to_regtest); |
1068 | | } |
1069 | | |
1070 | | #[test] |
1071 | | fn test_chain_type_equality() { |
1072 | | assert_eq!(ChainType::Mainnet, ChainType::Mainnet); |
1073 | | assert_ne!(ChainType::Mainnet, ChainType::Testnet); |
1074 | | assert_ne!(ChainType::Testnet, ChainType::Testnet4); |
1075 | | assert_ne!(ChainType::Signet, ChainType::Regtest); |
1076 | | } |
1077 | | |
1078 | | #[test] |
1079 | | fn test_chain_type_clone() { |
1080 | | let mainnet = ChainType::Mainnet; |
1081 | | let cloned = mainnet; |
1082 | | assert_eq!(mainnet, cloned); |
1083 | | } |
1084 | | |
1085 | | // ChainParams tests |
1086 | | #[test] |
1087 | | fn test_chain_params_creation() { |
1088 | | let _mainnet_params = ChainParams::new(ChainType::Mainnet); |
1089 | | let _testnet_params = ChainParams::new(ChainType::Testnet); |
1090 | | let _testnet4_params = ChainParams::new(ChainType::Testnet4); |
1091 | | let _signet_params = ChainParams::new(ChainType::Signet); |
1092 | | let _regtest_params = ChainParams::new(ChainType::Regtest); |
1093 | | } |
1094 | | |
1095 | | // Context tests |
1096 | | #[test] |
1097 | | fn test_context_creation_default() { |
1098 | | let mut context = ContextBuilder::new().build(); |
1099 | | assert!(context.is_ok()); |
1100 | | context = Context::new(); |
1101 | | assert!(context.is_ok()); |
1102 | | context = Context::builder().build(); |
1103 | | assert!(context.is_ok()); |
1104 | | } |
1105 | | |
1106 | | #[test] |
1107 | | fn test_context_creation_with_chain_types() { |
1108 | | let mainnet = ContextBuilder::new().chain_type(ChainType::Mainnet).build(); |
1109 | | assert!(mainnet.is_ok()); |
1110 | | |
1111 | | let testnet = ContextBuilder::new().chain_type(ChainType::Testnet).build(); |
1112 | | assert!(testnet.is_ok()); |
1113 | | |
1114 | | let testnet4 = ContextBuilder::new() |
1115 | | .chain_type(ChainType::Testnet4) |
1116 | | .build(); |
1117 | | assert!(testnet4.is_ok()); |
1118 | | |
1119 | | let signet = ContextBuilder::new().chain_type(ChainType::Signet).build(); |
1120 | | assert!(signet.is_ok()); |
1121 | | |
1122 | | let regtest = ContextBuilder::new().chain_type(ChainType::Regtest).build(); |
1123 | | assert!(regtest.is_ok()); |
1124 | | } |
1125 | | |
1126 | | #[test] |
1127 | | fn test_context_interrupt() { |
1128 | | let context = ContextBuilder::new() |
1129 | | .chain_type(ChainType::Regtest) |
1130 | | .build() |
1131 | | .unwrap(); |
1132 | | |
1133 | | let result = context.interrupt(); |
1134 | | assert!(result.is_ok()); |
1135 | | } |
1136 | | |
1137 | | #[test] |
1138 | | fn test_context_builder_default() { |
1139 | | let builder1 = ContextBuilder::default(); |
1140 | | let builder2 = ContextBuilder::new(); |
1141 | | |
1142 | | assert!(builder1.notification_registry.is_none()); |
1143 | | assert!(builder2.notification_registry.is_none()); |
1144 | | assert!(builder1.validation_registry.is_none()); |
1145 | | assert!(builder2.validation_registry.is_none()); |
1146 | | } |
1147 | | |
1148 | | // Callback tests |
1149 | | #[test] |
1150 | | fn test_notification_callback_registration_methods() { |
1151 | | let mut builder = ContextBuilder::new(); |
1152 | | |
1153 | | builder = builder |
1154 | | .with_progress_notification(|_title, _percent, _resume| {}) |
1155 | | .with_block_tip_notification(|_state, _hash, _progress| {}) |
1156 | | .with_header_tip_notification(|_state, _height, _timestamp, _presync| {}) |
1157 | | .with_warning_set_notification(|_warning, _message| {}) |
1158 | | .with_warning_unset_notification(|_warning| {}) |
1159 | | .with_flush_error_notification(|_message| {}) |
1160 | | .with_fatal_error_notification(|_message| {}); |
1161 | | |
1162 | | assert!(builder.notification_registry.is_some()); |
1163 | | } |
1164 | | |
1165 | | #[test] |
1166 | | fn test_validation_callback_registration_method() { |
1167 | | let mut builder = ContextBuilder::new(); |
1168 | | |
1169 | | builder = |
1170 | | builder.with_block_checked_validation(|_block, _state: BlockValidationStateRef<'_>| {}); |
1171 | | |
1172 | | assert!(builder.validation_registry.is_some()); |
1173 | | } |
1174 | | |
1175 | | #[test] |
1176 | | fn test_advanced_notification_configuration() { |
1177 | | let mut builder = ContextBuilder::new(); |
1178 | | |
1179 | | builder = builder.notifications(|registry| { |
1180 | | registry.register_progress(|_title, _percent, _resume| {}); |
1181 | | registry.register_block_tip(|_state, _hash, _progress| {}); |
1182 | | }); |
1183 | | |
1184 | | assert!(builder.notification_registry.is_some()); |
1185 | | } |
1186 | | |
1187 | | #[test] |
1188 | | fn test_advanced_validation_configuration() { |
1189 | | fn pow_handler(_entry: crate::BlockTreeEntry, _block: crate::Block) {} |
1190 | | fn connected_handler(_block: crate::Block, _entry: crate::BlockTreeEntry) {} |
1191 | | fn disconnected_handler(_block: crate::Block, _entry: crate::BlockTreeEntry) {} |
1192 | | |
1193 | | let mut builder = ContextBuilder::new(); |
1194 | | |
1195 | | builder = builder.validation(|registry| { |
1196 | | registry.register_block_checked(|_block, _state: BlockValidationStateRef<'_>| {}); |
1197 | | registry.register_new_pow_valid_block(pow_handler); |
1198 | | registry.register_block_connected(connected_handler); |
1199 | | registry.register_block_disconnected(disconnected_handler); |
1200 | | }); |
1201 | | |
1202 | | assert!(builder.validation_registry.is_some()); |
1203 | | } |
1204 | | |
1205 | | #[test] |
1206 | | fn test_mixed_callback_registration() { |
1207 | | let mut builder = ContextBuilder::new(); |
1208 | | |
1209 | | builder = builder |
1210 | | .with_progress_notification(|_title, _percent, _resume| {}) |
1211 | | .with_block_checked_validation(|_block, _state: BlockValidationStateRef<'_>| {}) |
1212 | | .chain_type(ChainType::Testnet); |
1213 | | |
1214 | | assert!(builder.notification_registry.is_some()); |
1215 | | assert!(builder.validation_registry.is_some()); |
1216 | | } |
1217 | | |
1218 | | #[test] |
1219 | | fn test_lazy_registry_creation() { |
1220 | | let builder = ContextBuilder::new(); |
1221 | | |
1222 | | assert!(builder.notification_registry.is_none()); |
1223 | | assert!(builder.validation_registry.is_none()); |
1224 | | } |
1225 | | |
1226 | | #[test] |
1227 | | fn test_method_chaining_preserves_other_settings() { |
1228 | | let builder = ContextBuilder::new() |
1229 | | .chain_type(ChainType::Regtest) |
1230 | | .with_progress_notification(|_title, _percent, _resume| {}) |
1231 | | .with_block_checked_validation(|_block, _state: BlockValidationStateRef<'_>| {}); |
1232 | | |
1233 | | assert!(builder.notification_registry.is_some()); |
1234 | | assert!(builder.validation_registry.is_some()); |
1235 | | } |
1236 | | |
1237 | | #[test] |
1238 | | fn test_build_with_callbacks() { |
1239 | | let context_result = ContextBuilder::new() |
1240 | | .with_progress_notification(|_title, _percent, _resume| {}) |
1241 | | .with_block_checked_validation(|_block, _state: BlockValidationStateRef<'_>| {}) |
1242 | | .with_block_connected_validation(|_block, _block_index: BlockTreeEntry<'_>| {}) |
1243 | | .chain_type(ChainType::Testnet) |
1244 | | .build(); |
1245 | | |
1246 | | assert!(context_result.is_ok()); |
1247 | | let _context = context_result.unwrap(); |
1248 | | } |
1249 | | } |