Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6ac3da2
Add BIP 157 compact block filter chain source
febyeji Mar 25, 2026
21032e9
Add CBF integration tests and documentation
febyeji Mar 25, 2026
ebaa03b
Add optional fee source from esplora/electrum
randomlogin Mar 25, 2026
01ba024
Remove race condition in test unified_send_receive
randomlogin Mar 26, 2026
fd47797
Fix CBF chain source build errors and UniFFI bindings (#5)
febyeji Mar 27, 2026
fae22a2
Remove last_synced_height from cbf
randomlogin Apr 1, 2026
03baa47
feat(cbf):
febyeji Apr 6, 2026
a4365d3
test(cbf): strengthen wait_for_cbf_sync with state verification
febyeji Apr 6, 2026
82d950c
Add test to capture wallet checkpoint push problems
randomlogin Apr 1, 2026
b710c30
fix(cbf): use CheckPoint::insert for reorg-aware wallet sync
randomlogin Apr 20, 2026
9b85754
remove latest_tip from the cbf chain source
randomlogin Apr 21, 2026
11377cc
fix(cbf): treat UnknownHash as skip-this-cycle in fee_rate_cache_from…
randomlogin Apr 21, 2026
8a1aa91
fix(cbf): advance lightning-wallet sync timestamp when no scripts reg…
randomlogin Apr 21, 2026
750ef83
update kyoto version (#14)
randomlogin Apr 24, 2026
45cbfff
fix(cbf): rename BestBlock to BlockLocator after rebase onto main
febyeji Apr 29, 2026
708af85
fix(cbf): replace lock().unwrap() with expect("lock")
febyeji Apr 30, 2026
dcd77cd
Fix(cbf): stop kyoto during backoff wait (#16)
randomlogin May 7, 2026
74c8858
Change `registered_scripts` from `Vec` to `HashSet` (#18)
randomlogin May 8, 2026
0d78371
Bump kyoto version (#20)
randomlogin May 17, 2026
0181500
fix(cbf): stop chain source before waiting on tasks
febyeji May 22, 2026
2bcc7c0
Add fee rate cache for cbf fee source (#21)
randomlogin May 25, 2026
ad2c021
fix(cbf): break wallet chain source reference cycle (#23)
febyeji May 28, 2026
31e867c
fix(cbf): adapt electrum fee source to latest client
febyeji Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ jobs:
- name: Test on Rust ${{ matrix.toolchain }}
if: "matrix.platform != 'windows-latest'"
run: |
RUSTFLAGS="--cfg no_download --cfg cycle_tests" cargo test
RUSTFLAGS="--cfg no_download --cfg cycle_tests" cargo test -- --skip cbf
- name: Test CBF on Rust ${{ matrix.toolchain }}
if: "matrix.platform != 'windows-latest'"
run: |
RUSTFLAGS="--cfg no_download --cfg cycle_tests" cargo test cbf -- --test-threads=1
- name: Test with UniFFI support on Rust ${{ matrix.toolchain }}
if: "matrix.platform != 'windows-latest' && matrix.build-uniffi"
run: |
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightn
bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] }
bdk_esplora = { version = "0.22.0", default-features = false, features = ["async-https-rustls", "tokio"]}
bdk_electrum = { version = "0.24.0", default-features = false, features = ["use-rustls-ring"]}
bip157 = { version = "0.6.0", default-features = false }
bdk_wallet = { version = "2.3.0", default-features = false, features = ["std", "keys-bip39"]}

bitreq = { version = "0.3", default-features = false, features = ["async-https", "json-using-serde"] }
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ fn main() {
LDK Node currently comes with a decidedly opinionated set of design choices:

- On-chain data is handled by the integrated [BDK][bdk] wallet.
- Chain data may currently be sourced from the Bitcoin Core RPC interface, or from an [Electrum][electrum] or [Esplora][esplora] server.
- Chain data may currently be sourced from the Bitcoin Core RPC interface, from an [Electrum][electrum] or [Esplora][esplora] server, or via [compact block filters (BIP 157)][bip157].
- Wallet and channel state may be persisted to an [SQLite][sqlite] database, to file system, or to a custom back-end to be implemented by the user.
- Gossip data may be sourced via Lightning's peer-to-peer network or the [Rapid Gossip Sync](https://docs.rs/lightning-rapid-gossip-sync/*/lightning_rapid_gossip_sync/) protocol.
- Entropy for the Lightning and on-chain wallets may be sourced from raw bytes or a [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) mnemonic. In addition, LDK Node offers the means to generate and persist the entropy bytes to disk.
Expand All @@ -80,6 +80,7 @@ The Minimum Supported Rust Version (MSRV) is currently 1.85.0.
[bdk]: https://bitcoindevkit.org/
[electrum]: https://github.com/spesmilo/electrum-protocol
[esplora]: https://github.com/Blockstream/esplora
[bip157]: https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki
[sqlite]: https://sqlite.org/
[rust]: https://www.rust-lang.org/
[swift]: https://www.swift.org/
Expand Down
5 changes: 5 additions & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ typedef dictionary EsploraSyncConfig;

typedef dictionary ElectrumSyncConfig;

typedef dictionary CbfSyncConfig;

typedef dictionary TorConfig;

typedef interface NodeEntropy;
Expand Down Expand Up @@ -38,6 +40,7 @@ interface Builder {
constructor(Config config);
void set_chain_source_esplora(string server_url, EsploraSyncConfig? config);
void set_chain_source_electrum(string server_url, ElectrumSyncConfig? config);
void set_chain_source_cbf(sequence<string> peers, CbfSyncConfig? sync_config, FeeSourceConfig? fee_source_config);
void set_chain_source_bitcoind_rpc(string rpc_host, u16 rpc_port, string rpc_user, string rpc_password);
void set_chain_source_bitcoind_rest(string rest_host, u16 rest_port, string rpc_host, u16 rpc_port, string rpc_user, string rpc_password);
void set_gossip_source_p2p();
Expand Down Expand Up @@ -354,6 +357,8 @@ enum Currency {

typedef enum AsyncPaymentsRole;

typedef enum FeeSourceConfig;

[Custom]
typedef string Txid;

Expand Down
100 changes: 97 additions & 3 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ use lightning::util::sweep::OutputSweeper;
use lightning_dns_resolver::OMDomainResolver;
use vss_client::headers::VssHeaderProvider;

use crate::chain::ChainSource;
use crate::chain::{ChainSource, FeeSourceConfig};
use crate::config::{
default_user_config, may_announce_channel, AnnounceError, AsyncPaymentsRole,
BitcoindRestClientConfig, Config, ElectrumSyncConfig, EsploraSyncConfig, HRNResolverConfig,
TorConfig, DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME, DEFAULT_LOG_LEVEL,
BitcoindRestClientConfig, CbfSyncConfig, Config, ElectrumSyncConfig, EsploraSyncConfig,
HRNResolverConfig, TorConfig, DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME,
DEFAULT_LOG_LEVEL,
};
use crate::connection::ConnectionManager;
use crate::entropy::NodeEntropy;
Expand Down Expand Up @@ -108,6 +109,11 @@ enum ChainDataSourceConfig {
rpc_password: String,
rest_client_config: Option<BitcoindRestClientConfig>,
},
Cbf {
peers: Vec<String>,
sync_config: Option<CbfSyncConfig>,
fee_source_config: Option<FeeSourceConfig>,
},
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -376,6 +382,28 @@ impl NodeBuilder {
self
}

/// Configures the [`Node`] instance to source its chain data via BIP 157 compact block
/// filters.
///
/// `peers` is an optional list of peer addresses to connect to for sourcing compact block
/// filters. If empty, the node will discover peers via DNS seeds.
///
/// If no `sync_config` is given, default values are used. See [`CbfSyncConfig`] for more
/// information.
///
/// Note: fee rate estimation with this chain source uses block-level averages (total fees
/// divided by block weight) rather than per-transaction fee rates. This can underestimate
/// next-block inclusion rates during periods of high mempool congestion. Percentile-based
/// target selection partially mitigates this.
pub fn set_chain_source_cbf(
&mut self, peers: Vec<String>, sync_config: Option<CbfSyncConfig>,
fee_source_config: Option<FeeSourceConfig>,
) -> &mut Self {
self.chain_data_source_config =
Some(ChainDataSourceConfig::Cbf { peers, sync_config, fee_source_config });
self
}

/// Configures the [`Node`] instance to connect to a Bitcoin Core node via RPC.
///
/// This method establishes an RPC connection that enables all essential chain operations including
Expand Down Expand Up @@ -970,6 +998,30 @@ impl ArcedNodeBuilder {
self.inner.write().expect("lock").set_chain_source_electrum(server_url, sync_config);
}

/// Configures the [`Node`] instance to source its chain data via BIP 157 compact block
/// filters.
///
/// `peers` is an optional list of peer addresses to connect to for sourcing compact block
/// filters. If empty, the node will discover peers via DNS seeds.
///
/// If no `sync_config` is given, default values are used. See [`CbfSyncConfig`] for more
/// information.
///
/// Note: fee rate estimation with this chain source uses block-level averages (total fees
/// divided by block weight) rather than per-transaction fee rates. This can underestimate
/// next-block inclusion rates during periods of high mempool congestion. Percentile-based
/// target selection partially mitigates this.
pub fn set_chain_source_cbf(
&self, peers: Vec<String>, sync_config: Option<CbfSyncConfig>,
fee_source_config: Option<FeeSourceConfig>,
) {
self.inner.write().expect("lock").set_chain_source_cbf(
peers,
sync_config,
fee_source_config,
);
}

/// Configures the [`Node`] instance to connect to a Bitcoin Core node via RPC.
///
/// This method establishes an RPC connection that enables all essential chain operations including
Expand Down Expand Up @@ -1509,6 +1561,25 @@ fn build_with_store_internal(
}),
},

Some(ChainDataSourceConfig::Cbf { peers, sync_config, fee_source_config }) => {
let sync_config = sync_config.clone().unwrap_or(CbfSyncConfig::default());
ChainSource::new_cbf(
peers.clone(),
sync_config,
fee_source_config.clone(),
Arc::clone(&fee_estimator),
Arc::clone(&tx_broadcaster),
Arc::clone(&kv_store),
Arc::clone(&config),
Arc::clone(&logger),
Arc::clone(&node_metrics),
)
.map_err(|e| {
log_error!(logger, "Failed to initialize CBF chain source: {}", e);
BuildError::ChainSourceSetupFailed
})?
},

None => {
// Default to Esplora client.
let server_url = DEFAULT_ESPLORA_SERVER_URL.to_string();
Expand Down Expand Up @@ -2261,6 +2332,9 @@ pub(crate) fn sanitize_alias(alias_str: &str) -> Result<NodeAlias, BuildError> {

#[cfg(test)]
mod tests {
#[cfg(feature = "uniffi")]
use crate::config::CbfSyncConfig;

use super::{sanitize_alias, BuildError, NodeAlias};

#[test]
Expand Down Expand Up @@ -2298,4 +2372,24 @@ mod tests {
let node = sanitize_alias(alias);
assert_eq!(node.err().unwrap(), BuildError::InvalidNodeAlias);
}

#[cfg(feature = "uniffi")]
#[test]
fn arced_builder_can_set_cbf_chain_source() {
let builder = super::ArcedNodeBuilder::new();
let sync_config = CbfSyncConfig::default();

let peers = vec!["127.0.0.1:8333".to_string()];
builder.set_chain_source_cbf(peers.clone(), Some(sync_config.clone()), None);

let guard = builder.inner.read().unwrap();
assert!(matches!(
guard.chain_data_source_config.as_ref(),
Some(super::ChainDataSourceConfig::Cbf {
peers: p,
sync_config: Some(config),
..
}) if config == &sync_config && p == &peers
));
}
}
Loading
Loading