Skip to content

Commit 6e7000c

Browse files
committed
Set channel_update disable bit based on staged even for onions
When generating a `channel_update` either in response to a fee configuration change or an HTLC failure, we currently poll the channel to check if the peer's connected when setting the disabled bit in the `channel_update`. This could cause cases where we set the disable bit even though the peer *just* disconnected, and don't generate a followup broadcast `channel_update` with the disabled bit unset. While a node generally shouldn't rebroadcast a `channel_update` it received in an onion, there's nothing inherently stopping them from doing so. Obviously in the fee-update case we expect the message to propagate. Luckily, since we already "stage" disable-changed updates, we can check the staged state and use that to set the disabled bit in all `channel_update` cases.
1 parent 2ebbe6f commit 6e7000c

File tree

3 files changed

+29
-8
lines changed

3 files changed

+29
-8
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,7 +2457,14 @@ where
24572457
// hopefully an attacker trying to path-trace payments cannot make this occur
24582458
// on a small/per-node/per-channel scale.
24592459
if !chan.is_live() { // channel_disabled
2460-
break Some(("Forwarding channel is not in a ready state.", 0x1000 | 20, chan_update_opt));
2460+
// If the channel_update we're going to return is disabled (i.e. the
2461+
// peer has been disabled for some time), return `channel_disabled`,
2462+
// otherwise return `temporary_channel_failure`.
2463+
if chan_update_opt.as_ref().map(|u| u.contents.flags & 2 == 2).unwrap_or(false) {
2464+
break Some(("Forwarding channel has been disconnected for some time.", 0x1000 | 20, chan_update_opt));
2465+
} else {
2466+
break Some(("Forwarding channel is not in a ready state.", 0x1000 | 7, chan_update_opt));
2467+
}
24612468
}
24622469
if *outgoing_amt_msat < chan.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
24632470
break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, chan_update_opt));
@@ -2582,11 +2589,18 @@ where
25822589
log_trace!(self.logger, "Generating channel update for channel {}", log_bytes!(chan.channel_id()));
25832590
let were_node_one = self.our_network_pubkey.serialize()[..] < chan.get_counterparty_node_id().serialize()[..];
25842591

2592+
let enabled = chan.is_usable() && match chan.channel_update_status() {
2593+
ChannelUpdateStatus::Enabled => true,
2594+
ChannelUpdateStatus::DisabledStaged => true,
2595+
ChannelUpdateStatus::Disabled => false,
2596+
ChannelUpdateStatus::EnabledStaged => false,
2597+
};
2598+
25852599
let unsigned = msgs::UnsignedChannelUpdate {
25862600
chain_hash: self.genesis_hash,
25872601
short_channel_id,
25882602
timestamp: chan.get_update_time_counter(),
2589-
flags: (!were_node_one) as u8 | ((!chan.is_live() as u8) << 1),
2603+
flags: (!were_node_one) as u8 | ((!enabled as u8) << 1),
25902604
cltv_expiry_delta: chan.get_cltv_expiry_delta(),
25912605
htlc_minimum_msat: chan.get_counterparty_htlc_minimum_msat(),
25922606
htlc_maximum_msat: chan.get_announced_htlc_max_msat(),
@@ -3741,22 +3755,22 @@ where
37413755
ChannelUpdateStatus::DisabledStaged if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Enabled),
37423756
ChannelUpdateStatus::EnabledStaged if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Disabled),
37433757
ChannelUpdateStatus::DisabledStaged if !chan.is_live() => {
3758+
chan.set_channel_update_status(ChannelUpdateStatus::Disabled);
37443759
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
37453760
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
37463761
msg: update
37473762
});
37483763
}
37493764
should_persist = NotifyOption::DoPersist;
3750-
chan.set_channel_update_status(ChannelUpdateStatus::Disabled);
37513765
},
37523766
ChannelUpdateStatus::EnabledStaged if chan.is_live() => {
3767+
chan.set_channel_update_status(ChannelUpdateStatus::Enabled);
37533768
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
37543769
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
37553770
msg: update
37563771
});
37573772
}
37583773
should_persist = NotifyOption::DoPersist;
3759-
chan.set_channel_update_status(ChannelUpdateStatus::Enabled);
37603774
},
37613775
_ => {},
37623776
}

lightning/src/ln/functional_test_utils.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2724,10 +2724,9 @@ macro_rules! handle_chan_reestablish_msgs {
27242724
}
27252725

27262726
let mut had_channel_update = false; // ChannelUpdate may be now or later, but not both
2727-
if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, ref msg }) = msg_events.get(idx) {
2727+
if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, .. }) = msg_events.get(idx) {
27282728
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
27292729
idx += 1;
2730-
assert_eq!(msg.contents.flags & 2, 0); // "disabled" flag must not be set as we just reconnected.
27312730
had_channel_update = true;
27322731
}
27332732

@@ -2771,10 +2770,9 @@ macro_rules! handle_chan_reestablish_msgs {
27712770
}
27722771
}
27732772

2774-
if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, ref msg }) = msg_events.get(idx) {
2773+
if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, .. }) = msg_events.get(idx) {
27752774
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
27762775
idx += 1;
2777-
assert_eq!(msg.contents.flags & 2, 0); // "disabled" flag must not be set as we just reconnected.
27782776
assert!(!had_channel_update);
27792777
}
27802778

lightning/src/ln/onion_route_tests.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,15 @@ fn test_onion_failure() {
587587
// disconnect event to the channel between nodes[1] ~ nodes[2]
588588
nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id());
589589
nodes[2].node.peer_disconnected(&nodes[1].node.get_our_node_id());
590+
}, true, Some(UPDATE|7), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy(short_channel_id)}), Some(short_channel_id));
591+
run_onion_failure_test("channel_disabled", 0, &nodes, &route, &payment_hash, &payment_secret, |_| {}, || {
592+
// Tick the timer twice on each node to mark the channel as disabled.
593+
nodes[1].node.timer_tick_occurred();
594+
nodes[1].node.timer_tick_occurred();
595+
nodes[1].node.get_and_clear_pending_msg_events();
596+
nodes[2].node.timer_tick_occurred();
597+
nodes[2].node.timer_tick_occurred();
598+
nodes[2].node.get_and_clear_pending_msg_events();
590599
}, true, Some(UPDATE|20), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy(short_channel_id)}), Some(short_channel_id));
591600
reconnect_nodes(&nodes[1], &nodes[2], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
592601

0 commit comments

Comments
 (0)