Skip to content

Commit dead2f3

Browse files
authored
Merge pull request #853 from tankyleo/2026-03-zero-reserve
Enable 0conf and 0reserve on channels with trusted peers
2 parents 8cf134c + 3e99138 commit dead2f3

File tree

6 files changed

+151
-35
lines changed

6 files changed

+151
-35
lines changed

Cargo.toml

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,17 @@ default = []
3939
#lightning-liquidity = { version = "0.2.0", features = ["std"] }
4040
#lightning-macros = { version = "0.2.0" }
4141

42-
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["std"] }
43-
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" }
44-
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["std"] }
45-
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" }
46-
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["tokio"] }
47-
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" }
48-
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" }
49-
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["rest-client", "rpc-client", "tokio"] }
50-
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
51-
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["std"] }
52-
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" }
42+
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a", features = ["std"] }
43+
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a" }
44+
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a", features = ["std"] }
45+
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a" }
46+
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a", features = ["tokio"] }
47+
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a" }
48+
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a" }
49+
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a", features = ["rest-client", "rpc-client", "tokio"] }
50+
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
51+
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a", features = ["std"] }
52+
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a" }
5353

5454
bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] }
5555
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 }
7979
vss-client = { package = "vss-client-ng", version = "0.5" }
8080
prost = { version = "0.11.6", default-features = false}
8181
#bitcoin-payment-instructions = { version = "0.6" }
82-
bitcoin-payment-instructions = { git = "https://github.com/joostjager/bitcoin-payment-instructions", branch = "ldk-dcf0c203e166da2348bef12b2e5eff4a250cdec7" }
82+
bitcoin-payment-instructions = { git = "https://github.com/tankyleo/bitcoin-payment-instructions", rev = "654c25c2c1234fadf01adec1554497610f554f09" }
8383

8484
[target.'cfg(windows)'.dependencies]
8585
winapi = { version = "0.3", features = ["winbase"] }
8686

8787
[dev-dependencies]
88-
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["std", "_test_utils"] }
88+
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "688544da72cb348e4405d39a75e4d81102c1278a", features = ["std", "_test_utils"] }
8989
rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] }
9090
proptest = "1.0.0"
9191
regex = "1.5.6"

bindings/ldk_node.udl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ interface Node {
113113
[Throws=NodeError]
114114
UserChannelId open_announced_channel_with_all(PublicKey node_id, SocketAddress address, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
115115
[Throws=NodeError]
116+
UserChannelId open_0reserve_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
117+
[Throws=NodeError]
118+
UserChannelId open_0reserve_channel_with_all(PublicKey node_id, SocketAddress address, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
119+
[Throws=NodeError]
116120
void splice_in([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, u64 splice_amount_sats);
117121
[Throws=NodeError]
118122
void splice_in_with_all([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id);

src/event.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use lightning::events::{
2222
ReplayEvent,
2323
};
2424
use lightning::impl_writeable_tlv_based_enum;
25-
use lightning::ln::channelmanager::PaymentId;
25+
use lightning::ln::channelmanager::{PaymentId, TrustedChannelFeatures};
2626
use lightning::ln::types::ChannelId;
2727
use lightning::routing::gossip::NodeId;
2828
use lightning::sign::EntropySource;
@@ -1285,10 +1285,11 @@ where
12851285
}
12861286
}
12871287
let res = if allow_0conf {
1288-
self.channel_manager.accept_inbound_channel_from_trusted_peer_0conf(
1288+
self.channel_manager.accept_inbound_channel_from_trusted_peer(
12891289
&temporary_channel_id,
12901290
&counterparty_node_id,
12911291
user_channel_id,
1292+
TrustedChannelFeatures::ZeroConf,
12921293
channel_override_config,
12931294
)
12941295
} else {

src/lib.rs

Lines changed: 99 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,7 +1129,7 @@ impl Node {
11291129
fn open_channel_inner(
11301130
&self, node_id: PublicKey, address: SocketAddress, channel_amount_sats: FundingAmount,
11311131
push_to_counterparty_msat: Option<u64>, channel_config: Option<ChannelConfig>,
1132-
announce_for_forwarding: bool,
1132+
announce_for_forwarding: bool, set_0reserve: bool,
11331133
) -> Result<UserChannelId, Error> {
11341134
if !*self.is_running.read().unwrap() {
11351135
return Err(Error::NotRunning);
@@ -1197,25 +1197,46 @@ impl Node {
11971197
self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(),
11981198
);
11991199

1200-
match self.channel_manager.create_channel(
1201-
peer_info.node_id,
1202-
channel_amount_sats,
1203-
push_msat,
1204-
user_channel_id,
1205-
None,
1206-
Some(user_config),
1207-
) {
1200+
let result = if set_0reserve {
1201+
self.channel_manager.create_channel_to_trusted_peer_0reserve(
1202+
peer_info.node_id,
1203+
channel_amount_sats,
1204+
push_msat,
1205+
user_channel_id,
1206+
None,
1207+
Some(user_config),
1208+
)
1209+
} else {
1210+
self.channel_manager.create_channel(
1211+
peer_info.node_id,
1212+
channel_amount_sats,
1213+
push_msat,
1214+
user_channel_id,
1215+
None,
1216+
Some(user_config),
1217+
)
1218+
};
1219+
1220+
let zero_reserve_string = if set_0reserve { "0reserve " } else { "" };
1221+
1222+
match result {
12081223
Ok(_) => {
12091224
log_info!(
12101225
self.logger,
1211-
"Initiated channel creation with peer {}. ",
1226+
"Initiated {}channel creation with peer {}. ",
1227+
zero_reserve_string,
12121228
peer_info.node_id
12131229
);
12141230
self.peer_store.add_peer(peer_info)?;
12151231
Ok(UserChannelId(user_channel_id))
12161232
},
12171233
Err(e) => {
1218-
log_error!(self.logger, "Failed to initiate channel creation: {:?}", e);
1234+
log_error!(
1235+
self.logger,
1236+
"Failed to initiate {}channel creation: {:?}",
1237+
zero_reserve_string,
1238+
e
1239+
);
12191240
Err(Error::ChannelCreationFailed)
12201241
},
12211242
}
@@ -1291,6 +1312,7 @@ impl Node {
12911312
push_to_counterparty_msat,
12921313
channel_config,
12931314
false,
1315+
false,
12941316
)
12951317
}
12961318

@@ -1331,6 +1353,7 @@ impl Node {
13311353
push_to_counterparty_msat,
13321354
channel_config,
13331355
true,
1356+
false,
13341357
)
13351358
}
13361359

@@ -1359,6 +1382,7 @@ impl Node {
13591382
push_to_counterparty_msat,
13601383
channel_config,
13611384
false,
1385+
false,
13621386
)
13631387
}
13641388

@@ -1396,6 +1420,70 @@ impl Node {
13961420
push_to_counterparty_msat,
13971421
channel_config,
13981422
true,
1423+
false,
1424+
)
1425+
}
1426+
1427+
/// Connect to a node and open a new unannounced channel, in which the target node can
1428+
/// spend its entire balance.
1429+
///
1430+
/// This channel allows the target node to try to steal your funds with no financial
1431+
/// penalty, so this channel should only be opened to nodes you trust.
1432+
///
1433+
/// Disconnects and reconnects are handled automatically.
1434+
///
1435+
/// If `push_to_counterparty_msat` is set, the given value will be pushed (read: sent) to the
1436+
/// channel counterparty on channel open. This can be useful to start out with the balance not
1437+
/// entirely shifted to one side, therefore allowing to receive payments from the getgo.
1438+
///
1439+
/// If Anchor channels are enabled, this will ensure the configured
1440+
/// [`AnchorChannelsConfig::per_channel_reserve_sats`] is available and will be retained before
1441+
/// opening the channel.
1442+
///
1443+
/// Returns a [`UserChannelId`] allowing to locally keep track of the channel.
1444+
///
1445+
/// [`AnchorChannelsConfig::per_channel_reserve_sats`]: crate::config::AnchorChannelsConfig::per_channel_reserve_sats
1446+
pub fn open_0reserve_channel(
1447+
&self, node_id: PublicKey, address: SocketAddress, channel_amount_sats: u64,
1448+
push_to_counterparty_msat: Option<u64>, channel_config: Option<ChannelConfig>,
1449+
) -> Result<UserChannelId, Error> {
1450+
self.open_channel_inner(
1451+
node_id,
1452+
address,
1453+
FundingAmount::Exact { amount_sats: channel_amount_sats },
1454+
push_to_counterparty_msat,
1455+
channel_config,
1456+
false,
1457+
true,
1458+
)
1459+
}
1460+
1461+
/// Connect to a node and open a new unannounced channel, using all available on-chain funds
1462+
/// minus fees and anchor reserves. The target node will be able to spend its entire channel
1463+
/// balance.
1464+
///
1465+
/// This channel allows the target node to try to steal your funds with no financial
1466+
/// penalty, so this channel should only be opened to nodes you trust.
1467+
///
1468+
/// Disconnects and reconnects are handled automatically.
1469+
///
1470+
/// If `push_to_counterparty_msat` is set, the given value will be pushed (read: sent) to the
1471+
/// channel counterparty on channel open. This can be useful to start out with the balance not
1472+
/// entirely shifted to one side, therefore allowing to receive payments from the getgo.
1473+
///
1474+
/// Returns a [`UserChannelId`] allowing to locally keep track of the channel.
1475+
pub fn open_0reserve_channel_with_all(
1476+
&self, node_id: PublicKey, address: SocketAddress, push_to_counterparty_msat: Option<u64>,
1477+
channel_config: Option<ChannelConfig>,
1478+
) -> Result<UserChannelId, Error> {
1479+
self.open_channel_inner(
1480+
node_id,
1481+
address,
1482+
FundingAmount::Max,
1483+
push_to_counterparty_msat,
1484+
channel_config,
1485+
false,
1486+
true,
13991487
)
14001488
}
14011489

src/liquidity.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ pub struct LSPS2ServiceConfig {
142142
///
143143
/// [`bLIP-52`]: https://github.com/lightning/blips/blob/master/blip-0052.md#trust-models
144144
pub client_trusts_lsp: bool,
145+
/// When set, clients that we open channels to will be allowed to spend their entire channel
146+
/// balance. This allows clients to try to steal your funds with no financial penalty, so
147+
/// this should only be set if you trust your clients.
148+
pub allow_client_0reserve: bool,
145149
}
146150

147151
pub(crate) struct LiquiditySourceBuilder<L: Deref>
@@ -786,22 +790,38 @@ where
786790
config.channel_config.forwarding_fee_base_msat = 0;
787791
config.channel_config.forwarding_fee_proportional_millionths = 0;
788792

789-
match self.channel_manager.create_channel(
790-
their_network_key,
791-
channel_amount_sats,
792-
0,
793-
user_channel_id,
794-
None,
795-
Some(config),
796-
) {
793+
let result = if service_config.allow_client_0reserve {
794+
self.channel_manager.create_channel_to_trusted_peer_0reserve(
795+
their_network_key,
796+
channel_amount_sats,
797+
0,
798+
user_channel_id,
799+
None,
800+
Some(config),
801+
)
802+
} else {
803+
self.channel_manager.create_channel(
804+
their_network_key,
805+
channel_amount_sats,
806+
0,
807+
user_channel_id,
808+
None,
809+
Some(config),
810+
)
811+
};
812+
813+
match result {
797814
Ok(_) => {},
798815
Err(e) => {
799816
// TODO: We just silently fail here. Eventually we will need to remember
800817
// the pending requests and regularly retry opening the channel until we
801818
// succeed.
819+
let zero_reserve_string =
820+
if service_config.allow_client_0reserve { "0reserve " } else { "" };
802821
log_error!(
803822
self.logger,
804-
"Failed to open LSPS2 channel to {}: {:?}",
823+
"Failed to open LSPS2 {}channel to {}: {:?}",
824+
zero_reserve_string,
805825
their_network_key,
806826
e
807827
);

tests/integration_tests_rust.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,7 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) {
17051705
min_channel_opening_fee_msat: 0,
17061706
max_client_to_self_delay: 1024,
17071707
client_trusts_lsp,
1708+
allow_client_0reserve: false,
17081709
};
17091710

17101711
let service_config = random_config(true);
@@ -2023,6 +2024,7 @@ async fn lsps2_client_trusts_lsp() {
20232024
min_channel_opening_fee_msat: 0,
20242025
max_client_to_self_delay: 1024,
20252026
client_trusts_lsp: true,
2027+
allow_client_0reserve: false,
20262028
};
20272029

20282030
let service_config = random_config(true);
@@ -2197,6 +2199,7 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() {
21972199
min_channel_opening_fee_msat: 0,
21982200
max_client_to_self_delay: 1024,
21992201
client_trusts_lsp: false,
2202+
allow_client_0reserve: false,
22002203
};
22012204

22022205
let service_config = random_config(true);

0 commit comments

Comments
 (0)