Skip to content

Commit 4685486

Browse files
committed
Add configuration support for 'their_channel_reserve_satoshis' while channel open/accept [lightningdevkit#1498]
`their_channel_reserve_satoshis` is the minimum amount that the other node is to keep as a direct payment. This ensures that if our counterparty broadcasts a revoked state, we can punish them by claiming at least this value on chain.
1 parent 5cca9a0 commit 4685486

File tree

3 files changed

+118
-24
lines changed

3 files changed

+118
-24
lines changed

lightning/src/ln/channel.rs

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use io;
4646
use prelude::*;
4747
use core::{cmp,mem,fmt};
4848
use core::ops::Deref;
49+
use std::ops::Mul;
4950
#[cfg(any(test, fuzzing, debug_assertions))]
5051
use sync::Mutex;
5152
use bitcoin::hashes::hex::ToHex;
@@ -795,6 +796,8 @@ pub const MAX_CHAN_DUST_LIMIT_SATOSHIS: u64 = MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS
795796
/// See https://github.com/lightning/bolts/issues/905 for more details.
796797
pub const MIN_CHAN_DUST_LIMIT_SATOSHIS: u64 = 354;
797798

799+
pub const MIN_THEIR_CHAN_RESERVE_SATOSHIS: u64 = 1000;
800+
798801
/// Used to return a simple Error back to ChannelManager. Will get converted to a
799802
/// msgs::ErrorAction::SendErrorMessage or msgs::ErrorAction::IgnoreError as appropriate with our
800803
/// channel_id in ChannelManager.
@@ -843,16 +846,24 @@ impl<Signer: Sign> Channel<Signer> {
843846
}
844847

845848
/// Returns a minimum channel reserve value the remote needs to maintain,
846-
/// required by us.
849+
/// required by us according to configured or default `their_channel_reserve_proportion_millionth`
847850
///
848851
/// Guaranteed to return a value no larger than channel_value_satoshis
849852
///
850-
/// This is used both for new channels and to figure out what reserve value we sent to peers
851-
/// for channels serialized before we included our selected reserve value in the serialized
852-
/// data explicitly.
853-
pub(crate) fn get_holder_selected_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 {
853+
/// This is used both for outgoing and incoming channels and has lower bound of `MIN_THEIR_CHAN_RESERVE_SATOSHIS`
854+
pub(crate) fn get_holder_selected_channel_reserve_satoshis(channel_value_satoshis: u64, config: &UserConfig) -> u64 {
855+
let their_channel_reserve_proportion = (config.channel_handshake_config.their_channel_reserve_proportion_millionth as f64) / 1000_000.0;
856+
let calculated_reserve = their_channel_reserve_proportion.mul(channel_value_satoshis as f64) as u64;
857+
cmp::min(channel_value_satoshis, cmp::max(calculated_reserve, MIN_THEIR_CHAN_RESERVE_SATOSHIS))
858+
}
859+
860+
/// This is for legacy reasons, present for forward-compatibility. LDK versions older than " < 0.0.104"
861+
/// don't know how read/handle values other than default from storage. Hence we use this function
862+
/// to not persist default values of `holder_selected_channel_reserve_satoshis` for channels
863+
/// into storage.
864+
pub(crate) fn get_legacy_default_holder_selected_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 {
854865
let (q, _) = channel_value_satoshis.overflowing_div(100);
855-
cmp::min(channel_value_satoshis, cmp::max(q, 1000)) //TODO
866+
cmp::min(channel_value_satoshis, cmp::max(q, 1000))
856867
}
857868

858869
pub(crate) fn opt_anchors(&self) -> bool {
@@ -912,7 +923,7 @@ impl<Signer: Sign> Channel<Signer> {
912923
if holder_selected_contest_delay < BREAKDOWN_TIMEOUT {
913924
return Err(APIError::APIMisuseError {err: format!("Configured with an unreasonable our_to_self_delay ({}) putting user funds at risks", holder_selected_contest_delay)});
914925
}
915-
let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis);
926+
let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis, config);
916927
if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
917928
return Err(APIError::APIMisuseError { err: format!("Holder selected channel reserve below implemention limit dust_limit_satoshis {}", holder_selected_channel_reserve_satoshis) });
918929
}
@@ -1204,12 +1215,12 @@ impl<Signer: Sign> Channel<Signer> {
12041215
}
12051216
}
12061217

1207-
let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis);
1218+
let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis, config);
12081219
if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
12091220
return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). dust_limit_satoshis is ({}).", holder_selected_channel_reserve_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
12101221
}
12111222
if holder_selected_channel_reserve_satoshis * 1000 >= full_channel_value_msat {
1212-
return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). Channel value is ({} - {}).", holder_selected_channel_reserve_satoshis, full_channel_value_msat, msg.push_msat)));
1223+
return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). Channel value is ({} - {}).", holder_selected_channel_reserve_satoshis * 1000, full_channel_value_msat, msg.push_msat)));
12131224
}
12141225
if msg.channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
12151226
log_debug!(logger, "channel_reserve_satoshis ({}) is smaller than our dust limit ({}). We can broadcast stale states without any risk, implying this channel is very insecure for our counterparty.",
@@ -6106,7 +6117,7 @@ impl<Signer: Sign> Writeable for Channel<Signer> {
61066117
// a different percentage of the channel value then 10%, which older versions of LDK used
61076118
// to set it to before the percentage was made configurable.
61086119
let serialized_holder_selected_reserve =
6109-
if self.holder_selected_channel_reserve_satoshis != Self::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis)
6120+
if self.holder_selected_channel_reserve_satoshis != Self::get_legacy_default_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis)
61106121
{ Some(self.holder_selected_channel_reserve_satoshis) } else { None };
61116122

61126123
let mut old_max_in_flight_percent_config = UserConfig::default().channel_handshake_config;
@@ -6381,7 +6392,7 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel<Signer>
63816392
let mut announcement_sigs = None;
63826393
let mut target_closing_feerate_sats_per_kw = None;
63836394
let mut monitor_pending_finalized_fulfills = Some(Vec::new());
6384-
let mut holder_selected_channel_reserve_satoshis = Some(Self::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis));
6395+
let mut holder_selected_channel_reserve_satoshis = Some(Self::get_legacy_default_holder_selected_channel_reserve_satoshis(channel_value_satoshis));
63856396
let mut holder_max_htlc_value_in_flight_msat = Some(Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis, &UserConfig::default().channel_handshake_config));
63866397
// Prior to supporting channel type negotiation, all of our channels were static_remotekey
63876398
// only, so we default to that if none was written.
@@ -6561,6 +6572,7 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel<Signer>
65616572

65626573
#[cfg(test)]
65636574
mod tests {
6575+
use std::cmp;
65646576
use bitcoin::blockdata::script::{Script, Builder};
65656577
use bitcoin::blockdata::transaction::{Transaction, TxOut};
65666578
use bitcoin::blockdata::constants::genesis_block;
@@ -6570,7 +6582,7 @@ mod tests {
65706582
use ln::PaymentHash;
65716583
use ln::channelmanager::{HTLCSource, PaymentId};
65726584
use ln::channel::{Channel, InboundHTLCOutput, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator};
6573-
use ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS};
6585+
use ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS};
65746586
use ln::features::{InitFeatures, ChannelTypeFeatures};
65756587
use ln::msgs::{ChannelUpdate, DataLossProtect, DecodeError, OptionalField, UnsignedChannelUpdate};
65766588
use ln::script::ShutdownScript;
@@ -6962,6 +6974,63 @@ mod tests {
69626974
assert_eq!(chan_8.holder_max_htlc_value_in_flight_msat, chan_8_value_msat);
69636975
}
69646976

6977+
#[test]
6978+
fn test_configured_holder_selected_channel_reserve_satoshis() {
6979+
6980+
// Test that `new_outbound` and `new_from_req` creates a channel with the correct value for
6981+
// `their_channel_reserve_proportion`, when configured with a valid percentage value,
6982+
// which is set to (2%) of the `channel_value` and is greater than lower bound `MIN_THEIR_CHAN_RESERVE_SATOSHIS`.
6983+
test_self_and_counterparty_channel_reserve(10000000, 0.02, 0.02);
6984+
6985+
// Test with valid but unreasonably high channel reserve
6986+
// Requesting and accepting party have requested for 49%-49% and 60%-40% channel reserve
6987+
test_self_and_counterparty_channel_reserve(10000000, 0.49, 0.49);
6988+
test_self_and_counterparty_channel_reserve(10000000, 0.60, 0.30);
6989+
6990+
// Test with calculated channel_reserve less than lower bound i.e `MIN_THEIR_CHAN_RESERVE_SATOSHIS`
6991+
test_self_and_counterparty_channel_reserve(100000, 0.00002, 0.30);
6992+
6993+
// Test with invalid channel reserves since sum of both is greater than total value of channel
6994+
test_self_and_counterparty_channel_reserve(10000000, 0.50, 0.50);
6995+
test_self_and_counterparty_channel_reserve(10000000, 0.60, 0.50);
6996+
}
6997+
6998+
fn test_self_and_counterparty_channel_reserve(channel_value_satoshis: u64, outbound_selected_channel_reserve_perc: f64, inbound_selected_channel_reserve_perc: f64) {
6999+
let fee_est = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000});
7000+
let logger = test_utils::TestLogger::new();
7001+
let secp_ctx = Secp256k1::new();
7002+
let seed = [42; 32];
7003+
let network = Network::Testnet;
7004+
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
7005+
let outbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
7006+
let inbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
7007+
7008+
7009+
let mut outbound_node_config = UserConfig::default();
7010+
outbound_node_config.channel_handshake_config.their_channel_reserve_proportion_millionth = (outbound_selected_channel_reserve_perc * 1000_000.0) as u32;
7011+
let chan = Channel::<EnforcingSigner>::new_outbound(&&fee_est, &&keys_provider, outbound_node_id, &InitFeatures::known(), channel_value_satoshis, 100000, 42, &outbound_node_config, 0, 42).unwrap();
7012+
7013+
let expected_outbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.channel_value_satoshis as f64 * outbound_selected_channel_reserve_perc) as u64);
7014+
assert_eq!(chan.holder_selected_channel_reserve_satoshis, expected_outbound_selected_chan_reserve);
7015+
7016+
let chan_open_channel_msg = chan.get_open_channel(genesis_block(network).header.block_hash());
7017+
let mut inbound_node_config = UserConfig::default();
7018+
inbound_node_config.channel_handshake_config.their_channel_reserve_proportion_millionth = (inbound_selected_channel_reserve_perc * 1000_000.0) as u32;
7019+
7020+
if outbound_selected_channel_reserve_perc + inbound_selected_channel_reserve_perc < 1.0 {
7021+
let chan_inbound_node = Channel::<EnforcingSigner>::new_from_req(&&fee_est, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, 42).unwrap();
7022+
7023+
let expected_inbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.channel_value_satoshis as f64 * inbound_selected_channel_reserve_perc) as u64);
7024+
7025+
assert_eq!(chan_inbound_node.holder_selected_channel_reserve_satoshis, expected_inbound_selected_chan_reserve);
7026+
assert_eq!(chan_inbound_node.counterparty_selected_channel_reserve_satoshis.unwrap(), expected_outbound_selected_chan_reserve);
7027+
} else {
7028+
// Channel Negotiations failed
7029+
let result = Channel::<EnforcingSigner>::new_from_req(&&fee_est, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, 42);
7030+
assert_eq!(true, result.is_err());
7031+
}
7032+
}
7033+
69657034
#[test]
69667035
fn channel_update() {
69677036
let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000});

lightning/src/ln/functional_tests.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn test_insane_channel_opens() {
7373
// Instantiate channel parameters where we push the maximum msats given our
7474
// funding satoshis
7575
let channel_value_sat = 31337; // same as funding satoshis
76-
let channel_reserve_satoshis = Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(channel_value_sat);
76+
let channel_reserve_satoshis = Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(channel_value_sat, &cfg);
7777
let push_msat = (channel_value_sat - channel_reserve_satoshis) * 1000;
7878

7979
// Have node0 initiate a channel to node1 with aforementioned parameters
@@ -148,13 +148,14 @@ fn do_test_counterparty_no_reserve(send_from_initiator: bool) {
148148
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
149149
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
150150
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
151+
let default_config = UserConfig::default();
151152

152153
// Have node0 initiate a channel to node1 with aforementioned parameters
153154
let mut push_amt = 100_000_000;
154155
let feerate_per_kw = 253;
155156
let opt_anchors = false;
156157
push_amt -= feerate_per_kw as u64 * (commitment_tx_base_weight(opt_anchors) + 4 * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000 * 1000;
157-
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000) * 1000;
158+
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000, &default_config) * 1000;
158159

159160
let temp_channel_id = nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, if send_from_initiator { 0 } else { push_amt }, 42, None).unwrap();
160161
let mut open_channel_message = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
@@ -633,7 +634,8 @@ fn test_update_fee_that_funder_cannot_afford() {
633634
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, channel_value, push_sats * 1000, InitFeatures::known(), InitFeatures::known());
634635
let channel_id = chan.2;
635636
let secp_ctx = Secp256k1::new();
636-
let bs_channel_reserve_sats = Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(channel_value);
637+
let default_config = UserConfig::default();
638+
let bs_channel_reserve_sats = Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(channel_value, &default_config);
637639

638640
let opt_anchors = false;
639641

@@ -1520,12 +1522,13 @@ fn test_chan_reserve_violation_outbound_htlc_inbound_chan() {
15201522
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
15211523
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
15221524
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1523-
1525+
let default_config = UserConfig::default();
15241526
let opt_anchors = false;
15251527

15261528
let mut push_amt = 100_000_000;
15271529
push_amt -= commit_tx_fee_msat(feerate_per_kw, MIN_AFFORDABLE_HTLC_COUNT as u64, opt_anchors);
1528-
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000) * 1000;
1530+
1531+
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000, &default_config) * 1000;
15291532

15301533
let _ = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, push_amt, InitFeatures::known(), InitFeatures::known());
15311534

@@ -1549,15 +1552,15 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
15491552
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
15501553
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
15511554
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1552-
1555+
let default_config = UserConfig::default();
15531556
let opt_anchors = false;
15541557

15551558
// Set nodes[0]'s balance such that they will consider any above-dust received HTLC to be a
15561559
// channel reserve violation (so their balance is channel reserve (1000 sats) + commitment
15571560
// transaction fee with 0 HTLCs (183 sats)).
15581561
let mut push_amt = 100_000_000;
15591562
push_amt -= commit_tx_fee_msat(feerate_per_kw, MIN_AFFORDABLE_HTLC_COUNT as u64, opt_anchors);
1560-
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000) * 1000;
1563+
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000, &default_config) * 1000;
15611564
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, push_amt, InitFeatures::known(), InitFeatures::known());
15621565

15631566
// Send four HTLCs to cover the initial push_msat buffer we're required to include
@@ -1602,15 +1605,15 @@ fn test_chan_reserve_dust_inbound_htlcs_outbound_chan() {
16021605
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
16031606
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None, None]);
16041607
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1605-
1608+
let default_config = UserConfig::default();
16061609
let opt_anchors = false;
16071610

16081611
// Set nodes[0]'s balance such that they will consider any above-dust received HTLC to be a
16091612
// channel reserve violation (so their balance is channel reserve (1000 sats) + commitment
16101613
// transaction fee with 0 HTLCs (183 sats)).
16111614
let mut push_amt = 100_000_000;
16121615
push_amt -= commit_tx_fee_msat(feerate_per_kw, MIN_AFFORDABLE_HTLC_COUNT as u64, opt_anchors);
1613-
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000) * 1000;
1616+
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000, &default_config) * 1000;
16141617
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, push_amt, InitFeatures::known(), InitFeatures::known());
16151618

16161619
let dust_amt = crate::ln::channel::MIN_CHAN_DUST_LIMIT_SATOSHIS * 1000
@@ -1640,7 +1643,7 @@ fn test_chan_init_feerate_unaffordability() {
16401643
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
16411644
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
16421645
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1643-
1646+
let default_config = UserConfig::default();
16441647
let opt_anchors = false;
16451648

16461649
// Set the push_msat amount such that nodes[0] will not be able to afford to add even a single
@@ -1652,7 +1655,7 @@ fn test_chan_init_feerate_unaffordability() {
16521655

16531656
// During open, we don't have a "counterparty channel reserve" to check against, so that
16541657
// requirement only comes into play on the open_channel handling side.
1655-
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000) * 1000;
1658+
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000, &default_config) * 1000;
16561659
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, push_amt, 42, None).unwrap();
16571660
let mut open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
16581661
open_channel_msg.push_msat += 1;
@@ -1771,10 +1774,11 @@ fn test_inbound_outbound_capacity_is_not_zero() {
17711774
let _ = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000, InitFeatures::known(), InitFeatures::known());
17721775
let channels0 = node_chanmgrs[0].list_channels();
17731776
let channels1 = node_chanmgrs[1].list_channels();
1777+
let default_config = UserConfig::default();
17741778
assert_eq!(channels0.len(), 1);
17751779
assert_eq!(channels1.len(), 1);
17761780

1777-
let reserve = Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100000);
1781+
let reserve = Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100000, &default_config);
17781782
assert_eq!(channels0[0].inbound_capacity_msat, 95000000 - reserve*1000);
17791783
assert_eq!(channels1[0].outbound_capacity_msat, 95000000 - reserve*1000);
17801784

0 commit comments

Comments
 (0)