Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
093eefa
2026-03-29 automated rustfmt nightly
Mar 29, 2026
3aef2b3
Merge pull request #852 from lightningdevkit/create-pull-request/patch
tnull Mar 30, 2026
c442e8e
Refactor `do_connect_peer` to always propagate result to subscribers
tnull Mar 30, 2026
09e7fa4
Switch Python build backend from setuptools to hatchling
tnull Mar 30, 2026
0da793e
Replace `python_create_package.sh` with uv-based build and publish sc…
tnull Mar 30, 2026
0a7ae2b
Switch Python CI workflow to use uv
tnull Mar 30, 2026
105f835
Replace `to_socket_addrs()` with `tokio::net::lookup_host`
tnull Mar 30, 2026
a5d91b4
Harden LNURL-auth request handling
benthecarman Apr 1, 2026
140451e
Merge pull request #858 from tnull/2026-03-switch-to-uv-python-build
tnull Apr 1, 2026
463b1fc
Merge pull request #854 from tnull/2026-03-switch-to-async-lookup
tnull Apr 2, 2026
38b6122
Re-pin `idna_adapter` to for MSRV builds
tnull Apr 2, 2026
253dc13
Merge pull request #867 from tnull/2026-04-readd-idna-adapter-pin
tnull Apr 2, 2026
818cccd
Run rate limiter garbage collection before inserting new user
joostjager Apr 2, 2026
9863649
Merge pull request #866 from joostjager/rate-limiter-gc
tnull Apr 3, 2026
fadc74f
Merge pull request #862 from benthecarman/ssrf-lnurl
tnull Apr 3, 2026
856c768
feat(cbf): add BIP 157 compact block filter chain source
febyeji Apr 4, 2026
3776ad3
refactor(cbf): extract build_cbf_node helper and add wallet reference
febyeji Apr 3, 2026
dd951a7
feat(cbf): auto-restart bip157 node with exponential backoff
febyeji Apr 3, 2026
aac6061
feat(cbf): add liveness check before returning requester
febyeji Apr 3, 2026
8e097fb
fix(cbf): clean up scan state on filter scan failure
febyeji Apr 3, 2026
a138a47
fix(cbf): add per-block-fetch timeout to wallet sync
febyeji Apr 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
10 changes: 9 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ jobs:
- name: Check formatting on Rust ${{ matrix.toolchain }}
if: matrix.check-fmt
run: rustup component add rustfmt && cargo fmt --all -- --check
- name: Pin packages to allow for MSRV
if: matrix.msrv
run: |
cargo update -p idna_adapter --precise "1.2.0" --verbose # idna_adapter 1.2.1 uses ICU4X 2.2.0, requiring 1.86 and newer
- name: Set RUSTFLAGS to deny warnings
if: "matrix.toolchain == 'stable'"
run: echo "RUSTFLAGS=-D warnings" >> "$GITHUB_ENV"
Expand Down Expand Up @@ -80,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 @@ -54,6 +54,7 @@ lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning",
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.4.2", 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
98 changes: 95 additions & 3 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ use lightning::util::sweep::OutputSweeper;
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, TorConfig,
DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME, DEFAULT_LOG_LEVEL,
BitcoindRestClientConfig, CbfSyncConfig, Config, ElectrumSyncConfig, EsploraSyncConfig,
TorConfig, DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME, DEFAULT_LOG_LEVEL,
};
use crate::connection::ConnectionManager;
use crate::entropy::NodeEntropy;
Expand Down Expand Up @@ -105,6 +105,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 @@ -193,6 +198,8 @@ pub enum BuildError {
NetworkMismatch,
/// The role of the node in an asynchronous payments context is not compatible with the current configuration.
AsyncPaymentsConfigMismatch,
/// We failed to setup the chain source.
ChainSourceSetupFailed,
}

impl fmt::Display for BuildError {
Expand Down Expand Up @@ -226,6 +233,7 @@ impl fmt::Display for BuildError {
"The async payments role is not compatible with the current configuration."
)
},
Self::ChainSourceSetupFailed => write!(f, "Failed to setup chain source."),
}
}
}
Expand Down Expand Up @@ -365,6 +373,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 @@ -892,6 +922,26 @@ impl ArcedNodeBuilder {
self.inner.write().unwrap().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().unwrap().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 @@ -1364,6 +1414,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 @@ -2079,6 +2148,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 @@ -2116,4 +2188,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),
fee_source_config: None,
}) if config == &sync_config && p == &peers
));
}
}
Loading
Loading