Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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.5.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 @@ -903,6 +931,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 @@ -1390,6 +1442,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 @@ -2147,6 +2218,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 @@ -2184,4 +2258,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