Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c1b755c
Add BIP 157 compact block filter chain source
febyeji Mar 25, 2026
29ce67b
Add CBF integration tests and documentation
febyeji Mar 25, 2026
c64a068
Add optional fee source from esplora/electrum
randomlogin Mar 25, 2026
6570862
Remove race condition in test unified_send_receive
randomlogin Mar 26, 2026
87e1514
Fix CBF chain source build errors and UniFFI bindings (#5)
febyeji Mar 27, 2026
6fdeb4e
Remove last_synced_height from cbf
randomlogin Apr 1, 2026
67816f5
feat(cbf):
febyeji Apr 6, 2026
64270bd
test(cbf): strengthen wait_for_cbf_sync with state verification
febyeji Apr 6, 2026
70459e7
Add test to capture wallet checkpoint push problems
randomlogin Apr 1, 2026
fd2a40b
fix(cbf): use CheckPoint::insert for reorg-aware wallet sync
randomlogin Apr 20, 2026
8ab36b4
remove latest_tip from the cbf chain source
randomlogin Apr 21, 2026
edd4867
fix(cbf): treat UnknownHash as skip-this-cycle in fee_rate_cache_from…
randomlogin Apr 21, 2026
24231dc
fix(cbf): advance lightning-wallet sync timestamp when no scripts reg…
randomlogin Apr 21, 2026
471343c
update kyoto version (#14)
randomlogin Apr 24, 2026
2e1c8dd
fix(cbf): rename BestBlock to BlockLocator after rebase onto main
febyeji Apr 29, 2026
5fb40ef
fix(cbf): replace lock().unwrap() with expect("lock")
febyeji Apr 30, 2026
ed174c7
Fix(cbf): stop kyoto during backoff wait (#16)
randomlogin May 7, 2026
d1030d8
Change `registered_scripts` from `Vec` to `HashSet` (#18)
randomlogin May 8, 2026
9bb9e1c
Bump kyoto version (#20)
randomlogin May 17, 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 @@ -56,6 +56,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.23.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 @@ -352,6 +355,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 @@ -46,11 +46,12 @@ use lightning_dns_resolver::OMDomainResolver;
use lightning_persister::fs_store::v1::FilesystemStore;
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 @@ -916,6 +944,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 @@ -1403,6 +1455,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 @@ -2155,6 +2226,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 @@ -2192,4 +2266,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