niebla_158/lib.rs
1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3//! niebla-158: a compact-filter (BIP-158) client engine for wallets.
4//!
5//! ## What you implement
6//! - [`FilterSource`]: fetch cfheaders batches, per-block filters, and raw blocks.
7//! - [`WalletHooks`]: provide a **watchlist** and handle **on_block_match** callbacks.
8//! - [`Store`]: keep a couple of integers (verified tip + last scanned).
9//! - [`HeaderSource`]: return block header info by height (used to scan ranges).
10//!
11//! ## What the engine does
12//! - Validates **cfheaders** against optional checkpoints (defense-in-depth).
13//! - Iterates new heights, pulls **filters**, tests against your watchlist.
14//! - On a hit, fetches the **block**, decodes transactions, and notifies you.
15//!
16//! ## Minimal usage
17//! ```rust,ignore
18//! use niebla_158::prelude::*;
19//! use bitcoin::{BlockHash, ScriptBuf, hashes::{sha256d, Hash as _}};
20//! use async_trait::async_trait;
21//!
22//! // --- Your implementations ---
23//! struct MySource;
24//! #[async_trait]
25//! impl FilterSource for MySource {
26//! async fn get_cfheaders(&self, _start: u32, _stop: BlockHash) -> anyhow::Result<CfHeadersBatch> {
27//! Ok(CfHeadersBatch { start_height: 0, headers: vec![] })
28//! }
29//! async fn get_cfilter(&self, _block: BlockHash) -> anyhow::Result<Vec<u8>> { Ok(vec![]) }
30//! async fn get_block(&self, _block: BlockHash) -> anyhow::Result<Vec<u8>> { Ok(vec![]) }
31//! }
32//!
33//! struct MyHeaders;
34//! #[async_trait]
35//! impl HeaderSource for MyHeaders {
36//! async fn tip_height(&self) -> anyhow::Result<u32> { Ok(0) }
37//! async fn hash_at_height(&self, _h: u32) -> anyhow::Result<BlockHash> {
38//! Ok(BlockHash::from_raw_hash(sha256d::Hash::all_zeros()))
39//! }
40//! }
41//!
42//! struct MyStore;
43//! #[async_trait]
44//! impl Store for MyStore {
45//! async fn load_cf_tip(&self) -> anyhow::Result<Option<(u32, BlockHash)>> { Ok(None) }
46//! async fn save_cf_tip(&self, _h: u32, _cf: BlockHash) -> anyhow::Result<()> { Ok(()) }
47//! async fn get_last_scanned(&self) -> anyhow::Result<u32> { Ok(0) }
48//! async fn set_last_scanned(&self, _h: u32) -> anyhow::Result<()> { Ok(()) }
49//! async fn get_birth_height(&self) -> anyhow::Result<Option<u32>> { Ok(None) }
50//! async fn set_birth_height(&self, _h: u32) -> anyhow::Result<()> { Ok(()) }
51//! }
52//!
53//! struct MyWallet;
54//! #[async_trait]
55//! impl WalletHooks for MyWallet {
56//! async fn watchlist(&self) -> anyhow::Result<Vec<ScriptBuf>> { Ok(vec![]) }
57//! async fn on_block_match(
58//! &self, _h: u32, _b: BlockHash, _txs: Vec<bitcoin::Transaction>
59//! ) -> anyhow::Result<()> { Ok(()) }
60//! }
61//!
62//! // --- Wire it up ---
63//! async fn run() -> anyhow::Result<()> {
64//! let engine = Niebla158::new(MyStore, MyWallet, MySource, MyHeaders);
65//! // Drive with an iterator of (height, header_hash); here empty:
66//! engine.run_to_tip(std::iter::empty()).await?;
67//! Ok(())
68//! }
69//! ```
70/// Engine that verifies cfheaders, scans filters, and fetches matching blocks.
71pub mod engine;
72
73/// Traits and types for fetching cfheaders, cfilters, and blocks from the network.
74pub mod filter_source;
75
76/// Wallet callbacks: provide a watchlist and receive matches.
77pub mod hooks;
78
79/// Block header lookup abstraction (height → hash).
80pub mod headers;
81
82// Internal helpers:
83mod cfheaders;
84mod checkpoints;
85mod matcher;
86
87/// Persistence layer (traits and SQLite implementation).
88pub mod store;
89
90// Public re-exports
91pub use engine::Niebla158;
92pub use filter_source::FilterSource;
93pub use hooks::WalletHooks;
94pub use store::{sqlite_store::SqliteStore, Store};
95
96/// Convenience prelude for end users.
97pub mod prelude {
98 pub use crate::{FilterSource, Niebla158, SqliteStore, Store, WalletHooks};
99}
100