Skip to content

Commit

Permalink
Implement fee spike reserve for channel initiators sending payments.
Browse files Browse the repository at this point in the history
From lightning-rfc PR #740.
  • Loading branch information
valentinewallace committed Apr 13, 2020
1 parent f1a57ae commit d7d24e4
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 10 deletions.
5 changes: 4 additions & 1 deletion lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3585,9 +3585,12 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
return Err(ChannelError::Ignore("Cannot send value that would put us over the max HTLC value in flight our peer will accept"));
}

// Add additional reserve that avoids stuck channels in the case of fee spikes.
let fee_spike_reserve = if self.channel_outbound { 2 * self.feerate_per_kw } else { 0 };

// Check self.their_channel_reserve_satoshis (the amount we must keep as
// reserve for them to have something to claim if we misbehave)
if self.value_to_self_msat < self.their_channel_reserve_satoshis * 1000 + amount_msat + htlc_outbound_value_msat + self.commit_tx_fee_outbound_htlc() {
if self.value_to_self_msat < self.their_channel_reserve_satoshis * 1000 + amount_msat + htlc_outbound_value_msat + self.commit_tx_fee_outbound_htlc() + fee_spike_reserve {
return Err(ChannelError::Ignore("Cannot send value that would put us over their reserve value"));
}

Expand Down
22 changes: 13 additions & 9 deletions lightning/src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1522,7 +1522,6 @@ fn test_duplicate_htlc_different_direction_onchain() {
}

fn do_channel_reserve_test(test_recv: bool) {

let chanmon_cfgs = create_chanmon_cfgs(3);
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
Expand Down Expand Up @@ -1556,6 +1555,7 @@ fn do_channel_reserve_test(test_recv: bool) {

let feemsat = 239; // somehow we know?
let total_fee_msat = (nodes.len() - 2) as u64 * 239;
let fee_spike_reserve = 2 * get_feerate!(nodes[0], chan_1.2);

let recv_value_0 = stat01.their_max_htlc_value_in_flight_msat - total_fee_msat;

Expand All @@ -1577,7 +1577,7 @@ fn do_channel_reserve_test(test_recv: bool) {
// nodes[0]'s wealth
loop {
let amt_msat = recv_value_0 + total_fee_msat;
if stat01.value_to_self_msat - amt_msat < stat01.channel_reserve_msat + stat01.commit_tx_fee_outbound {
if stat01.value_to_self_msat - amt_msat < stat01.channel_reserve_msat + stat01.commit_tx_fee_outbound + fee_spike_reserve {
break;
}
send_payment(&nodes[0], &vec![&nodes[1], &nodes[2]][..], recv_value_0, recv_value_0);
Expand All @@ -1598,7 +1598,7 @@ fn do_channel_reserve_test(test_recv: bool) {
}

{
let recv_value = stat01.value_to_self_msat - stat01.channel_reserve_msat - total_fee_msat - stat01.commit_tx_fee_outbound;
let recv_value = stat01.value_to_self_msat - stat01.channel_reserve_msat - total_fee_msat - stat01.commit_tx_fee_outbound - fee_spike_reserve;
// attempt to get channel_reserve violation
let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value + 1);
let err = nodes[0].node.send_payment(route.clone(), our_payment_hash).err().unwrap();
Expand All @@ -1612,7 +1612,7 @@ fn do_channel_reserve_test(test_recv: bool) {

// adding pending output
let commit_tx_fee_1_acked_htlc = get_feerate!(nodes[0], chan_1.2) * COMMITMENT_TX_WEIGHT_PER_HTLC;
let recv_value_1 = (stat01.value_to_self_msat - stat01.channel_reserve_msat - total_fee_msat - stat01.commit_tx_fee_outbound - commit_tx_fee_1_acked_htlc)/2;
let recv_value_1 = (stat01.value_to_self_msat - stat01.channel_reserve_msat - total_fee_msat - stat01.commit_tx_fee_outbound - commit_tx_fee_1_acked_htlc - fee_spike_reserve)/2;
let amt_msat_1 = recv_value_1 + total_fee_msat;

let (route_1, our_payment_hash_1, our_payment_preimage_1) = get_route_and_payment_hash!(recv_value_1);
Expand All @@ -1627,7 +1627,7 @@ fn do_channel_reserve_test(test_recv: bool) {
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event_1.msgs[0]);

// channel reserve test with htlc pending output > 0
let recv_value_2 = stat01.value_to_self_msat - amt_msat_1 - stat01.channel_reserve_msat - total_fee_msat - stat01.commit_tx_fee_outbound - commit_tx_fee_1_acked_htlc;
let recv_value_2 = stat01.value_to_self_msat - amt_msat_1 - stat01.channel_reserve_msat - total_fee_msat - stat01.commit_tx_fee_outbound - commit_tx_fee_1_acked_htlc - fee_spike_reserve;
{
let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_2 + 1);
match nodes[0].node.send_payment(route, our_payment_hash).err().unwrap() {
Expand Down Expand Up @@ -1684,7 +1684,7 @@ fn do_channel_reserve_test(test_recv: bool) {
let recv_value_22 = recv_value_2 - recv_value_21 - total_fee_msat - commit_tx_fee_1_holding_cell_htlc;
{
let stat = get_channel_value_stat!(nodes[0], chan_1.2);
assert_eq!(stat.value_to_self_msat - (stat.pending_outbound_htlcs_amount_msat + recv_value_21 + recv_value_22 + total_fee_msat + total_fee_msat + commit_tx_fee_1_holding_cell_htlc + commit_tx_fee_1_acked_htlc + stat01.commit_tx_fee_outbound), stat.channel_reserve_msat);
assert_eq!(stat.value_to_self_msat - (stat.pending_outbound_htlcs_amount_msat + recv_value_21 + recv_value_22 + total_fee_msat + total_fee_msat + commit_tx_fee_1_holding_cell_htlc + commit_tx_fee_1_acked_htlc + stat01.commit_tx_fee_outbound + fee_spike_reserve), stat.channel_reserve_msat);
}

// now see if they go through on both sides
Expand Down Expand Up @@ -1794,7 +1794,7 @@ fn do_channel_reserve_test(test_recv: bool) {
let expected_value_to_self = stat01.value_to_self_msat - (recv_value_1 + total_fee_msat) - (recv_value_21 + total_fee_msat) - (recv_value_22 + total_fee_msat) - (recv_value_3 + total_fee_msat);
let stat0 = get_channel_value_stat!(nodes[0], chan_1.2);
assert_eq!(stat0.value_to_self_msat, expected_value_to_self);
assert_eq!(stat0.value_to_self_msat, stat0.channel_reserve_msat + stat0.commit_tx_fee_outbound);
assert_eq!(stat0.value_to_self_msat, stat0.channel_reserve_msat + stat0.commit_tx_fee_outbound + fee_spike_reserve);

let stat2 = get_channel_value_stat!(nodes[2], chan_2.2);
assert_eq!(stat2.value_to_self_msat, stat22.value_to_self_msat + recv_value_1 + recv_value_21 + recv_value_22 + recv_value_3);
Expand Down Expand Up @@ -5973,15 +5973,19 @@ fn test_update_add_htlc_bolt2_receiver_sender_can_afford_amount_sent() {
let chan_stat = get_channel_value_stat!(nodes[0], chan.2);
let their_channel_reserve = chan_stat.channel_reserve_msat;
let commit_tx_fee = chan_stat.commit_tx_fee_outbound;
let fee_spike_reserve = 2 * get_feerate!(nodes[0], chan.2);

let max_can_send = 5000000 - their_channel_reserve - commit_tx_fee;
let max_can_send = 5000000 - their_channel_reserve - commit_tx_fee - fee_spike_reserve;
let route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &[], max_can_send, TEST_FINAL_CLTV).unwrap();
let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]);
nodes[0].node.send_payment(route, our_payment_hash).unwrap();
check_added_monitors!(nodes[0], 1);
let mut updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());

updates.update_add_htlcs[0].amount_msat = max_can_send + 1;
// Even though channel-initiator senders are required to respect the fee_spike_reserve,
// at this time channel-initiatee receivers are not required to enforce that senders
// respect the fee_spike_reserve.
updates.update_add_htlcs[0].amount_msat = max_can_send + fee_spike_reserve + 1;
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]);

assert!(nodes[1].node.list_channels().is_empty());
Expand Down

0 comments on commit d7d24e4

Please sign in to comment.