Coverage Report

Created: 2025-11-24 16:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}