diff --git a/Cargo.toml b/Cargo.toml index 539941677..ccee12669 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,17 +39,17 @@ default = [] #lightning-liquidity = { version = "0.2.0", features = ["std"] } #lightning-macros = { version = "0.2.0" } -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["std"] } -lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" } -lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["std"] } -lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" } -lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["tokio"] } -lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" } -lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" } -lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["rest-client", "rpc-client", "tokio"] } -lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } -lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["std"] } -lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6", features = ["std"] } +lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6" } +lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6", features = ["std"] } +lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6" } +lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6", features = ["tokio"] } +lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6" } +lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6" } +lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6", features = ["rest-client", "rpc-client", "tokio"] } +lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } +lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6", features = ["std"] } +lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6" } 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"]} @@ -79,13 +79,13 @@ async-trait = { version = "0.1", default-features = false } vss-client = { package = "vss-client-ng", version = "0.5" } prost = { version = "0.11.6", default-features = false} #bitcoin-payment-instructions = { version = "0.6" } -bitcoin-payment-instructions = { git = "https://github.com/joostjager/bitcoin-payment-instructions", branch = "ldk-dcf0c203e166da2348bef12b2e5eff4a250cdec7" } +bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "a7b32d5fded9bb45f73bf82e6d7187adf705171c" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winbase"] } [dev-dependencies] -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["std", "_test_utils"] } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "38a62c32454d3eac22578144c479dbf9a6d9bff6", features = ["std", "_test_utils"] } rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] } proptest = "1.0.0" regex = "1.5.6" diff --git a/src/builder.rs b/src/builder.rs index cd8cc184f..0f735feaa 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -435,7 +435,7 @@ impl NodeBuilder { /// Configures the [`Node`] instance to source inbound liquidity from the given /// [bLIP-51 / LSPS1] service. /// - /// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`]. + /// Will mark the LSP as trusted for 0-confirmation, 0-reserve channels, see [`Config::trusted_peers_0conf_0reserve`]. /// /// The given `token` will be used by the LSP to authenticate the user. /// @@ -443,8 +443,8 @@ impl NodeBuilder { pub fn set_liquidity_source_lsps1( &mut self, node_id: PublicKey, address: SocketAddress, token: Option, ) -> &mut Self { - // Mark the LSP as trusted for 0conf - self.config.trusted_peers_0conf.push(node_id.clone()); + // Mark the LSP as trusted for 0conf, 0reserve + self.config.trusted_peers_0conf_0reserve.push(node_id.clone()); let liquidity_source_config = self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default()); @@ -456,7 +456,7 @@ impl NodeBuilder { /// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given /// [bLIP-52 / LSPS2] service. /// - /// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`]. + /// Will mark the LSP as trusted for 0-confirmation, 0-reserve channels, see [`Config::trusted_peers_0conf_0reserve`]. /// /// The given `token` will be used by the LSP to authenticate the user. /// @@ -464,8 +464,8 @@ impl NodeBuilder { pub fn set_liquidity_source_lsps2( &mut self, node_id: PublicKey, address: SocketAddress, token: Option, ) -> &mut Self { - // Mark the LSP as trusted for 0conf - self.config.trusted_peers_0conf.push(node_id.clone()); + // Mark the LSP as trusted for 0conf, 0reserve + self.config.trusted_peers_0conf_0reserve.push(node_id.clone()); let liquidity_source_config = self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default()); @@ -956,7 +956,7 @@ impl ArcedNodeBuilder { /// Configures the [`Node`] instance to source inbound liquidity from the given /// [bLIP-51 / LSPS1] service. /// - /// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`]. + /// Will mark the LSP as trusted for 0-confirmation, 0-reserve channels, see [`Config::trusted_peers_0conf_0reserve`]. /// /// The given `token` will be used by the LSP to authenticate the user. /// @@ -970,7 +970,7 @@ impl ArcedNodeBuilder { /// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given /// [bLIP-52 / LSPS2] service. /// - /// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`]. + /// Will mark the LSP as trusted for 0-confirmation, 0-reserve channels, see [`Config::trusted_peers_0conf_0reserve`]. /// /// The given `token` will be used by the LSP to authenticate the user. /// diff --git a/src/config.rs b/src/config.rs index 71e4d2314..a50076550 100644 --- a/src/config.rs +++ b/src/config.rs @@ -123,7 +123,7 @@ pub(crate) const LNURL_AUTH_TIMEOUT_SECS: u64 = 15; /// | `listening_addresses` | None | /// | `announcement_addresses` | None | /// | `node_alias` | None | -/// | `trusted_peers_0conf` | [] | +/// | `trusted_peers_0conf_0reserve` | [] | /// | `probing_liquidity_limit_multiplier` | 3 | /// | `anchor_channels_config` | Some(..) | /// | `route_parameters` | None | @@ -156,12 +156,19 @@ pub struct Config { /// **Note**: We will only allow opening and accepting public channels if the `node_alias` and the /// `listening_addresses` are set. pub node_alias: Option, - /// A list of peers that we allow to establish zero confirmation channels to us. - /// - /// **Note:** Allowing payments via zero-confirmation channels is potentially insecure if the - /// funding transaction ends up never being confirmed on-chain. Zero-confirmation channels - /// should therefore only be accepted from trusted peers. - pub trusted_peers_0conf: Vec, + /// A list of peers that we trust. If a peer on this list opens a channel to us, we will + /// forward their HTLCs before any confirmations of the funding transaction (zero-conf), and + /// allow them to spend their entire balance (zero-reserve). If we open a channel to a peer + /// on this list, we will allow them to spend their entire channel balance (note that for + /// channels *we* open, the decision of whether to accept HTLC forwards with no + /// confirmations of the funding transaction is *the peer's* decision). + /// + /// **Note:** Allowing payments via zero-confirmation channels is potentially insecure if + /// the funding transaction never gets confirmed on-chain. Zero-reserve channels + /// allow the counterparty to make cheating attempts with no financial penalty. + /// Zero-confirmation, and zero-reserve channels should therefore only be accepted from and + /// opened to trusted peers. + pub trusted_peers_0conf_0reserve: Vec, /// The liquidity factor by which we filter the outgoing channels used for sending probes. /// /// Channels with available liquidity less than the required amount times this value won't be @@ -208,7 +215,7 @@ impl Default for Config { network: DEFAULT_NETWORK, listening_addresses: None, announcement_addresses: None, - trusted_peers_0conf: Vec::new(), + trusted_peers_0conf_0reserve: Vec::new(), probing_liquidity_limit_multiplier: DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER, anchor_channels_config: Some(AnchorChannelsConfig::default()), tor_config: None, diff --git a/src/event.rs b/src/event.rs index f06d701bc..e147176ea 100644 --- a/src/event.rs +++ b/src/event.rs @@ -22,7 +22,7 @@ use lightning::events::{ ReplayEvent, }; use lightning::impl_writeable_tlv_based_enum; -use lightning::ln::channelmanager::PaymentId; +use lightning::ln::channelmanager::{PaymentId, TrustedChannelFeatures}; use lightning::ln::types::ChannelId; use lightning::routing::gossip::NodeId; use lightning::sign::EntropySource; @@ -1258,7 +1258,6 @@ where let user_channel_id: u128 = u128::from_ne_bytes( self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(), ); - let allow_0conf = self.config.trusted_peers_0conf.contains(&counterparty_node_id); let mut channel_override_config = None; if let Some((lsp_node_id, _)) = self .liquidity_source @@ -1284,11 +1283,14 @@ where }); } } - let res = if allow_0conf { - self.channel_manager.accept_inbound_channel_from_trusted_peer_0conf( + let is_trusted_peer = + self.config.trusted_peers_0conf_0reserve.contains(&counterparty_node_id); + let res = if is_trusted_peer { + self.channel_manager.accept_inbound_channel_from_trusted_peer( &temporary_channel_id, &counterparty_node_id, user_channel_id, + TrustedChannelFeatures::ZeroConfZeroReserve, channel_override_config, ) } else { @@ -1305,10 +1307,10 @@ where log_info!( self.logger, "Accepting inbound{}{} channel of {}sats from{} peer {}", - if allow_0conf { " 0conf" } else { "" }, + if is_trusted_peer { " 0conf, 0reserve" } else { "" }, if anchor_channel { " Anchor" } else { "" }, funding_satoshis, - if allow_0conf { " trusted" } else { "" }, + if is_trusted_peer { " trusted" } else { "" }, counterparty_node_id, ); }, @@ -1316,10 +1318,10 @@ where log_error!( self.logger, "Error while accepting inbound{}{} channel from{} peer {}: {:?}", - if allow_0conf { " 0conf" } else { "" }, + if is_trusted_peer { " 0conf, 0reserve" } else { "" }, if anchor_channel { " Anchor" } else { "" }, counterparty_node_id, - if allow_0conf { " trusted" } else { "" }, + if is_trusted_peer { " trusted" } else { "" }, e, ); }, diff --git a/src/lib.rs b/src/lib.rs index 2e02e996c..b25b03e58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1196,27 +1196,57 @@ impl Node { self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(), ); - match self.channel_manager.create_channel( - peer_info.node_id, - channel_amount_sats, - push_msat, - user_channel_id, - None, - Some(user_config), - ) { - Ok(_) => { - log_info!( - self.logger, - "Initiated channel creation with peer {}. ", - peer_info.node_id - ); - self.peer_store.add_peer(peer_info)?; - Ok(UserChannelId(user_channel_id)) - }, - Err(e) => { - log_error!(self.logger, "Failed to initiate channel creation: {:?}", e); - Err(Error::ChannelCreationFailed) - }, + let is_trusted_peer = self.config.trusted_peers_0conf_0reserve.contains(&node_id); + if is_trusted_peer { + match self.channel_manager.create_channel_to_trusted_peer_0reserve( + peer_info.node_id, + channel_amount_sats, + push_msat, + user_channel_id, + None, + Some(user_config), + ) { + Ok(_) => { + log_info!( + self.logger, + "Initiated 0reserve channel creation with peer {}. ", + peer_info.node_id + ); + self.peer_store.add_peer(peer_info)?; + Ok(UserChannelId(user_channel_id)) + }, + Err(e) => { + log_error!( + self.logger, + "Failed to initiate 0reserve channel creation: {:?}", + e + ); + Err(Error::ChannelCreationFailed) + }, + } + } else { + match self.channel_manager.create_channel( + peer_info.node_id, + channel_amount_sats, + push_msat, + user_channel_id, + None, + Some(user_config), + ) { + Ok(_) => { + log_info!( + self.logger, + "Initiated channel creation with peer {}. ", + peer_info.node_id + ); + self.peer_store.add_peer(peer_info)?; + Ok(UserChannelId(user_channel_id)) + }, + Err(e) => { + log_error!(self.logger, "Failed to initiate channel creation: {:?}", e); + Err(Error::ChannelCreationFailed) + }, + } } } @@ -1469,12 +1499,7 @@ impl Node { let funding_template = self .channel_manager - .splice_channel( - &channel_details.channel_id, - &counterparty_node_id, - min_feerate, - max_feerate, - ) + .splice_channel(&channel_details.channel_id, &counterparty_node_id) .map_err(|e| { log_error!(self.logger, "Failed to splice channel: {:?}", e); Error::ChannelSplicingFailed @@ -1482,12 +1507,14 @@ impl Node { let contribution = self .runtime - .block_on( - funding_template - .splice_in(Amount::from_sat(splice_amount_sats), Arc::clone(&self.wallet)), - ) - .map_err(|()| { - log_error!(self.logger, "Failed to splice channel: coin selection failed"); + .block_on(funding_template.splice_in( + Amount::from_sat(splice_amount_sats), + min_feerate, + max_feerate, + Arc::clone(&self.wallet), + )) + .map_err(|e| { + log_error!(self.logger, "Failed to splice channel: {}", e); Error::ChannelSplicingFailed })?; @@ -1585,12 +1612,7 @@ impl Node { let funding_template = self .channel_manager - .splice_channel( - &channel_details.channel_id, - &counterparty_node_id, - min_feerate, - max_feerate, - ) + .splice_channel(&channel_details.channel_id, &counterparty_node_id) .map_err(|e| { log_error!(self.logger, "Failed to splice channel: {:?}", e); Error::ChannelSplicingFailed @@ -1602,9 +1624,14 @@ impl Node { }]; let contribution = self .runtime - .block_on(funding_template.splice_out(outputs, Arc::clone(&self.wallet))) - .map_err(|()| { - log_error!(self.logger, "Failed to splice channel: coin selection failed"); + .block_on(funding_template.splice_out( + outputs, + min_feerate, + max_feerate, + Arc::clone(&self.wallet), + )) + .map_err(|e| { + log_error!(self.logger, "Failed to splice channel: {}", e); Error::ChannelSplicingFailed })?; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 4f68f9825..3a50d00ae 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -381,12 +381,12 @@ macro_rules! setup_builder { pub(crate) use setup_builder; pub(crate) fn setup_two_nodes( - chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool, + chain_source: &TestChainSource, allow_0conf_0reserve: bool, anchor_channels: bool, anchors_trusted_no_reserve: bool, ) -> (TestNode, TestNode) { setup_two_nodes_with_store( chain_source, - allow_0conf, + allow_0conf_0reserve, anchor_channels, anchors_trusted_no_reserve, TestStoreType::TestSyncStore, @@ -394,7 +394,7 @@ pub(crate) fn setup_two_nodes( } pub(crate) fn setup_two_nodes_with_store( - chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool, + chain_source: &TestChainSource, allow_0conf_0reserve: bool, anchor_channels: bool, anchors_trusted_no_reserve: bool, store_type: TestStoreType, ) -> (TestNode, TestNode) { println!("== Node A =="); @@ -405,8 +405,8 @@ pub(crate) fn setup_two_nodes_with_store( println!("\n== Node B =="); let mut config_b = random_config(anchor_channels); config_b.store_type = store_type; - if allow_0conf { - config_b.node_config.trusted_peers_0conf.push(node_a.node_id()); + if allow_0conf_0reserve { + config_b.node_config.trusted_peers_0conf_0reserve.push(node_a.node_id()); } if anchor_channels && anchors_trusted_no_reserve { config_b @@ -789,8 +789,8 @@ pub async fn splice_in_with_all( } pub(crate) async fn do_channel_full_cycle( - node_a: TestNode, node_b: TestNode, bitcoind: &BitcoindClient, electrsd: &E, allow_0conf: bool, - expect_anchor_channel: bool, force_close: bool, + node_a: TestNode, node_b: TestNode, bitcoind: &BitcoindClient, electrsd: &E, + allow_0conf_0reserve: bool, expect_anchor_channel: bool, force_close: bool, ) { let addr_a = node_a.onchain_payment().new_address().unwrap(); let addr_b = node_b.onchain_payment().new_address().unwrap(); @@ -864,7 +864,7 @@ pub(crate) async fn do_channel_full_cycle( wait_for_tx(electrsd, funding_txo_a.txid).await; - if !allow_0conf { + if !allow_0conf_0reserve { generate_blocks_and_wait(&bitcoind, electrsd, 6).await; } diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 413b2d44a..8d206bcdd 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -71,7 +71,7 @@ async fn channel_full_cycle_force_close_trusted_no_reserve() { } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn channel_full_cycle_0conf() { +async fn channel_full_cycle_0conf_0reserve() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); let (node_a, node_b) = setup_two_nodes(&chain_source, true, true, false);