Clean up and more liberally free holding cell HTLCs (without re-entrancy) - #851

Merged
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get
May 24, 2021
Merged

Clean up and more liberally free holding cell HTLCs (without re-entrancy)#851
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This is #756 reworked a decent chunk. In #756 the reentrancy story around channel_monitor_updated scared me significantly, but for some reason I apparently never bothered to really think deeply about the options. Here we move the holding cell free to get_and_clear_pending_msg_events, which is a super obvious location for it - it should be called right after channel_monitor_updated in most cases, but shouldn't have the same reentrancy issues because it already can generate a monitor update. It requires some macro rework, but its not too bad.

@TheBlueMattTheBlueMatt added this to the 0.0.14 milestone Mar 18, 2021
@codecov

codecovBot commented Mar 19, 2021

Copy link
Copy Markdown

Codecov Report

Merging #851 (34fcd99) into main (b6de281) will increase coverage by 0.08%.
The diff coverage is 80.70%.

Impacted file tree graph

@@ Coverage Diff @@## main #851 +/- ##
==========================================
+ Coverage 90.48% 90.56% +0.08% 
==========================================
Files 59 59 Lines 29896 30015 +119 ==========================================
+ Hits 27050 27184 +134 + Misses 2846 2831 -15 
Impacted FilesCoverage Δ
lightning/src/ln/functional_test_utils.rs95.06% <ø> (ø)
lightning/src/ln/channelmanager.rs82.92% <55.55%> (-0.55%)⬇️
lightning/src/ln/chanmon_update_fail_tests.rs97.77% <99.20%> (+0.13%)⬆️
lightning/src/ln/channel.rs88.29% <100.00%> (+1.04%)⬆️
lightning/src/ln/functional_tests.rs96.87% <0.00%> (-0.04%)⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b6de281...34fcd99. Read the comment docs.

Comment threadlightning/src/ln/channelmanager.rs Outdated
order = RAACommitmentOrder::RevokeAndACKFirst;
}
return_monitor_err!(self, e, channel_state, chan, order, revoke_and_ack.is_some(), commitment_update.is_some());
//TODO: Resend the funding_locked if needed once we get the monitor running again

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This todo doesn't need to be migrated to the new code, right?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thats a good question! I don't think it matters - this PR switched the ordering of message resending to resend the funding_locked before handling the monitor update, meaning we'll never need to resend because we always send (instead of maybe not sending if the monitor update fails).

The "don't send a funding_locked if we're paused pending monitor update" thing isn't really a security thing (we just expose a next_per_commitment_point which doesn't allow our counterparty to....do anything), its just kinda nice to not tell our counterparty we're ready to accept HTLCs when we're definitely not. That said, Channel::channel_reestablish doesn't really handle it all that carefully anyway, and we may end up re-sending a funding_locked on reconnect when we are still pending a monitor update failure.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 0993c08 to b14ea14CompareMarch 25, 2021 19:21

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major. This looks pretty reasonable to me!

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment on lines +2305 to +2318
if self.channel_state >= ChannelState::ChannelFunded as u32 &&
(self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)) == 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just move these checks into free_holding_cell(..)? Also, revoke_and_ack currently calls the regular free_holding_cell_htlcs(..), wonder if it should call maybe_free instead?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, we already check all the preconditions at the callsites, I suppose we could move to just calling maybe_..., but that seems more brittle, given we already check them.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), channel_id);
handle_errors.push((chan.get_counterparty_node_id(), res));
if drop { return false; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so drop == true implies that we don't need to push the UpdateHTLCs event below? Maybe could use a comment for why.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed drop to close_channel is that more clear?

}

for (counterparty_node_id, err) in handle_errors.drain(..) {
let _ = handle_error!(self, err, counterparty_node_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it matters, but if we Permanently failed to update_channel above, we may still attempt another update_channel here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, right, that's awkward, but I think also globally true - we always generate a ChannelForceClosed update for the monitor, even if the user can't persist it.

// disconnect, we may be handed some HTLCs to fail backwards here.
assert!(htlcs_to_fail.is_empty());
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), shutdown_msg));
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), htlcs_to_fail, shutdown_msg));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a general point: might we wanna think about how this logic fits into "cancelling payments" in the future? Because I could see a situation where a user attempts a payments, which gets stuck in the holding cell, and they'd prefer to attempt the payment again by a different route and cancel the existing one.

@TheBlueMattTheBlueMattMar 26, 2021

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, possibly, but I'm not very worried. The holding cell is only really intended for things that are just pending some network latency - you should update a channel monitor or receive a revoke_and_ack from your peer within some hundreds of milliseconds, at max - not for things that are going to take a while - ie its not (really) for when your counterparty is offline.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from b14ea14 to af14d44CompareMarch 26, 2021 23:11

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ACK af14d44 modulo fixing 5613be8's commit message

Comment threadlightning/src/ln/channel.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from af14d44 to 386cc60CompareMarch 31, 2021 03:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed Val's comments, full diff is:

$ git diff-tree -U1 af14d44 386cc60
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index c40e7f1c4..a2bcede85 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -2309,2 +2309,3 @@ impl<Signer: Sign> Channel<Signer> {
}
+
/// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 73ba9d9bd..b538655bd 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -731,3 +731,3 @@ macro_rules! handle_monitor_err {
ChannelMonitorUpdateErr::PermanentFailure => {
- log_error!($self.logger, "Closing channel {} due to monitor update PermanentFailure", log_bytes!($chan_id[..]));
+ log_error!($self.logger, "Closing channel {} due to monitor update ChannelMonitorUpdateErr::PermanentFailure", log_bytes!($chan_id[..]));
if let Some(short_id) = $chan.get_short_channel_id() {

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 386cc60 to 0df3461CompareApril 20, 2021 18:40
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 3 times, most recently from 478044e to 72b730cCompareApril 21, 2021 22:25
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 72b730c to ffbe950CompareApril 28, 2021 00:02
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on upstream, with pretty trivial changes.

@TheBlueMattTheBlueMatt modified the milestones: 0.0.14, 0.0.15Apr 29, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Looks like this is slipping to 0.0.15. It would be good to land some of these fixes as they are potential bugs, even if very rare.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed until e33012f included so far

Comment threadfuzz/src/chanmon_consistency.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated

let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
if chanmon_update.is_some() {
// On reconnect (or monitor restoration), we, by definition, only resend a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think mentioning monitor restoration (monitor_updating_restored ?) is confusing as this function never returns a monitor update.

I would remove it and instead in the $funding_locked branch, say something "at monitor restoration, we might resend to our counterparty funding_locked but this event doesn't generate a channel monitor update itself"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you're right. I removed the reference to monitor restoration, but I'm not sure your comment communicates enough information to later re-analyze the situation going on.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm "In case of monitor restoration, funding_locked will never be generated as this event isn't altering monitor state"?

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
let funding_broadcastable: Option<Transaction> = $funding_broadcastable; // Force type-checking to resolve
macro_rules! handle_cs { () => {
if let Some(monitor_update) = chanmon_update {
assert!($order == RAACommitmentOrder::RevokeAndACKFirst);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make my mind about correctness of this statement. It implies the path in Channel::channel_reestablish L3023, when we return htlc failures to route backward included in a new commitment transaction ? It doesn't necessary mean peer send us a commitment_signed for which we should send a RAA /

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I can't convince myself its safe anymore, I walked this bit back.

Comment threadlightning/src/ln/channelmanager.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 4 times, most recently from 0b365ec to 1005f07CompareMay 13, 2021 20:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream, relevant changes responding to comments are in fixup commits.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left few more comments, top commits sounds good to me, but want to do a second parse with fixed comments.

Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
match chan.maybe_free_holding_cell_htlcs(&self.logger) {
Ok((None, ref htlcs)) if htlcs.is_empty() => true,
Ok((commitment_opt, failed_htlcs)) => {
forwarding_failed_htlcs.push((failed_htlcs, *channel_id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be backwarding_failed_htlcs here ? Per fail_holding_cell_htlcs comment, those htlcs are designated as "The HTLCs need to be failed backwards or, if they were one of our outgoing HTLCs".

Yeah likely, we should give a brush stroke at some point about the whole HTLC routing section with consistent naming and better comments.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that's confusing, the name was "htlcs which failed during forwarding" but that wasn't clear.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 2 times, most recently from 9b1aa05 to 7eaca87CompareMay 14, 2021 22:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I believe I've addressed all the comments.

The channel restoration code in channel monitor updating and peer
reconnection both do incredibly similar things, and there is
little reason to have them be separate. Sadly because they require
holding a lock with a reference to elements in the lock, its not
practical to make them utility functions, so instead we introduce
a two-step macro here which will eventually be used for both.
Because we still support pre-NLL Rust, the macro has to be in two
parts - one which runs with the channel_state lock, and one which
does not.
This mostly swaps some Vecs that can only ever contain one element
for Options.
This fails chanmon_consistency on IgnoreError error events and on
messages left over to be sent to a just-disconnected peer, which
should have been drained.
These should never appear, so consider them a fuzzer fail case.
Because of the merge between peer reconnection and channel monitor
updating channel restoration code, we now sometimes generate
(somewhat spurious) announcement signatures when restoring channel
monitor updating. This should not result in a fuzzing failure.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 365cee1 to 7fde891CompareMay 20, 2021 21:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no changes aside from two new fixup commits to respond to Jeff's comments.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I'd be more comfortable if @ariard gave a look before merging.

Comment threadlightning/src/ln/channelmanager.rs Outdated
This merges the code for restoring channel functionality between
channel monitor updating restored and peer reconnection, reducing
redundant code.
Both break_chan_entry and try_chan_entry do almost identical work,
only differing on if they `break` or `return` in response to an
error. Because we will now also need an option to do neither, we
break out the common code into a shared `convert_chan_err` macro.
If there is no pending channel update messages when monitor updating
is restored (though there may be an RAA to send), and we're
connected to our peer and not awaiting a remote RAA, we need to
free anything in our holding cell.
However, we don't want to immediately free the holding cell during
channel_monitor_updated as it presents a somewhat bug-prone case of
reentrancy:
a) it would re-enter user code around a monitor update while being
called from user code notifying us of the same monitor being
updated, making deadlocs very likely (in fact, our fuzzers
would have a bug here!),
b) the re-entrancy only occurs in a very rare case, making it
likely users will not hit it in testing, only deadlocking in
production.
Thus, we add a holding-cell-free pass over each channel in
get_and_clear_pending_msg_events. This fits up nicely with the
anticipated bug - users almost certainly need to process new
network messages immediately after monitor updating has been
restored to send messages which were not sent originally when the
monitor updating was paused.
Without this, chanmon_fail_consistency was able to find a stuck
condition where we sit on an HTLC failure in our holding cell and
don't ever handle it (at least until we have other actions to take
which empty the holding cell).
Because we may now generate a monitor update during
get_and_clear_pending_msg_events calls, we need to ensure we
re-serialize the relevant ChannelManager before attempting to
reload it, if such a monitor update occurred.
Previously, if we got disconnected from a peer while there were
HTLCs pending forwarding in the holding cell, we'd clear them and
fail them all backwards. This is largely fine, but since we now
have support for handling such HTLCs on reconnect, we might as
well not, instead relying on our timeout logic to fail them
backwards if it takes too long to forward them.
If the fuzz target is failing due to a channel force-close, the
immediately-visible error is that we're signing a stale state. This
is because the ChannelMonitorUpdateStep::ChannelForceClosed event
results in a signature in the test clone which was deserialized
using a OnlyReadsKeysInterface. Instead, we need to deserialize
using the full KeysInterface instance.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 7fde891 to 34fcd99CompareMay 21, 2021 15:10
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed and renamed one variable as @jkczyz suggested:

$ git diff-tree -U1 7fde8910 34fcd99f
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 14d9c528..9f9820c1 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -910,3 +910,3 @@ macro_rules! handle_chan_restoration_locked {
let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
- let chanmon_update_is_some = chanmon_update.is_some();
+ let chanmon_update_is_none = chanmon_update.is_none();
let res = loop {
@@ -1006,3 +1006,3 @@ macro_rules! handle_chan_restoration_locked {
- if !chanmon_update_is_some {
+ if chanmon_update_is_none {
// If there was no ChannelMonitorUpdate, we should never generate an Err in the res loop

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Gonna go ahead and take this since its been a long time. Post-merge review from @ariard would also be welcome but I'd rather not wait on it if he's busy.

@TheBlueMatt
TheBlueMatt merged commit 3a0356f into lightningdevkit:mainMay 24, 2021
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 24, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 29, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@TheBlueMatt@jkczyz@valentinewallace@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Clean up and more liberally free holding cell HTLCs (without re-entrancy) - #851

Merged
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get
May 24, 2021
Merged

Clean up and more liberally free holding cell HTLCs (without re-entrancy)#851
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This is #756 reworked a decent chunk. In #756 the reentrancy story around channel_monitor_updated scared me significantly, but for some reason I apparently never bothered to really think deeply about the options. Here we move the holding cell free to get_and_clear_pending_msg_events, which is a super obvious location for it - it should be called right after channel_monitor_updated in most cases, but shouldn't have the same reentrancy issues because it already can generate a monitor update. It requires some macro rework, but its not too bad.

@TheBlueMattTheBlueMatt added this to the 0.0.14 milestone Mar 18, 2021
@codecov

codecovBot commented Mar 19, 2021

Copy link
Copy Markdown

Codecov Report

Merging #851 (34fcd99) into main (b6de281) will increase coverage by 0.08%.
The diff coverage is 80.70%.

Impacted file tree graph

@@ Coverage Diff @@## main #851 +/- ##
==========================================
+ Coverage 90.48% 90.56% +0.08% 
==========================================
Files 59 59 Lines 29896 30015 +119 ==========================================
+ Hits 27050 27184 +134 + Misses 2846 2831 -15 
Impacted FilesCoverage Δ
lightning/src/ln/functional_test_utils.rs95.06% <ø> (ø)
lightning/src/ln/channelmanager.rs82.92% <55.55%> (-0.55%)⬇️
lightning/src/ln/chanmon_update_fail_tests.rs97.77% <99.20%> (+0.13%)⬆️
lightning/src/ln/channel.rs88.29% <100.00%> (+1.04%)⬆️
lightning/src/ln/functional_tests.rs96.87% <0.00%> (-0.04%)⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b6de281...34fcd99. Read the comment docs.

Comment threadlightning/src/ln/channelmanager.rs Outdated
order = RAACommitmentOrder::RevokeAndACKFirst;
}
return_monitor_err!(self, e, channel_state, chan, order, revoke_and_ack.is_some(), commitment_update.is_some());
//TODO: Resend the funding_locked if needed once we get the monitor running again

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This todo doesn't need to be migrated to the new code, right?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thats a good question! I don't think it matters - this PR switched the ordering of message resending to resend the funding_locked before handling the monitor update, meaning we'll never need to resend because we always send (instead of maybe not sending if the monitor update fails).

The "don't send a funding_locked if we're paused pending monitor update" thing isn't really a security thing (we just expose a next_per_commitment_point which doesn't allow our counterparty to....do anything), its just kinda nice to not tell our counterparty we're ready to accept HTLCs when we're definitely not. That said, Channel::channel_reestablish doesn't really handle it all that carefully anyway, and we may end up re-sending a funding_locked on reconnect when we are still pending a monitor update failure.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 0993c08 to b14ea14CompareMarch 25, 2021 19:21

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major. This looks pretty reasonable to me!

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment on lines +2305 to +2318
if self.channel_state >= ChannelState::ChannelFunded as u32 &&
(self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)) == 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just move these checks into free_holding_cell(..)? Also, revoke_and_ack currently calls the regular free_holding_cell_htlcs(..), wonder if it should call maybe_free instead?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, we already check all the preconditions at the callsites, I suppose we could move to just calling maybe_..., but that seems more brittle, given we already check them.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), channel_id);
handle_errors.push((chan.get_counterparty_node_id(), res));
if drop { return false; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so drop == true implies that we don't need to push the UpdateHTLCs event below? Maybe could use a comment for why.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed drop to close_channel is that more clear?

}

for (counterparty_node_id, err) in handle_errors.drain(..) {
let _ = handle_error!(self, err, counterparty_node_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it matters, but if we Permanently failed to update_channel above, we may still attempt another update_channel here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, right, that's awkward, but I think also globally true - we always generate a ChannelForceClosed update for the monitor, even if the user can't persist it.

// disconnect, we may be handed some HTLCs to fail backwards here.
assert!(htlcs_to_fail.is_empty());
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), shutdown_msg));
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), htlcs_to_fail, shutdown_msg));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a general point: might we wanna think about how this logic fits into "cancelling payments" in the future? Because I could see a situation where a user attempts a payments, which gets stuck in the holding cell, and they'd prefer to attempt the payment again by a different route and cancel the existing one.

@TheBlueMattTheBlueMattMar 26, 2021

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, possibly, but I'm not very worried. The holding cell is only really intended for things that are just pending some network latency - you should update a channel monitor or receive a revoke_and_ack from your peer within some hundreds of milliseconds, at max - not for things that are going to take a while - ie its not (really) for when your counterparty is offline.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from b14ea14 to af14d44CompareMarch 26, 2021 23:11

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ACK af14d44 modulo fixing 5613be8's commit message

Comment threadlightning/src/ln/channel.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from af14d44 to 386cc60CompareMarch 31, 2021 03:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed Val's comments, full diff is:

$ git diff-tree -U1 af14d44 386cc60
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index c40e7f1c4..a2bcede85 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -2309,2 +2309,3 @@ impl<Signer: Sign> Channel<Signer> {
}
+
/// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 73ba9d9bd..b538655bd 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -731,3 +731,3 @@ macro_rules! handle_monitor_err {
ChannelMonitorUpdateErr::PermanentFailure => {
- log_error!($self.logger, "Closing channel {} due to monitor update PermanentFailure", log_bytes!($chan_id[..]));
+ log_error!($self.logger, "Closing channel {} due to monitor update ChannelMonitorUpdateErr::PermanentFailure", log_bytes!($chan_id[..]));
if let Some(short_id) = $chan.get_short_channel_id() {

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 386cc60 to 0df3461CompareApril 20, 2021 18:40
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 3 times, most recently from 478044e to 72b730cCompareApril 21, 2021 22:25
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 72b730c to ffbe950CompareApril 28, 2021 00:02
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on upstream, with pretty trivial changes.

@TheBlueMattTheBlueMatt modified the milestones: 0.0.14, 0.0.15Apr 29, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Looks like this is slipping to 0.0.15. It would be good to land some of these fixes as they are potential bugs, even if very rare.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed until e33012f included so far

Comment threadfuzz/src/chanmon_consistency.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated

let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
if chanmon_update.is_some() {
// On reconnect (or monitor restoration), we, by definition, only resend a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think mentioning monitor restoration (monitor_updating_restored ?) is confusing as this function never returns a monitor update.

I would remove it and instead in the $funding_locked branch, say something "at monitor restoration, we might resend to our counterparty funding_locked but this event doesn't generate a channel monitor update itself"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you're right. I removed the reference to monitor restoration, but I'm not sure your comment communicates enough information to later re-analyze the situation going on.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm "In case of monitor restoration, funding_locked will never be generated as this event isn't altering monitor state"?

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
let funding_broadcastable: Option<Transaction> = $funding_broadcastable; // Force type-checking to resolve
macro_rules! handle_cs { () => {
if let Some(monitor_update) = chanmon_update {
assert!($order == RAACommitmentOrder::RevokeAndACKFirst);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make my mind about correctness of this statement. It implies the path in Channel::channel_reestablish L3023, when we return htlc failures to route backward included in a new commitment transaction ? It doesn't necessary mean peer send us a commitment_signed for which we should send a RAA /

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I can't convince myself its safe anymore, I walked this bit back.

Comment threadlightning/src/ln/channelmanager.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 4 times, most recently from 0b365ec to 1005f07CompareMay 13, 2021 20:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream, relevant changes responding to comments are in fixup commits.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left few more comments, top commits sounds good to me, but want to do a second parse with fixed comments.

Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
match chan.maybe_free_holding_cell_htlcs(&self.logger) {
Ok((None, ref htlcs)) if htlcs.is_empty() => true,
Ok((commitment_opt, failed_htlcs)) => {
forwarding_failed_htlcs.push((failed_htlcs, *channel_id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be backwarding_failed_htlcs here ? Per fail_holding_cell_htlcs comment, those htlcs are designated as "The HTLCs need to be failed backwards or, if they were one of our outgoing HTLCs".

Yeah likely, we should give a brush stroke at some point about the whole HTLC routing section with consistent naming and better comments.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that's confusing, the name was "htlcs which failed during forwarding" but that wasn't clear.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 2 times, most recently from 9b1aa05 to 7eaca87CompareMay 14, 2021 22:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I believe I've addressed all the comments.

The channel restoration code in channel monitor updating and peer
reconnection both do incredibly similar things, and there is
little reason to have them be separate. Sadly because they require
holding a lock with a reference to elements in the lock, its not
practical to make them utility functions, so instead we introduce
a two-step macro here which will eventually be used for both.
Because we still support pre-NLL Rust, the macro has to be in two
parts - one which runs with the channel_state lock, and one which
does not.
This mostly swaps some Vecs that can only ever contain one element
for Options.
This fails chanmon_consistency on IgnoreError error events and on
messages left over to be sent to a just-disconnected peer, which
should have been drained.
These should never appear, so consider them a fuzzer fail case.
Because of the merge between peer reconnection and channel monitor
updating channel restoration code, we now sometimes generate
(somewhat spurious) announcement signatures when restoring channel
monitor updating. This should not result in a fuzzing failure.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 365cee1 to 7fde891CompareMay 20, 2021 21:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no changes aside from two new fixup commits to respond to Jeff's comments.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I'd be more comfortable if @ariard gave a look before merging.

Comment threadlightning/src/ln/channelmanager.rs Outdated
This merges the code for restoring channel functionality between
channel monitor updating restored and peer reconnection, reducing
redundant code.
Both break_chan_entry and try_chan_entry do almost identical work,
only differing on if they `break` or `return` in response to an
error. Because we will now also need an option to do neither, we
break out the common code into a shared `convert_chan_err` macro.
If there is no pending channel update messages when monitor updating
is restored (though there may be an RAA to send), and we're
connected to our peer and not awaiting a remote RAA, we need to
free anything in our holding cell.
However, we don't want to immediately free the holding cell during
channel_monitor_updated as it presents a somewhat bug-prone case of
reentrancy:
a) it would re-enter user code around a monitor update while being
called from user code notifying us of the same monitor being
updated, making deadlocs very likely (in fact, our fuzzers
would have a bug here!),
b) the re-entrancy only occurs in a very rare case, making it
likely users will not hit it in testing, only deadlocking in
production.
Thus, we add a holding-cell-free pass over each channel in
get_and_clear_pending_msg_events. This fits up nicely with the
anticipated bug - users almost certainly need to process new
network messages immediately after monitor updating has been
restored to send messages which were not sent originally when the
monitor updating was paused.
Without this, chanmon_fail_consistency was able to find a stuck
condition where we sit on an HTLC failure in our holding cell and
don't ever handle it (at least until we have other actions to take
which empty the holding cell).
Because we may now generate a monitor update during
get_and_clear_pending_msg_events calls, we need to ensure we
re-serialize the relevant ChannelManager before attempting to
reload it, if such a monitor update occurred.
Previously, if we got disconnected from a peer while there were
HTLCs pending forwarding in the holding cell, we'd clear them and
fail them all backwards. This is largely fine, but since we now
have support for handling such HTLCs on reconnect, we might as
well not, instead relying on our timeout logic to fail them
backwards if it takes too long to forward them.
If the fuzz target is failing due to a channel force-close, the
immediately-visible error is that we're signing a stale state. This
is because the ChannelMonitorUpdateStep::ChannelForceClosed event
results in a signature in the test clone which was deserialized
using a OnlyReadsKeysInterface. Instead, we need to deserialize
using the full KeysInterface instance.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 7fde891 to 34fcd99CompareMay 21, 2021 15:10
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed and renamed one variable as @jkczyz suggested:

$ git diff-tree -U1 7fde8910 34fcd99f
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 14d9c528..9f9820c1 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -910,3 +910,3 @@ macro_rules! handle_chan_restoration_locked {
let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
- let chanmon_update_is_some = chanmon_update.is_some();
+ let chanmon_update_is_none = chanmon_update.is_none();
let res = loop {
@@ -1006,3 +1006,3 @@ macro_rules! handle_chan_restoration_locked {
- if !chanmon_update_is_some {
+ if chanmon_update_is_none {
// If there was no ChannelMonitorUpdate, we should never generate an Err in the res loop

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Gonna go ahead and take this since its been a long time. Post-merge review from @ariard would also be welcome but I'd rather not wait on it if he's busy.

@TheBlueMatt
TheBlueMatt merged commit 3a0356f into lightningdevkit:mainMay 24, 2021
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 24, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 29, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@TheBlueMatt@jkczyz@valentinewallace@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Clean up and more liberally free holding cell HTLCs (without re-entrancy) - #851

Merged
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get
May 24, 2021
Merged

Clean up and more liberally free holding cell HTLCs (without re-entrancy)#851
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This is #756 reworked a decent chunk. In #756 the reentrancy story around channel_monitor_updated scared me significantly, but for some reason I apparently never bothered to really think deeply about the options. Here we move the holding cell free to get_and_clear_pending_msg_events, which is a super obvious location for it - it should be called right after channel_monitor_updated in most cases, but shouldn't have the same reentrancy issues because it already can generate a monitor update. It requires some macro rework, but its not too bad.

@TheBlueMattTheBlueMatt added this to the 0.0.14 milestone Mar 18, 2021
@codecov

codecovBot commented Mar 19, 2021

Copy link
Copy Markdown

Codecov Report

Merging #851 (34fcd99) into main (b6de281) will increase coverage by 0.08%.
The diff coverage is 80.70%.

Impacted file tree graph

@@ Coverage Diff @@## main #851 +/- ##
==========================================
+ Coverage 90.48% 90.56% +0.08% 
==========================================
Files 59 59 Lines 29896 30015 +119 ==========================================
+ Hits 27050 27184 +134 + Misses 2846 2831 -15 
Impacted FilesCoverage Δ
lightning/src/ln/functional_test_utils.rs95.06% <ø> (ø)
lightning/src/ln/channelmanager.rs82.92% <55.55%> (-0.55%)⬇️
lightning/src/ln/chanmon_update_fail_tests.rs97.77% <99.20%> (+0.13%)⬆️
lightning/src/ln/channel.rs88.29% <100.00%> (+1.04%)⬆️
lightning/src/ln/functional_tests.rs96.87% <0.00%> (-0.04%)⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b6de281...34fcd99. Read the comment docs.

Comment threadlightning/src/ln/channelmanager.rs Outdated
order = RAACommitmentOrder::RevokeAndACKFirst;
}
return_monitor_err!(self, e, channel_state, chan, order, revoke_and_ack.is_some(), commitment_update.is_some());
//TODO: Resend the funding_locked if needed once we get the monitor running again

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This todo doesn't need to be migrated to the new code, right?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thats a good question! I don't think it matters - this PR switched the ordering of message resending to resend the funding_locked before handling the monitor update, meaning we'll never need to resend because we always send (instead of maybe not sending if the monitor update fails).

The "don't send a funding_locked if we're paused pending monitor update" thing isn't really a security thing (we just expose a next_per_commitment_point which doesn't allow our counterparty to....do anything), its just kinda nice to not tell our counterparty we're ready to accept HTLCs when we're definitely not. That said, Channel::channel_reestablish doesn't really handle it all that carefully anyway, and we may end up re-sending a funding_locked on reconnect when we are still pending a monitor update failure.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 0993c08 to b14ea14CompareMarch 25, 2021 19:21

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major. This looks pretty reasonable to me!

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment on lines +2305 to +2318
if self.channel_state >= ChannelState::ChannelFunded as u32 &&
(self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)) == 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just move these checks into free_holding_cell(..)? Also, revoke_and_ack currently calls the regular free_holding_cell_htlcs(..), wonder if it should call maybe_free instead?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, we already check all the preconditions at the callsites, I suppose we could move to just calling maybe_..., but that seems more brittle, given we already check them.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), channel_id);
handle_errors.push((chan.get_counterparty_node_id(), res));
if drop { return false; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so drop == true implies that we don't need to push the UpdateHTLCs event below? Maybe could use a comment for why.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed drop to close_channel is that more clear?

}

for (counterparty_node_id, err) in handle_errors.drain(..) {
let _ = handle_error!(self, err, counterparty_node_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it matters, but if we Permanently failed to update_channel above, we may still attempt another update_channel here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, right, that's awkward, but I think also globally true - we always generate a ChannelForceClosed update for the monitor, even if the user can't persist it.

// disconnect, we may be handed some HTLCs to fail backwards here.
assert!(htlcs_to_fail.is_empty());
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), shutdown_msg));
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), htlcs_to_fail, shutdown_msg));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a general point: might we wanna think about how this logic fits into "cancelling payments" in the future? Because I could see a situation where a user attempts a payments, which gets stuck in the holding cell, and they'd prefer to attempt the payment again by a different route and cancel the existing one.

@TheBlueMattTheBlueMattMar 26, 2021

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, possibly, but I'm not very worried. The holding cell is only really intended for things that are just pending some network latency - you should update a channel monitor or receive a revoke_and_ack from your peer within some hundreds of milliseconds, at max - not for things that are going to take a while - ie its not (really) for when your counterparty is offline.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from b14ea14 to af14d44CompareMarch 26, 2021 23:11

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ACK af14d44 modulo fixing 5613be8's commit message

Comment threadlightning/src/ln/channel.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from af14d44 to 386cc60CompareMarch 31, 2021 03:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed Val's comments, full diff is:

$ git diff-tree -U1 af14d44 386cc60
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index c40e7f1c4..a2bcede85 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -2309,2 +2309,3 @@ impl<Signer: Sign> Channel<Signer> {
}
+
/// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 73ba9d9bd..b538655bd 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -731,3 +731,3 @@ macro_rules! handle_monitor_err {
ChannelMonitorUpdateErr::PermanentFailure => {
- log_error!($self.logger, "Closing channel {} due to monitor update PermanentFailure", log_bytes!($chan_id[..]));
+ log_error!($self.logger, "Closing channel {} due to monitor update ChannelMonitorUpdateErr::PermanentFailure", log_bytes!($chan_id[..]));
if let Some(short_id) = $chan.get_short_channel_id() {

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 386cc60 to 0df3461CompareApril 20, 2021 18:40
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 3 times, most recently from 478044e to 72b730cCompareApril 21, 2021 22:25
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 72b730c to ffbe950CompareApril 28, 2021 00:02
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on upstream, with pretty trivial changes.

@TheBlueMattTheBlueMatt modified the milestones: 0.0.14, 0.0.15Apr 29, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Looks like this is slipping to 0.0.15. It would be good to land some of these fixes as they are potential bugs, even if very rare.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed until e33012f included so far

Comment threadfuzz/src/chanmon_consistency.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated

let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
if chanmon_update.is_some() {
// On reconnect (or monitor restoration), we, by definition, only resend a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think mentioning monitor restoration (monitor_updating_restored ?) is confusing as this function never returns a monitor update.

I would remove it and instead in the $funding_locked branch, say something "at monitor restoration, we might resend to our counterparty funding_locked but this event doesn't generate a channel monitor update itself"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you're right. I removed the reference to monitor restoration, but I'm not sure your comment communicates enough information to later re-analyze the situation going on.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm "In case of monitor restoration, funding_locked will never be generated as this event isn't altering monitor state"?

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
let funding_broadcastable: Option<Transaction> = $funding_broadcastable; // Force type-checking to resolve
macro_rules! handle_cs { () => {
if let Some(monitor_update) = chanmon_update {
assert!($order == RAACommitmentOrder::RevokeAndACKFirst);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make my mind about correctness of this statement. It implies the path in Channel::channel_reestablish L3023, when we return htlc failures to route backward included in a new commitment transaction ? It doesn't necessary mean peer send us a commitment_signed for which we should send a RAA /

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I can't convince myself its safe anymore, I walked this bit back.

Comment threadlightning/src/ln/channelmanager.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 4 times, most recently from 0b365ec to 1005f07CompareMay 13, 2021 20:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream, relevant changes responding to comments are in fixup commits.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left few more comments, top commits sounds good to me, but want to do a second parse with fixed comments.

Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
match chan.maybe_free_holding_cell_htlcs(&self.logger) {
Ok((None, ref htlcs)) if htlcs.is_empty() => true,
Ok((commitment_opt, failed_htlcs)) => {
forwarding_failed_htlcs.push((failed_htlcs, *channel_id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be backwarding_failed_htlcs here ? Per fail_holding_cell_htlcs comment, those htlcs are designated as "The HTLCs need to be failed backwards or, if they were one of our outgoing HTLCs".

Yeah likely, we should give a brush stroke at some point about the whole HTLC routing section with consistent naming and better comments.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that's confusing, the name was "htlcs which failed during forwarding" but that wasn't clear.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 2 times, most recently from 9b1aa05 to 7eaca87CompareMay 14, 2021 22:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I believe I've addressed all the comments.

The channel restoration code in channel monitor updating and peer
reconnection both do incredibly similar things, and there is
little reason to have them be separate. Sadly because they require
holding a lock with a reference to elements in the lock, its not
practical to make them utility functions, so instead we introduce
a two-step macro here which will eventually be used for both.
Because we still support pre-NLL Rust, the macro has to be in two
parts - one which runs with the channel_state lock, and one which
does not.
This mostly swaps some Vecs that can only ever contain one element
for Options.
This fails chanmon_consistency on IgnoreError error events and on
messages left over to be sent to a just-disconnected peer, which
should have been drained.
These should never appear, so consider them a fuzzer fail case.
Because of the merge between peer reconnection and channel monitor
updating channel restoration code, we now sometimes generate
(somewhat spurious) announcement signatures when restoring channel
monitor updating. This should not result in a fuzzing failure.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 365cee1 to 7fde891CompareMay 20, 2021 21:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no changes aside from two new fixup commits to respond to Jeff's comments.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I'd be more comfortable if @ariard gave a look before merging.

Comment threadlightning/src/ln/channelmanager.rs Outdated
This merges the code for restoring channel functionality between
channel monitor updating restored and peer reconnection, reducing
redundant code.
Both break_chan_entry and try_chan_entry do almost identical work,
only differing on if they `break` or `return` in response to an
error. Because we will now also need an option to do neither, we
break out the common code into a shared `convert_chan_err` macro.
If there is no pending channel update messages when monitor updating
is restored (though there may be an RAA to send), and we're
connected to our peer and not awaiting a remote RAA, we need to
free anything in our holding cell.
However, we don't want to immediately free the holding cell during
channel_monitor_updated as it presents a somewhat bug-prone case of
reentrancy:
a) it would re-enter user code around a monitor update while being
called from user code notifying us of the same monitor being
updated, making deadlocs very likely (in fact, our fuzzers
would have a bug here!),
b) the re-entrancy only occurs in a very rare case, making it
likely users will not hit it in testing, only deadlocking in
production.
Thus, we add a holding-cell-free pass over each channel in
get_and_clear_pending_msg_events. This fits up nicely with the
anticipated bug - users almost certainly need to process new
network messages immediately after monitor updating has been
restored to send messages which were not sent originally when the
monitor updating was paused.
Without this, chanmon_fail_consistency was able to find a stuck
condition where we sit on an HTLC failure in our holding cell and
don't ever handle it (at least until we have other actions to take
which empty the holding cell).
Because we may now generate a monitor update during
get_and_clear_pending_msg_events calls, we need to ensure we
re-serialize the relevant ChannelManager before attempting to
reload it, if such a monitor update occurred.
Previously, if we got disconnected from a peer while there were
HTLCs pending forwarding in the holding cell, we'd clear them and
fail them all backwards. This is largely fine, but since we now
have support for handling such HTLCs on reconnect, we might as
well not, instead relying on our timeout logic to fail them
backwards if it takes too long to forward them.
If the fuzz target is failing due to a channel force-close, the
immediately-visible error is that we're signing a stale state. This
is because the ChannelMonitorUpdateStep::ChannelForceClosed event
results in a signature in the test clone which was deserialized
using a OnlyReadsKeysInterface. Instead, we need to deserialize
using the full KeysInterface instance.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 7fde891 to 34fcd99CompareMay 21, 2021 15:10
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed and renamed one variable as @jkczyz suggested:

$ git diff-tree -U1 7fde8910 34fcd99f
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 14d9c528..9f9820c1 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -910,3 +910,3 @@ macro_rules! handle_chan_restoration_locked {
let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
- let chanmon_update_is_some = chanmon_update.is_some();
+ let chanmon_update_is_none = chanmon_update.is_none();
let res = loop {
@@ -1006,3 +1006,3 @@ macro_rules! handle_chan_restoration_locked {
- if !chanmon_update_is_some {
+ if chanmon_update_is_none {
// If there was no ChannelMonitorUpdate, we should never generate an Err in the res loop

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Gonna go ahead and take this since its been a long time. Post-merge review from @ariard would also be welcome but I'd rather not wait on it if he's busy.

@TheBlueMatt
TheBlueMatt merged commit 3a0356f into lightningdevkit:mainMay 24, 2021
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 24, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 29, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@TheBlueMatt@jkczyz@valentinewallace@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Clean up and more liberally free holding cell HTLCs (without re-entrancy) - #851

Merged
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get
May 24, 2021
Merged

Clean up and more liberally free holding cell HTLCs (without re-entrancy)#851
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This is #756 reworked a decent chunk. In #756 the reentrancy story around channel_monitor_updated scared me significantly, but for some reason I apparently never bothered to really think deeply about the options. Here we move the holding cell free to get_and_clear_pending_msg_events, which is a super obvious location for it - it should be called right after channel_monitor_updated in most cases, but shouldn't have the same reentrancy issues because it already can generate a monitor update. It requires some macro rework, but its not too bad.

@TheBlueMattTheBlueMatt added this to the 0.0.14 milestone Mar 18, 2021
@codecov

codecovBot commented Mar 19, 2021

Copy link
Copy Markdown

Codecov Report

Merging #851 (34fcd99) into main (b6de281) will increase coverage by 0.08%.
The diff coverage is 80.70%.

Impacted file tree graph

@@ Coverage Diff @@## main #851 +/- ##
==========================================
+ Coverage 90.48% 90.56% +0.08% 
==========================================
Files 59 59 Lines 29896 30015 +119 ==========================================
+ Hits 27050 27184 +134 + Misses 2846 2831 -15 
Impacted FilesCoverage Δ
lightning/src/ln/functional_test_utils.rs95.06% <ø> (ø)
lightning/src/ln/channelmanager.rs82.92% <55.55%> (-0.55%)⬇️
lightning/src/ln/chanmon_update_fail_tests.rs97.77% <99.20%> (+0.13%)⬆️
lightning/src/ln/channel.rs88.29% <100.00%> (+1.04%)⬆️
lightning/src/ln/functional_tests.rs96.87% <0.00%> (-0.04%)⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b6de281...34fcd99. Read the comment docs.

Comment threadlightning/src/ln/channelmanager.rs Outdated
order = RAACommitmentOrder::RevokeAndACKFirst;
}
return_monitor_err!(self, e, channel_state, chan, order, revoke_and_ack.is_some(), commitment_update.is_some());
//TODO: Resend the funding_locked if needed once we get the monitor running again

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This todo doesn't need to be migrated to the new code, right?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thats a good question! I don't think it matters - this PR switched the ordering of message resending to resend the funding_locked before handling the monitor update, meaning we'll never need to resend because we always send (instead of maybe not sending if the monitor update fails).

The "don't send a funding_locked if we're paused pending monitor update" thing isn't really a security thing (we just expose a next_per_commitment_point which doesn't allow our counterparty to....do anything), its just kinda nice to not tell our counterparty we're ready to accept HTLCs when we're definitely not. That said, Channel::channel_reestablish doesn't really handle it all that carefully anyway, and we may end up re-sending a funding_locked on reconnect when we are still pending a monitor update failure.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 0993c08 to b14ea14CompareMarch 25, 2021 19:21

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major. This looks pretty reasonable to me!

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment on lines +2305 to +2318
if self.channel_state >= ChannelState::ChannelFunded as u32 &&
(self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)) == 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just move these checks into free_holding_cell(..)? Also, revoke_and_ack currently calls the regular free_holding_cell_htlcs(..), wonder if it should call maybe_free instead?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, we already check all the preconditions at the callsites, I suppose we could move to just calling maybe_..., but that seems more brittle, given we already check them.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), channel_id);
handle_errors.push((chan.get_counterparty_node_id(), res));
if drop { return false; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so drop == true implies that we don't need to push the UpdateHTLCs event below? Maybe could use a comment for why.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed drop to close_channel is that more clear?

}

for (counterparty_node_id, err) in handle_errors.drain(..) {
let _ = handle_error!(self, err, counterparty_node_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it matters, but if we Permanently failed to update_channel above, we may still attempt another update_channel here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, right, that's awkward, but I think also globally true - we always generate a ChannelForceClosed update for the monitor, even if the user can't persist it.

// disconnect, we may be handed some HTLCs to fail backwards here.
assert!(htlcs_to_fail.is_empty());
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), shutdown_msg));
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), htlcs_to_fail, shutdown_msg));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a general point: might we wanna think about how this logic fits into "cancelling payments" in the future? Because I could see a situation where a user attempts a payments, which gets stuck in the holding cell, and they'd prefer to attempt the payment again by a different route and cancel the existing one.

@TheBlueMattTheBlueMattMar 26, 2021

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, possibly, but I'm not very worried. The holding cell is only really intended for things that are just pending some network latency - you should update a channel monitor or receive a revoke_and_ack from your peer within some hundreds of milliseconds, at max - not for things that are going to take a while - ie its not (really) for when your counterparty is offline.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from b14ea14 to af14d44CompareMarch 26, 2021 23:11

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ACK af14d44 modulo fixing 5613be8's commit message

Comment threadlightning/src/ln/channel.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from af14d44 to 386cc60CompareMarch 31, 2021 03:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed Val's comments, full diff is:

$ git diff-tree -U1 af14d44 386cc60
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index c40e7f1c4..a2bcede85 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -2309,2 +2309,3 @@ impl<Signer: Sign> Channel<Signer> {
}
+
/// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 73ba9d9bd..b538655bd 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -731,3 +731,3 @@ macro_rules! handle_monitor_err {
ChannelMonitorUpdateErr::PermanentFailure => {
- log_error!($self.logger, "Closing channel {} due to monitor update PermanentFailure", log_bytes!($chan_id[..]));
+ log_error!($self.logger, "Closing channel {} due to monitor update ChannelMonitorUpdateErr::PermanentFailure", log_bytes!($chan_id[..]));
if let Some(short_id) = $chan.get_short_channel_id() {

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 386cc60 to 0df3461CompareApril 20, 2021 18:40
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 3 times, most recently from 478044e to 72b730cCompareApril 21, 2021 22:25
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 72b730c to ffbe950CompareApril 28, 2021 00:02
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on upstream, with pretty trivial changes.

@TheBlueMattTheBlueMatt modified the milestones: 0.0.14, 0.0.15Apr 29, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Looks like this is slipping to 0.0.15. It would be good to land some of these fixes as they are potential bugs, even if very rare.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed until e33012f included so far

Comment threadfuzz/src/chanmon_consistency.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated

let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
if chanmon_update.is_some() {
// On reconnect (or monitor restoration), we, by definition, only resend a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think mentioning monitor restoration (monitor_updating_restored ?) is confusing as this function never returns a monitor update.

I would remove it and instead in the $funding_locked branch, say something "at monitor restoration, we might resend to our counterparty funding_locked but this event doesn't generate a channel monitor update itself"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you're right. I removed the reference to monitor restoration, but I'm not sure your comment communicates enough information to later re-analyze the situation going on.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm "In case of monitor restoration, funding_locked will never be generated as this event isn't altering monitor state"?

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
let funding_broadcastable: Option<Transaction> = $funding_broadcastable; // Force type-checking to resolve
macro_rules! handle_cs { () => {
if let Some(monitor_update) = chanmon_update {
assert!($order == RAACommitmentOrder::RevokeAndACKFirst);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make my mind about correctness of this statement. It implies the path in Channel::channel_reestablish L3023, when we return htlc failures to route backward included in a new commitment transaction ? It doesn't necessary mean peer send us a commitment_signed for which we should send a RAA /

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I can't convince myself its safe anymore, I walked this bit back.

Comment threadlightning/src/ln/channelmanager.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 4 times, most recently from 0b365ec to 1005f07CompareMay 13, 2021 20:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream, relevant changes responding to comments are in fixup commits.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left few more comments, top commits sounds good to me, but want to do a second parse with fixed comments.

Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
match chan.maybe_free_holding_cell_htlcs(&self.logger) {
Ok((None, ref htlcs)) if htlcs.is_empty() => true,
Ok((commitment_opt, failed_htlcs)) => {
forwarding_failed_htlcs.push((failed_htlcs, *channel_id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be backwarding_failed_htlcs here ? Per fail_holding_cell_htlcs comment, those htlcs are designated as "The HTLCs need to be failed backwards or, if they were one of our outgoing HTLCs".

Yeah likely, we should give a brush stroke at some point about the whole HTLC routing section with consistent naming and better comments.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that's confusing, the name was "htlcs which failed during forwarding" but that wasn't clear.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 2 times, most recently from 9b1aa05 to 7eaca87CompareMay 14, 2021 22:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I believe I've addressed all the comments.

The channel restoration code in channel monitor updating and peer
reconnection both do incredibly similar things, and there is
little reason to have them be separate. Sadly because they require
holding a lock with a reference to elements in the lock, its not
practical to make them utility functions, so instead we introduce
a two-step macro here which will eventually be used for both.
Because we still support pre-NLL Rust, the macro has to be in two
parts - one which runs with the channel_state lock, and one which
does not.
This mostly swaps some Vecs that can only ever contain one element
for Options.
This fails chanmon_consistency on IgnoreError error events and on
messages left over to be sent to a just-disconnected peer, which
should have been drained.
These should never appear, so consider them a fuzzer fail case.
Because of the merge between peer reconnection and channel monitor
updating channel restoration code, we now sometimes generate
(somewhat spurious) announcement signatures when restoring channel
monitor updating. This should not result in a fuzzing failure.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 365cee1 to 7fde891CompareMay 20, 2021 21:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no changes aside from two new fixup commits to respond to Jeff's comments.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I'd be more comfortable if @ariard gave a look before merging.

Comment threadlightning/src/ln/channelmanager.rs Outdated
This merges the code for restoring channel functionality between
channel monitor updating restored and peer reconnection, reducing
redundant code.
Both break_chan_entry and try_chan_entry do almost identical work,
only differing on if they `break` or `return` in response to an
error. Because we will now also need an option to do neither, we
break out the common code into a shared `convert_chan_err` macro.
If there is no pending channel update messages when monitor updating
is restored (though there may be an RAA to send), and we're
connected to our peer and not awaiting a remote RAA, we need to
free anything in our holding cell.
However, we don't want to immediately free the holding cell during
channel_monitor_updated as it presents a somewhat bug-prone case of
reentrancy:
a) it would re-enter user code around a monitor update while being
called from user code notifying us of the same monitor being
updated, making deadlocs very likely (in fact, our fuzzers
would have a bug here!),
b) the re-entrancy only occurs in a very rare case, making it
likely users will not hit it in testing, only deadlocking in
production.
Thus, we add a holding-cell-free pass over each channel in
get_and_clear_pending_msg_events. This fits up nicely with the
anticipated bug - users almost certainly need to process new
network messages immediately after monitor updating has been
restored to send messages which were not sent originally when the
monitor updating was paused.
Without this, chanmon_fail_consistency was able to find a stuck
condition where we sit on an HTLC failure in our holding cell and
don't ever handle it (at least until we have other actions to take
which empty the holding cell).
Because we may now generate a monitor update during
get_and_clear_pending_msg_events calls, we need to ensure we
re-serialize the relevant ChannelManager before attempting to
reload it, if such a monitor update occurred.
Previously, if we got disconnected from a peer while there were
HTLCs pending forwarding in the holding cell, we'd clear them and
fail them all backwards. This is largely fine, but since we now
have support for handling such HTLCs on reconnect, we might as
well not, instead relying on our timeout logic to fail them
backwards if it takes too long to forward them.
If the fuzz target is failing due to a channel force-close, the
immediately-visible error is that we're signing a stale state. This
is because the ChannelMonitorUpdateStep::ChannelForceClosed event
results in a signature in the test clone which was deserialized
using a OnlyReadsKeysInterface. Instead, we need to deserialize
using the full KeysInterface instance.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 7fde891 to 34fcd99CompareMay 21, 2021 15:10
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed and renamed one variable as @jkczyz suggested:

$ git diff-tree -U1 7fde8910 34fcd99f
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 14d9c528..9f9820c1 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -910,3 +910,3 @@ macro_rules! handle_chan_restoration_locked {
let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
- let chanmon_update_is_some = chanmon_update.is_some();
+ let chanmon_update_is_none = chanmon_update.is_none();
let res = loop {
@@ -1006,3 +1006,3 @@ macro_rules! handle_chan_restoration_locked {
- if !chanmon_update_is_some {
+ if chanmon_update_is_none {
// If there was no ChannelMonitorUpdate, we should never generate an Err in the res loop

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Gonna go ahead and take this since its been a long time. Post-merge review from @ariard would also be welcome but I'd rather not wait on it if he's busy.

@TheBlueMatt
TheBlueMatt merged commit 3a0356f into lightningdevkit:mainMay 24, 2021
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 24, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 29, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@TheBlueMatt@jkczyz@valentinewallace@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Clean up and more liberally free holding cell HTLCs (without re-entrancy) - #851

Merged
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get
May 24, 2021
Merged

Clean up and more liberally free holding cell HTLCs (without re-entrancy)#851
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This is #756 reworked a decent chunk. In #756 the reentrancy story around channel_monitor_updated scared me significantly, but for some reason I apparently never bothered to really think deeply about the options. Here we move the holding cell free to get_and_clear_pending_msg_events, which is a super obvious location for it - it should be called right after channel_monitor_updated in most cases, but shouldn't have the same reentrancy issues because it already can generate a monitor update. It requires some macro rework, but its not too bad.

@TheBlueMattTheBlueMatt added this to the 0.0.14 milestone Mar 18, 2021
@codecov

codecovBot commented Mar 19, 2021

Copy link
Copy Markdown

Codecov Report

Merging #851 (34fcd99) into main (b6de281) will increase coverage by 0.08%.
The diff coverage is 80.70%.

Impacted file tree graph

@@ Coverage Diff @@## main #851 +/- ##
==========================================
+ Coverage 90.48% 90.56% +0.08% 
==========================================
Files 59 59 Lines 29896 30015 +119 ==========================================
+ Hits 27050 27184 +134 + Misses 2846 2831 -15 
Impacted FilesCoverage Δ
lightning/src/ln/functional_test_utils.rs95.06% <ø> (ø)
lightning/src/ln/channelmanager.rs82.92% <55.55%> (-0.55%)⬇️
lightning/src/ln/chanmon_update_fail_tests.rs97.77% <99.20%> (+0.13%)⬆️
lightning/src/ln/channel.rs88.29% <100.00%> (+1.04%)⬆️
lightning/src/ln/functional_tests.rs96.87% <0.00%> (-0.04%)⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b6de281...34fcd99. Read the comment docs.

Comment threadlightning/src/ln/channelmanager.rs Outdated
order = RAACommitmentOrder::RevokeAndACKFirst;
}
return_monitor_err!(self, e, channel_state, chan, order, revoke_and_ack.is_some(), commitment_update.is_some());
//TODO: Resend the funding_locked if needed once we get the monitor running again

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This todo doesn't need to be migrated to the new code, right?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thats a good question! I don't think it matters - this PR switched the ordering of message resending to resend the funding_locked before handling the monitor update, meaning we'll never need to resend because we always send (instead of maybe not sending if the monitor update fails).

The "don't send a funding_locked if we're paused pending monitor update" thing isn't really a security thing (we just expose a next_per_commitment_point which doesn't allow our counterparty to....do anything), its just kinda nice to not tell our counterparty we're ready to accept HTLCs when we're definitely not. That said, Channel::channel_reestablish doesn't really handle it all that carefully anyway, and we may end up re-sending a funding_locked on reconnect when we are still pending a monitor update failure.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 0993c08 to b14ea14CompareMarch 25, 2021 19:21

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major. This looks pretty reasonable to me!

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment on lines +2305 to +2318
if self.channel_state >= ChannelState::ChannelFunded as u32 &&
(self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)) == 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just move these checks into free_holding_cell(..)? Also, revoke_and_ack currently calls the regular free_holding_cell_htlcs(..), wonder if it should call maybe_free instead?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, we already check all the preconditions at the callsites, I suppose we could move to just calling maybe_..., but that seems more brittle, given we already check them.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), channel_id);
handle_errors.push((chan.get_counterparty_node_id(), res));
if drop { return false; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so drop == true implies that we don't need to push the UpdateHTLCs event below? Maybe could use a comment for why.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed drop to close_channel is that more clear?

}

for (counterparty_node_id, err) in handle_errors.drain(..) {
let _ = handle_error!(self, err, counterparty_node_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it matters, but if we Permanently failed to update_channel above, we may still attempt another update_channel here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, right, that's awkward, but I think also globally true - we always generate a ChannelForceClosed update for the monitor, even if the user can't persist it.

// disconnect, we may be handed some HTLCs to fail backwards here.
assert!(htlcs_to_fail.is_empty());
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), shutdown_msg));
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), htlcs_to_fail, shutdown_msg));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a general point: might we wanna think about how this logic fits into "cancelling payments" in the future? Because I could see a situation where a user attempts a payments, which gets stuck in the holding cell, and they'd prefer to attempt the payment again by a different route and cancel the existing one.

@TheBlueMattTheBlueMattMar 26, 2021

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, possibly, but I'm not very worried. The holding cell is only really intended for things that are just pending some network latency - you should update a channel monitor or receive a revoke_and_ack from your peer within some hundreds of milliseconds, at max - not for things that are going to take a while - ie its not (really) for when your counterparty is offline.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from b14ea14 to af14d44CompareMarch 26, 2021 23:11

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ACK af14d44 modulo fixing 5613be8's commit message

Comment threadlightning/src/ln/channel.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from af14d44 to 386cc60CompareMarch 31, 2021 03:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed Val's comments, full diff is:

$ git diff-tree -U1 af14d44 386cc60
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index c40e7f1c4..a2bcede85 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -2309,2 +2309,3 @@ impl<Signer: Sign> Channel<Signer> {
}
+
/// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 73ba9d9bd..b538655bd 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -731,3 +731,3 @@ macro_rules! handle_monitor_err {
ChannelMonitorUpdateErr::PermanentFailure => {
- log_error!($self.logger, "Closing channel {} due to monitor update PermanentFailure", log_bytes!($chan_id[..]));
+ log_error!($self.logger, "Closing channel {} due to monitor update ChannelMonitorUpdateErr::PermanentFailure", log_bytes!($chan_id[..]));
if let Some(short_id) = $chan.get_short_channel_id() {

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 386cc60 to 0df3461CompareApril 20, 2021 18:40
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 3 times, most recently from 478044e to 72b730cCompareApril 21, 2021 22:25
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 72b730c to ffbe950CompareApril 28, 2021 00:02
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on upstream, with pretty trivial changes.

@TheBlueMattTheBlueMatt modified the milestones: 0.0.14, 0.0.15Apr 29, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Looks like this is slipping to 0.0.15. It would be good to land some of these fixes as they are potential bugs, even if very rare.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed until e33012f included so far

Comment threadfuzz/src/chanmon_consistency.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated

let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
if chanmon_update.is_some() {
// On reconnect (or monitor restoration), we, by definition, only resend a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think mentioning monitor restoration (monitor_updating_restored ?) is confusing as this function never returns a monitor update.

I would remove it and instead in the $funding_locked branch, say something "at monitor restoration, we might resend to our counterparty funding_locked but this event doesn't generate a channel monitor update itself"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you're right. I removed the reference to monitor restoration, but I'm not sure your comment communicates enough information to later re-analyze the situation going on.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm "In case of monitor restoration, funding_locked will never be generated as this event isn't altering monitor state"?

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
let funding_broadcastable: Option<Transaction> = $funding_broadcastable; // Force type-checking to resolve
macro_rules! handle_cs { () => {
if let Some(monitor_update) = chanmon_update {
assert!($order == RAACommitmentOrder::RevokeAndACKFirst);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make my mind about correctness of this statement. It implies the path in Channel::channel_reestablish L3023, when we return htlc failures to route backward included in a new commitment transaction ? It doesn't necessary mean peer send us a commitment_signed for which we should send a RAA /

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I can't convince myself its safe anymore, I walked this bit back.

Comment threadlightning/src/ln/channelmanager.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 4 times, most recently from 0b365ec to 1005f07CompareMay 13, 2021 20:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream, relevant changes responding to comments are in fixup commits.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left few more comments, top commits sounds good to me, but want to do a second parse with fixed comments.

Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
match chan.maybe_free_holding_cell_htlcs(&self.logger) {
Ok((None, ref htlcs)) if htlcs.is_empty() => true,
Ok((commitment_opt, failed_htlcs)) => {
forwarding_failed_htlcs.push((failed_htlcs, *channel_id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be backwarding_failed_htlcs here ? Per fail_holding_cell_htlcs comment, those htlcs are designated as "The HTLCs need to be failed backwards or, if they were one of our outgoing HTLCs".

Yeah likely, we should give a brush stroke at some point about the whole HTLC routing section with consistent naming and better comments.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that's confusing, the name was "htlcs which failed during forwarding" but that wasn't clear.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 2 times, most recently from 9b1aa05 to 7eaca87CompareMay 14, 2021 22:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I believe I've addressed all the comments.

The channel restoration code in channel monitor updating and peer
reconnection both do incredibly similar things, and there is
little reason to have them be separate. Sadly because they require
holding a lock with a reference to elements in the lock, its not
practical to make them utility functions, so instead we introduce
a two-step macro here which will eventually be used for both.
Because we still support pre-NLL Rust, the macro has to be in two
parts - one which runs with the channel_state lock, and one which
does not.
This mostly swaps some Vecs that can only ever contain one element
for Options.
This fails chanmon_consistency on IgnoreError error events and on
messages left over to be sent to a just-disconnected peer, which
should have been drained.
These should never appear, so consider them a fuzzer fail case.
Because of the merge between peer reconnection and channel monitor
updating channel restoration code, we now sometimes generate
(somewhat spurious) announcement signatures when restoring channel
monitor updating. This should not result in a fuzzing failure.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 365cee1 to 7fde891CompareMay 20, 2021 21:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no changes aside from two new fixup commits to respond to Jeff's comments.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I'd be more comfortable if @ariard gave a look before merging.

Comment threadlightning/src/ln/channelmanager.rs Outdated
This merges the code for restoring channel functionality between
channel monitor updating restored and peer reconnection, reducing
redundant code.
Both break_chan_entry and try_chan_entry do almost identical work,
only differing on if they `break` or `return` in response to an
error. Because we will now also need an option to do neither, we
break out the common code into a shared `convert_chan_err` macro.
If there is no pending channel update messages when monitor updating
is restored (though there may be an RAA to send), and we're
connected to our peer and not awaiting a remote RAA, we need to
free anything in our holding cell.
However, we don't want to immediately free the holding cell during
channel_monitor_updated as it presents a somewhat bug-prone case of
reentrancy:
a) it would re-enter user code around a monitor update while being
called from user code notifying us of the same monitor being
updated, making deadlocs very likely (in fact, our fuzzers
would have a bug here!),
b) the re-entrancy only occurs in a very rare case, making it
likely users will not hit it in testing, only deadlocking in
production.
Thus, we add a holding-cell-free pass over each channel in
get_and_clear_pending_msg_events. This fits up nicely with the
anticipated bug - users almost certainly need to process new
network messages immediately after monitor updating has been
restored to send messages which were not sent originally when the
monitor updating was paused.
Without this, chanmon_fail_consistency was able to find a stuck
condition where we sit on an HTLC failure in our holding cell and
don't ever handle it (at least until we have other actions to take
which empty the holding cell).
Because we may now generate a monitor update during
get_and_clear_pending_msg_events calls, we need to ensure we
re-serialize the relevant ChannelManager before attempting to
reload it, if such a monitor update occurred.
Previously, if we got disconnected from a peer while there were
HTLCs pending forwarding in the holding cell, we'd clear them and
fail them all backwards. This is largely fine, but since we now
have support for handling such HTLCs on reconnect, we might as
well not, instead relying on our timeout logic to fail them
backwards if it takes too long to forward them.
If the fuzz target is failing due to a channel force-close, the
immediately-visible error is that we're signing a stale state. This
is because the ChannelMonitorUpdateStep::ChannelForceClosed event
results in a signature in the test clone which was deserialized
using a OnlyReadsKeysInterface. Instead, we need to deserialize
using the full KeysInterface instance.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 7fde891 to 34fcd99CompareMay 21, 2021 15:10
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed and renamed one variable as @jkczyz suggested:

$ git diff-tree -U1 7fde8910 34fcd99f
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 14d9c528..9f9820c1 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -910,3 +910,3 @@ macro_rules! handle_chan_restoration_locked {
let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
- let chanmon_update_is_some = chanmon_update.is_some();
+ let chanmon_update_is_none = chanmon_update.is_none();
let res = loop {
@@ -1006,3 +1006,3 @@ macro_rules! handle_chan_restoration_locked {
- if !chanmon_update_is_some {
+ if chanmon_update_is_none {
// If there was no ChannelMonitorUpdate, we should never generate an Err in the res loop

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Gonna go ahead and take this since its been a long time. Post-merge review from @ariard would also be welcome but I'd rather not wait on it if he's busy.

@TheBlueMatt
TheBlueMatt merged commit 3a0356f into lightningdevkit:mainMay 24, 2021
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 24, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 29, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@TheBlueMatt@jkczyz@valentinewallace@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Clean up and more liberally free holding cell HTLCs (without re-entrancy) - #851

Merged
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get
May 24, 2021
Merged

Clean up and more liberally free holding cell HTLCs (without re-entrancy)#851
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This is #756 reworked a decent chunk. In #756 the reentrancy story around channel_monitor_updated scared me significantly, but for some reason I apparently never bothered to really think deeply about the options. Here we move the holding cell free to get_and_clear_pending_msg_events, which is a super obvious location for it - it should be called right after channel_monitor_updated in most cases, but shouldn't have the same reentrancy issues because it already can generate a monitor update. It requires some macro rework, but its not too bad.

@TheBlueMattTheBlueMatt added this to the 0.0.14 milestone Mar 18, 2021
@codecov

codecovBot commented Mar 19, 2021

Copy link
Copy Markdown

Codecov Report

Merging #851 (34fcd99) into main (b6de281) will increase coverage by 0.08%.
The diff coverage is 80.70%.

Impacted file tree graph

@@ Coverage Diff @@## main #851 +/- ##
==========================================
+ Coverage 90.48% 90.56% +0.08% 
==========================================
Files 59 59 Lines 29896 30015 +119 ==========================================
+ Hits 27050 27184 +134 + Misses 2846 2831 -15 
Impacted FilesCoverage Δ
lightning/src/ln/functional_test_utils.rs95.06% <ø> (ø)
lightning/src/ln/channelmanager.rs82.92% <55.55%> (-0.55%)⬇️
lightning/src/ln/chanmon_update_fail_tests.rs97.77% <99.20%> (+0.13%)⬆️
lightning/src/ln/channel.rs88.29% <100.00%> (+1.04%)⬆️
lightning/src/ln/functional_tests.rs96.87% <0.00%> (-0.04%)⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b6de281...34fcd99. Read the comment docs.

Comment threadlightning/src/ln/channelmanager.rs Outdated
order = RAACommitmentOrder::RevokeAndACKFirst;
}
return_monitor_err!(self, e, channel_state, chan, order, revoke_and_ack.is_some(), commitment_update.is_some());
//TODO: Resend the funding_locked if needed once we get the monitor running again

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This todo doesn't need to be migrated to the new code, right?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thats a good question! I don't think it matters - this PR switched the ordering of message resending to resend the funding_locked before handling the monitor update, meaning we'll never need to resend because we always send (instead of maybe not sending if the monitor update fails).

The "don't send a funding_locked if we're paused pending monitor update" thing isn't really a security thing (we just expose a next_per_commitment_point which doesn't allow our counterparty to....do anything), its just kinda nice to not tell our counterparty we're ready to accept HTLCs when we're definitely not. That said, Channel::channel_reestablish doesn't really handle it all that carefully anyway, and we may end up re-sending a funding_locked on reconnect when we are still pending a monitor update failure.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 0993c08 to b14ea14CompareMarch 25, 2021 19:21

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major. This looks pretty reasonable to me!

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment on lines +2305 to +2318
if self.channel_state >= ChannelState::ChannelFunded as u32 &&
(self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)) == 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just move these checks into free_holding_cell(..)? Also, revoke_and_ack currently calls the regular free_holding_cell_htlcs(..), wonder if it should call maybe_free instead?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, we already check all the preconditions at the callsites, I suppose we could move to just calling maybe_..., but that seems more brittle, given we already check them.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), channel_id);
handle_errors.push((chan.get_counterparty_node_id(), res));
if drop { return false; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so drop == true implies that we don't need to push the UpdateHTLCs event below? Maybe could use a comment for why.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed drop to close_channel is that more clear?

}

for (counterparty_node_id, err) in handle_errors.drain(..) {
let _ = handle_error!(self, err, counterparty_node_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it matters, but if we Permanently failed to update_channel above, we may still attempt another update_channel here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, right, that's awkward, but I think also globally true - we always generate a ChannelForceClosed update for the monitor, even if the user can't persist it.

// disconnect, we may be handed some HTLCs to fail backwards here.
assert!(htlcs_to_fail.is_empty());
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), shutdown_msg));
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), htlcs_to_fail, shutdown_msg));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a general point: might we wanna think about how this logic fits into "cancelling payments" in the future? Because I could see a situation where a user attempts a payments, which gets stuck in the holding cell, and they'd prefer to attempt the payment again by a different route and cancel the existing one.

@TheBlueMattTheBlueMattMar 26, 2021

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, possibly, but I'm not very worried. The holding cell is only really intended for things that are just pending some network latency - you should update a channel monitor or receive a revoke_and_ack from your peer within some hundreds of milliseconds, at max - not for things that are going to take a while - ie its not (really) for when your counterparty is offline.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from b14ea14 to af14d44CompareMarch 26, 2021 23:11

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ACK af14d44 modulo fixing 5613be8's commit message

Comment threadlightning/src/ln/channel.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from af14d44 to 386cc60CompareMarch 31, 2021 03:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed Val's comments, full diff is:

$ git diff-tree -U1 af14d44 386cc60
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index c40e7f1c4..a2bcede85 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -2309,2 +2309,3 @@ impl<Signer: Sign> Channel<Signer> {
}
+
/// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 73ba9d9bd..b538655bd 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -731,3 +731,3 @@ macro_rules! handle_monitor_err {
ChannelMonitorUpdateErr::PermanentFailure => {
- log_error!($self.logger, "Closing channel {} due to monitor update PermanentFailure", log_bytes!($chan_id[..]));
+ log_error!($self.logger, "Closing channel {} due to monitor update ChannelMonitorUpdateErr::PermanentFailure", log_bytes!($chan_id[..]));
if let Some(short_id) = $chan.get_short_channel_id() {

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 386cc60 to 0df3461CompareApril 20, 2021 18:40
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 3 times, most recently from 478044e to 72b730cCompareApril 21, 2021 22:25
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 72b730c to ffbe950CompareApril 28, 2021 00:02
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on upstream, with pretty trivial changes.

@TheBlueMattTheBlueMatt modified the milestones: 0.0.14, 0.0.15Apr 29, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Looks like this is slipping to 0.0.15. It would be good to land some of these fixes as they are potential bugs, even if very rare.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed until e33012f included so far

Comment threadfuzz/src/chanmon_consistency.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated

let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
if chanmon_update.is_some() {
// On reconnect (or monitor restoration), we, by definition, only resend a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think mentioning monitor restoration (monitor_updating_restored ?) is confusing as this function never returns a monitor update.

I would remove it and instead in the $funding_locked branch, say something "at monitor restoration, we might resend to our counterparty funding_locked but this event doesn't generate a channel monitor update itself"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you're right. I removed the reference to monitor restoration, but I'm not sure your comment communicates enough information to later re-analyze the situation going on.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm "In case of monitor restoration, funding_locked will never be generated as this event isn't altering monitor state"?

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
let funding_broadcastable: Option<Transaction> = $funding_broadcastable; // Force type-checking to resolve
macro_rules! handle_cs { () => {
if let Some(monitor_update) = chanmon_update {
assert!($order == RAACommitmentOrder::RevokeAndACKFirst);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make my mind about correctness of this statement. It implies the path in Channel::channel_reestablish L3023, when we return htlc failures to route backward included in a new commitment transaction ? It doesn't necessary mean peer send us a commitment_signed for which we should send a RAA /

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I can't convince myself its safe anymore, I walked this bit back.

Comment threadlightning/src/ln/channelmanager.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 4 times, most recently from 0b365ec to 1005f07CompareMay 13, 2021 20:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream, relevant changes responding to comments are in fixup commits.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left few more comments, top commits sounds good to me, but want to do a second parse with fixed comments.

Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
match chan.maybe_free_holding_cell_htlcs(&self.logger) {
Ok((None, ref htlcs)) if htlcs.is_empty() => true,
Ok((commitment_opt, failed_htlcs)) => {
forwarding_failed_htlcs.push((failed_htlcs, *channel_id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be backwarding_failed_htlcs here ? Per fail_holding_cell_htlcs comment, those htlcs are designated as "The HTLCs need to be failed backwards or, if they were one of our outgoing HTLCs".

Yeah likely, we should give a brush stroke at some point about the whole HTLC routing section with consistent naming and better comments.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that's confusing, the name was "htlcs which failed during forwarding" but that wasn't clear.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 2 times, most recently from 9b1aa05 to 7eaca87CompareMay 14, 2021 22:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I believe I've addressed all the comments.

The channel restoration code in channel monitor updating and peer
reconnection both do incredibly similar things, and there is
little reason to have them be separate. Sadly because they require
holding a lock with a reference to elements in the lock, its not
practical to make them utility functions, so instead we introduce
a two-step macro here which will eventually be used for both.
Because we still support pre-NLL Rust, the macro has to be in two
parts - one which runs with the channel_state lock, and one which
does not.
This mostly swaps some Vecs that can only ever contain one element
for Options.
This fails chanmon_consistency on IgnoreError error events and on
messages left over to be sent to a just-disconnected peer, which
should have been drained.
These should never appear, so consider them a fuzzer fail case.
Because of the merge between peer reconnection and channel monitor
updating channel restoration code, we now sometimes generate
(somewhat spurious) announcement signatures when restoring channel
monitor updating. This should not result in a fuzzing failure.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 365cee1 to 7fde891CompareMay 20, 2021 21:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no changes aside from two new fixup commits to respond to Jeff's comments.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I'd be more comfortable if @ariard gave a look before merging.

Comment threadlightning/src/ln/channelmanager.rs Outdated
This merges the code for restoring channel functionality between
channel monitor updating restored and peer reconnection, reducing
redundant code.
Both break_chan_entry and try_chan_entry do almost identical work,
only differing on if they `break` or `return` in response to an
error. Because we will now also need an option to do neither, we
break out the common code into a shared `convert_chan_err` macro.
If there is no pending channel update messages when monitor updating
is restored (though there may be an RAA to send), and we're
connected to our peer and not awaiting a remote RAA, we need to
free anything in our holding cell.
However, we don't want to immediately free the holding cell during
channel_monitor_updated as it presents a somewhat bug-prone case of
reentrancy:
a) it would re-enter user code around a monitor update while being
called from user code notifying us of the same monitor being
updated, making deadlocs very likely (in fact, our fuzzers
would have a bug here!),
b) the re-entrancy only occurs in a very rare case, making it
likely users will not hit it in testing, only deadlocking in
production.
Thus, we add a holding-cell-free pass over each channel in
get_and_clear_pending_msg_events. This fits up nicely with the
anticipated bug - users almost certainly need to process new
network messages immediately after monitor updating has been
restored to send messages which were not sent originally when the
monitor updating was paused.
Without this, chanmon_fail_consistency was able to find a stuck
condition where we sit on an HTLC failure in our holding cell and
don't ever handle it (at least until we have other actions to take
which empty the holding cell).
Because we may now generate a monitor update during
get_and_clear_pending_msg_events calls, we need to ensure we
re-serialize the relevant ChannelManager before attempting to
reload it, if such a monitor update occurred.
Previously, if we got disconnected from a peer while there were
HTLCs pending forwarding in the holding cell, we'd clear them and
fail them all backwards. This is largely fine, but since we now
have support for handling such HTLCs on reconnect, we might as
well not, instead relying on our timeout logic to fail them
backwards if it takes too long to forward them.
If the fuzz target is failing due to a channel force-close, the
immediately-visible error is that we're signing a stale state. This
is because the ChannelMonitorUpdateStep::ChannelForceClosed event
results in a signature in the test clone which was deserialized
using a OnlyReadsKeysInterface. Instead, we need to deserialize
using the full KeysInterface instance.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 7fde891 to 34fcd99CompareMay 21, 2021 15:10
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed and renamed one variable as @jkczyz suggested:

$ git diff-tree -U1 7fde8910 34fcd99f
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 14d9c528..9f9820c1 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -910,3 +910,3 @@ macro_rules! handle_chan_restoration_locked {
let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
- let chanmon_update_is_some = chanmon_update.is_some();
+ let chanmon_update_is_none = chanmon_update.is_none();
let res = loop {
@@ -1006,3 +1006,3 @@ macro_rules! handle_chan_restoration_locked {
- if !chanmon_update_is_some {
+ if chanmon_update_is_none {
// If there was no ChannelMonitorUpdate, we should never generate an Err in the res loop

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Gonna go ahead and take this since its been a long time. Post-merge review from @ariard would also be welcome but I'd rather not wait on it if he's busy.

@TheBlueMatt
TheBlueMatt merged commit 3a0356f into lightningdevkit:mainMay 24, 2021
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 24, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 29, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@TheBlueMatt@jkczyz@valentinewallace@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Clean up and more liberally free holding cell HTLCs (without re-entrancy) - #851

Merged
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get
May 24, 2021
Merged

Clean up and more liberally free holding cell HTLCs (without re-entrancy)#851
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This is #756 reworked a decent chunk. In #756 the reentrancy story around channel_monitor_updated scared me significantly, but for some reason I apparently never bothered to really think deeply about the options. Here we move the holding cell free to get_and_clear_pending_msg_events, which is a super obvious location for it - it should be called right after channel_monitor_updated in most cases, but shouldn't have the same reentrancy issues because it already can generate a monitor update. It requires some macro rework, but its not too bad.

@TheBlueMattTheBlueMatt added this to the 0.0.14 milestone Mar 18, 2021
@codecov

codecovBot commented Mar 19, 2021

Copy link
Copy Markdown

Codecov Report

Merging #851 (34fcd99) into main (b6de281) will increase coverage by 0.08%.
The diff coverage is 80.70%.

Impacted file tree graph

@@ Coverage Diff @@## main #851 +/- ##
==========================================
+ Coverage 90.48% 90.56% +0.08% 
==========================================
Files 59 59 Lines 29896 30015 +119 ==========================================
+ Hits 27050 27184 +134 + Misses 2846 2831 -15 
Impacted FilesCoverage Δ
lightning/src/ln/functional_test_utils.rs95.06% <ø> (ø)
lightning/src/ln/channelmanager.rs82.92% <55.55%> (-0.55%)⬇️
lightning/src/ln/chanmon_update_fail_tests.rs97.77% <99.20%> (+0.13%)⬆️
lightning/src/ln/channel.rs88.29% <100.00%> (+1.04%)⬆️
lightning/src/ln/functional_tests.rs96.87% <0.00%> (-0.04%)⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b6de281...34fcd99. Read the comment docs.

Comment threadlightning/src/ln/channelmanager.rs Outdated
order = RAACommitmentOrder::RevokeAndACKFirst;
}
return_monitor_err!(self, e, channel_state, chan, order, revoke_and_ack.is_some(), commitment_update.is_some());
//TODO: Resend the funding_locked if needed once we get the monitor running again

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This todo doesn't need to be migrated to the new code, right?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thats a good question! I don't think it matters - this PR switched the ordering of message resending to resend the funding_locked before handling the monitor update, meaning we'll never need to resend because we always send (instead of maybe not sending if the monitor update fails).

The "don't send a funding_locked if we're paused pending monitor update" thing isn't really a security thing (we just expose a next_per_commitment_point which doesn't allow our counterparty to....do anything), its just kinda nice to not tell our counterparty we're ready to accept HTLCs when we're definitely not. That said, Channel::channel_reestablish doesn't really handle it all that carefully anyway, and we may end up re-sending a funding_locked on reconnect when we are still pending a monitor update failure.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 0993c08 to b14ea14CompareMarch 25, 2021 19:21

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major. This looks pretty reasonable to me!

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment on lines +2305 to +2318
if self.channel_state >= ChannelState::ChannelFunded as u32 &&
(self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)) == 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just move these checks into free_holding_cell(..)? Also, revoke_and_ack currently calls the regular free_holding_cell_htlcs(..), wonder if it should call maybe_free instead?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, we already check all the preconditions at the callsites, I suppose we could move to just calling maybe_..., but that seems more brittle, given we already check them.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), channel_id);
handle_errors.push((chan.get_counterparty_node_id(), res));
if drop { return false; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so drop == true implies that we don't need to push the UpdateHTLCs event below? Maybe could use a comment for why.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed drop to close_channel is that more clear?

}

for (counterparty_node_id, err) in handle_errors.drain(..) {
let _ = handle_error!(self, err, counterparty_node_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it matters, but if we Permanently failed to update_channel above, we may still attempt another update_channel here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, right, that's awkward, but I think also globally true - we always generate a ChannelForceClosed update for the monitor, even if the user can't persist it.

// disconnect, we may be handed some HTLCs to fail backwards here.
assert!(htlcs_to_fail.is_empty());
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), shutdown_msg));
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), htlcs_to_fail, shutdown_msg));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a general point: might we wanna think about how this logic fits into "cancelling payments" in the future? Because I could see a situation where a user attempts a payments, which gets stuck in the holding cell, and they'd prefer to attempt the payment again by a different route and cancel the existing one.

@TheBlueMattTheBlueMattMar 26, 2021

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, possibly, but I'm not very worried. The holding cell is only really intended for things that are just pending some network latency - you should update a channel monitor or receive a revoke_and_ack from your peer within some hundreds of milliseconds, at max - not for things that are going to take a while - ie its not (really) for when your counterparty is offline.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from b14ea14 to af14d44CompareMarch 26, 2021 23:11

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ACK af14d44 modulo fixing 5613be8's commit message

Comment threadlightning/src/ln/channel.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from af14d44 to 386cc60CompareMarch 31, 2021 03:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed Val's comments, full diff is:

$ git diff-tree -U1 af14d44 386cc60
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index c40e7f1c4..a2bcede85 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -2309,2 +2309,3 @@ impl<Signer: Sign> Channel<Signer> {
}
+
/// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 73ba9d9bd..b538655bd 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -731,3 +731,3 @@ macro_rules! handle_monitor_err {
ChannelMonitorUpdateErr::PermanentFailure => {
- log_error!($self.logger, "Closing channel {} due to monitor update PermanentFailure", log_bytes!($chan_id[..]));
+ log_error!($self.logger, "Closing channel {} due to monitor update ChannelMonitorUpdateErr::PermanentFailure", log_bytes!($chan_id[..]));
if let Some(short_id) = $chan.get_short_channel_id() {

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 386cc60 to 0df3461CompareApril 20, 2021 18:40
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 3 times, most recently from 478044e to 72b730cCompareApril 21, 2021 22:25
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 72b730c to ffbe950CompareApril 28, 2021 00:02
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on upstream, with pretty trivial changes.

@TheBlueMattTheBlueMatt modified the milestones: 0.0.14, 0.0.15Apr 29, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Looks like this is slipping to 0.0.15. It would be good to land some of these fixes as they are potential bugs, even if very rare.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed until e33012f included so far

Comment threadfuzz/src/chanmon_consistency.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated

let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
if chanmon_update.is_some() {
// On reconnect (or monitor restoration), we, by definition, only resend a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think mentioning monitor restoration (monitor_updating_restored ?) is confusing as this function never returns a monitor update.

I would remove it and instead in the $funding_locked branch, say something "at monitor restoration, we might resend to our counterparty funding_locked but this event doesn't generate a channel monitor update itself"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you're right. I removed the reference to monitor restoration, but I'm not sure your comment communicates enough information to later re-analyze the situation going on.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm "In case of monitor restoration, funding_locked will never be generated as this event isn't altering monitor state"?

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
let funding_broadcastable: Option<Transaction> = $funding_broadcastable; // Force type-checking to resolve
macro_rules! handle_cs { () => {
if let Some(monitor_update) = chanmon_update {
assert!($order == RAACommitmentOrder::RevokeAndACKFirst);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make my mind about correctness of this statement. It implies the path in Channel::channel_reestablish L3023, when we return htlc failures to route backward included in a new commitment transaction ? It doesn't necessary mean peer send us a commitment_signed for which we should send a RAA /

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I can't convince myself its safe anymore, I walked this bit back.

Comment threadlightning/src/ln/channelmanager.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 4 times, most recently from 0b365ec to 1005f07CompareMay 13, 2021 20:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream, relevant changes responding to comments are in fixup commits.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left few more comments, top commits sounds good to me, but want to do a second parse with fixed comments.

Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
match chan.maybe_free_holding_cell_htlcs(&self.logger) {
Ok((None, ref htlcs)) if htlcs.is_empty() => true,
Ok((commitment_opt, failed_htlcs)) => {
forwarding_failed_htlcs.push((failed_htlcs, *channel_id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be backwarding_failed_htlcs here ? Per fail_holding_cell_htlcs comment, those htlcs are designated as "The HTLCs need to be failed backwards or, if they were one of our outgoing HTLCs".

Yeah likely, we should give a brush stroke at some point about the whole HTLC routing section with consistent naming and better comments.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that's confusing, the name was "htlcs which failed during forwarding" but that wasn't clear.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 2 times, most recently from 9b1aa05 to 7eaca87CompareMay 14, 2021 22:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I believe I've addressed all the comments.

The channel restoration code in channel monitor updating and peer
reconnection both do incredibly similar things, and there is
little reason to have them be separate. Sadly because they require
holding a lock with a reference to elements in the lock, its not
practical to make them utility functions, so instead we introduce
a two-step macro here which will eventually be used for both.
Because we still support pre-NLL Rust, the macro has to be in two
parts - one which runs with the channel_state lock, and one which
does not.
This mostly swaps some Vecs that can only ever contain one element
for Options.
This fails chanmon_consistency on IgnoreError error events and on
messages left over to be sent to a just-disconnected peer, which
should have been drained.
These should never appear, so consider them a fuzzer fail case.
Because of the merge between peer reconnection and channel monitor
updating channel restoration code, we now sometimes generate
(somewhat spurious) announcement signatures when restoring channel
monitor updating. This should not result in a fuzzing failure.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 365cee1 to 7fde891CompareMay 20, 2021 21:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no changes aside from two new fixup commits to respond to Jeff's comments.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I'd be more comfortable if @ariard gave a look before merging.

Comment threadlightning/src/ln/channelmanager.rs Outdated
This merges the code for restoring channel functionality between
channel monitor updating restored and peer reconnection, reducing
redundant code.
Both break_chan_entry and try_chan_entry do almost identical work,
only differing on if they `break` or `return` in response to an
error. Because we will now also need an option to do neither, we
break out the common code into a shared `convert_chan_err` macro.
If there is no pending channel update messages when monitor updating
is restored (though there may be an RAA to send), and we're
connected to our peer and not awaiting a remote RAA, we need to
free anything in our holding cell.
However, we don't want to immediately free the holding cell during
channel_monitor_updated as it presents a somewhat bug-prone case of
reentrancy:
a) it would re-enter user code around a monitor update while being
called from user code notifying us of the same monitor being
updated, making deadlocs very likely (in fact, our fuzzers
would have a bug here!),
b) the re-entrancy only occurs in a very rare case, making it
likely users will not hit it in testing, only deadlocking in
production.
Thus, we add a holding-cell-free pass over each channel in
get_and_clear_pending_msg_events. This fits up nicely with the
anticipated bug - users almost certainly need to process new
network messages immediately after monitor updating has been
restored to send messages which were not sent originally when the
monitor updating was paused.
Without this, chanmon_fail_consistency was able to find a stuck
condition where we sit on an HTLC failure in our holding cell and
don't ever handle it (at least until we have other actions to take
which empty the holding cell).
Because we may now generate a monitor update during
get_and_clear_pending_msg_events calls, we need to ensure we
re-serialize the relevant ChannelManager before attempting to
reload it, if such a monitor update occurred.
Previously, if we got disconnected from a peer while there were
HTLCs pending forwarding in the holding cell, we'd clear them and
fail them all backwards. This is largely fine, but since we now
have support for handling such HTLCs on reconnect, we might as
well not, instead relying on our timeout logic to fail them
backwards if it takes too long to forward them.
If the fuzz target is failing due to a channel force-close, the
immediately-visible error is that we're signing a stale state. This
is because the ChannelMonitorUpdateStep::ChannelForceClosed event
results in a signature in the test clone which was deserialized
using a OnlyReadsKeysInterface. Instead, we need to deserialize
using the full KeysInterface instance.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 7fde891 to 34fcd99CompareMay 21, 2021 15:10
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed and renamed one variable as @jkczyz suggested:

$ git diff-tree -U1 7fde8910 34fcd99f
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 14d9c528..9f9820c1 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -910,3 +910,3 @@ macro_rules! handle_chan_restoration_locked {
let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
- let chanmon_update_is_some = chanmon_update.is_some();
+ let chanmon_update_is_none = chanmon_update.is_none();
let res = loop {
@@ -1006,3 +1006,3 @@ macro_rules! handle_chan_restoration_locked {
- if !chanmon_update_is_some {
+ if chanmon_update_is_none {
// If there was no ChannelMonitorUpdate, we should never generate an Err in the res loop

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Gonna go ahead and take this since its been a long time. Post-merge review from @ariard would also be welcome but I'd rather not wait on it if he's busy.

@TheBlueMatt
TheBlueMatt merged commit 3a0356f into lightningdevkit:mainMay 24, 2021
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 24, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 29, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@TheBlueMatt@jkczyz@valentinewallace@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Clean up and more liberally free holding cell HTLCs (without re-entrancy) - #851

Merged
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get
May 24, 2021
Merged

Clean up and more liberally free holding cell HTLCs (without re-entrancy)#851
TheBlueMatt merged 15 commits into
lightningdevkit:mainfrom
TheBlueMatt:2021-03-holding-cell-clear-msg-get

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This is #756 reworked a decent chunk. In #756 the reentrancy story around channel_monitor_updated scared me significantly, but for some reason I apparently never bothered to really think deeply about the options. Here we move the holding cell free to get_and_clear_pending_msg_events, which is a super obvious location for it - it should be called right after channel_monitor_updated in most cases, but shouldn't have the same reentrancy issues because it already can generate a monitor update. It requires some macro rework, but its not too bad.

@TheBlueMattTheBlueMatt added this to the 0.0.14 milestone Mar 18, 2021
@codecov

codecovBot commented Mar 19, 2021

Copy link
Copy Markdown

Codecov Report

Merging #851 (34fcd99) into main (b6de281) will increase coverage by 0.08%.
The diff coverage is 80.70%.

Impacted file tree graph

@@ Coverage Diff @@## main #851 +/- ##
==========================================
+ Coverage 90.48% 90.56% +0.08% 
==========================================
Files 59 59 Lines 29896 30015 +119 ==========================================
+ Hits 27050 27184 +134 + Misses 2846 2831 -15 
Impacted FilesCoverage Δ
lightning/src/ln/functional_test_utils.rs95.06% <ø> (ø)
lightning/src/ln/channelmanager.rs82.92% <55.55%> (-0.55%)⬇️
lightning/src/ln/chanmon_update_fail_tests.rs97.77% <99.20%> (+0.13%)⬆️
lightning/src/ln/channel.rs88.29% <100.00%> (+1.04%)⬆️
lightning/src/ln/functional_tests.rs96.87% <0.00%> (-0.04%)⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b6de281...34fcd99. Read the comment docs.

Comment threadlightning/src/ln/channelmanager.rs Outdated
order = RAACommitmentOrder::RevokeAndACKFirst;
}
return_monitor_err!(self, e, channel_state, chan, order, revoke_and_ack.is_some(), commitment_update.is_some());
//TODO: Resend the funding_locked if needed once we get the monitor running again

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This todo doesn't need to be migrated to the new code, right?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thats a good question! I don't think it matters - this PR switched the ordering of message resending to resend the funding_locked before handling the monitor update, meaning we'll never need to resend because we always send (instead of maybe not sending if the monitor update fails).

The "don't send a funding_locked if we're paused pending monitor update" thing isn't really a security thing (we just expose a next_per_commitment_point which doesn't allow our counterparty to....do anything), its just kinda nice to not tell our counterparty we're ready to accept HTLCs when we're definitely not. That said, Channel::channel_reestablish doesn't really handle it all that carefully anyway, and we may end up re-sending a funding_locked on reconnect when we are still pending a monitor update failure.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 0993c08 to b14ea14CompareMarch 25, 2021 19:21

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major. This looks pretty reasonable to me!

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment on lines +2305 to +2318
if self.channel_state >= ChannelState::ChannelFunded as u32 &&
(self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)) == 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just move these checks into free_holding_cell(..)? Also, revoke_and_ack currently calls the regular free_holding_cell_htlcs(..), wonder if it should call maybe_free instead?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, we already check all the preconditions at the callsites, I suppose we could move to just calling maybe_..., but that seems more brittle, given we already check them.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), channel_id);
handle_errors.push((chan.get_counterparty_node_id(), res));
if drop { return false; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so drop == true implies that we don't need to push the UpdateHTLCs event below? Maybe could use a comment for why.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed drop to close_channel is that more clear?

}

for (counterparty_node_id, err) in handle_errors.drain(..) {
let _ = handle_error!(self, err, counterparty_node_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it matters, but if we Permanently failed to update_channel above, we may still attempt another update_channel here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, right, that's awkward, but I think also globally true - we always generate a ChannelForceClosed update for the monitor, even if the user can't persist it.

// disconnect, we may be handed some HTLCs to fail backwards here.
assert!(htlcs_to_fail.is_empty());
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), shutdown_msg));
return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), htlcs_to_fail, shutdown_msg));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a general point: might we wanna think about how this logic fits into "cancelling payments" in the future? Because I could see a situation where a user attempts a payments, which gets stuck in the holding cell, and they'd prefer to attempt the payment again by a different route and cancel the existing one.

@TheBlueMattTheBlueMattMar 26, 2021

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, possibly, but I'm not very worried. The holding cell is only really intended for things that are just pending some network latency - you should update a channel monitor or receive a revoke_and_ack from your peer within some hundreds of milliseconds, at max - not for things that are going to take a while - ie its not (really) for when your counterparty is offline.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from b14ea14 to af14d44CompareMarch 26, 2021 23:11

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ACK af14d44 modulo fixing 5613be8's commit message

Comment threadlightning/src/ln/channel.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from af14d44 to 386cc60CompareMarch 31, 2021 03:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed Val's comments, full diff is:

$ git diff-tree -U1 af14d44 386cc60
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index c40e7f1c4..a2bcede85 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -2309,2 +2309,3 @@ impl<Signer: Sign> Channel<Signer> {
}
+
/// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 73ba9d9bd..b538655bd 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -731,3 +731,3 @@ macro_rules! handle_monitor_err {
ChannelMonitorUpdateErr::PermanentFailure => {
- log_error!($self.logger, "Closing channel {} due to monitor update PermanentFailure", log_bytes!($chan_id[..]));
+ log_error!($self.logger, "Closing channel {} due to monitor update ChannelMonitorUpdateErr::PermanentFailure", log_bytes!($chan_id[..]));
if let Some(short_id) = $chan.get_short_channel_id() {

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 386cc60 to 0df3461CompareApril 20, 2021 18:40
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 3 times, most recently from 478044e to 72b730cCompareApril 21, 2021 22:25
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 72b730c to ffbe950CompareApril 28, 2021 00:02
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on upstream, with pretty trivial changes.

@TheBlueMattTheBlueMatt modified the milestones: 0.0.14, 0.0.15Apr 29, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Looks like this is slipping to 0.0.15. It would be good to land some of these fixes as they are potential bugs, even if very rare.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed until e33012f included so far

Comment threadfuzz/src/chanmon_consistency.rs
Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated

let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
if chanmon_update.is_some() {
// On reconnect (or monitor restoration), we, by definition, only resend a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think mentioning monitor restoration (monitor_updating_restored ?) is confusing as this function never returns a monitor update.

I would remove it and instead in the $funding_locked branch, say something "at monitor restoration, we might resend to our counterparty funding_locked but this event doesn't generate a channel monitor update itself"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you're right. I removed the reference to monitor restoration, but I'm not sure your comment communicates enough information to later re-analyze the situation going on.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm "In case of monitor restoration, funding_locked will never be generated as this event isn't altering monitor state"?

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
let funding_broadcastable: Option<Transaction> = $funding_broadcastable; // Force type-checking to resolve
macro_rules! handle_cs { () => {
if let Some(monitor_update) = chanmon_update {
assert!($order == RAACommitmentOrder::RevokeAndACKFirst);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make my mind about correctness of this statement. It implies the path in Channel::channel_reestablish L3023, when we return htlc failures to route backward included in a new commitment transaction ? It doesn't necessary mean peer send us a commitment_signed for which we should send a RAA /

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I can't convince myself its safe anymore, I walked this bit back.

Comment threadlightning/src/ln/channelmanager.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 4 times, most recently from 0b365ec to 1005f07CompareMay 13, 2021 20:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased on latest upstream, relevant changes responding to comments are in fixup commits.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left few more comments, top commits sounds good to me, but want to do a second parse with fixed comments.

Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
match chan.maybe_free_holding_cell_htlcs(&self.logger) {
Ok((None, ref htlcs)) if htlcs.is_empty() => true,
Ok((commitment_opt, failed_htlcs)) => {
forwarding_failed_htlcs.push((failed_htlcs, *channel_id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be backwarding_failed_htlcs here ? Per fail_holding_cell_htlcs comment, those htlcs are designated as "The HTLCs need to be failed backwards or, if they were one of our outgoing HTLCs".

Yeah likely, we should give a brush stroke at some point about the whole HTLC routing section with consistent naming and better comments.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that's confusing, the name was "htlcs which failed during forwarding" but that wasn't clear.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch 2 times, most recently from 9b1aa05 to 7eaca87CompareMay 14, 2021 22:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I believe I've addressed all the comments.

The channel restoration code in channel monitor updating and peer
reconnection both do incredibly similar things, and there is
little reason to have them be separate. Sadly because they require
holding a lock with a reference to elements in the lock, its not
practical to make them utility functions, so instead we introduce
a two-step macro here which will eventually be used for both.
Because we still support pre-NLL Rust, the macro has to be in two
parts - one which runs with the channel_state lock, and one which
does not.
This mostly swaps some Vecs that can only ever contain one element
for Options.
This fails chanmon_consistency on IgnoreError error events and on
messages left over to be sent to a just-disconnected peer, which
should have been drained.
These should never appear, so consider them a fuzzer fail case.
Because of the merge between peer reconnection and channel monitor
updating channel restoration code, we now sometimes generate
(somewhat spurious) announcement signatures when restoring channel
monitor updating. This should not result in a fuzzing failure.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 365cee1 to 7fde891CompareMay 20, 2021 21:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no changes aside from two new fixup commits to respond to Jeff's comments.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I'd be more comfortable if @ariard gave a look before merging.

Comment threadlightning/src/ln/channelmanager.rs Outdated
This merges the code for restoring channel functionality between
channel monitor updating restored and peer reconnection, reducing
redundant code.
Both break_chan_entry and try_chan_entry do almost identical work,
only differing on if they `break` or `return` in response to an
error. Because we will now also need an option to do neither, we
break out the common code into a shared `convert_chan_err` macro.
If there is no pending channel update messages when monitor updating
is restored (though there may be an RAA to send), and we're
connected to our peer and not awaiting a remote RAA, we need to
free anything in our holding cell.
However, we don't want to immediately free the holding cell during
channel_monitor_updated as it presents a somewhat bug-prone case of
reentrancy:
a) it would re-enter user code around a monitor update while being
called from user code notifying us of the same monitor being
updated, making deadlocs very likely (in fact, our fuzzers
would have a bug here!),
b) the re-entrancy only occurs in a very rare case, making it
likely users will not hit it in testing, only deadlocking in
production.
Thus, we add a holding-cell-free pass over each channel in
get_and_clear_pending_msg_events. This fits up nicely with the
anticipated bug - users almost certainly need to process new
network messages immediately after monitor updating has been
restored to send messages which were not sent originally when the
monitor updating was paused.
Without this, chanmon_fail_consistency was able to find a stuck
condition where we sit on an HTLC failure in our holding cell and
don't ever handle it (at least until we have other actions to take
which empty the holding cell).
Because we may now generate a monitor update during
get_and_clear_pending_msg_events calls, we need to ensure we
re-serialize the relevant ChannelManager before attempting to
reload it, if such a monitor update occurred.
Previously, if we got disconnected from a peer while there were
HTLCs pending forwarding in the holding cell, we'd clear them and
fail them all backwards. This is largely fine, but since we now
have support for handling such HTLCs on reconnect, we might as
well not, instead relying on our timeout logic to fail them
backwards if it takes too long to forward them.
If the fuzz target is failing due to a channel force-close, the
immediately-visible error is that we're signing a stale state. This
is because the ChannelMonitorUpdateStep::ChannelForceClosed event
results in a signature in the test clone which was deserialized
using a OnlyReadsKeysInterface. Instead, we need to deserialize
using the full KeysInterface instance.
@TheBlueMatt
TheBlueMattforce-pushed the 2021-03-holding-cell-clear-msg-get branch from 7fde891 to 34fcd99CompareMay 21, 2021 15:10
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed and renamed one variable as @jkczyz suggested:

$ git diff-tree -U1 7fde8910 34fcd99f
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 14d9c528..9f9820c1 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -910,3 +910,3 @@ macro_rules! handle_chan_restoration_locked {
let chanmon_update: Option<ChannelMonitorUpdate> = $chanmon_update; // Force type-checking to resolve
- let chanmon_update_is_some = chanmon_update.is_some();
+ let chanmon_update_is_none = chanmon_update.is_none();
let res = loop {
@@ -1006,3 +1006,3 @@ macro_rules! handle_chan_restoration_locked {
- if !chanmon_update_is_some {
+ if chanmon_update_is_none {
// If there was no ChannelMonitorUpdate, we should never generate an Err in the res loop

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Gonna go ahead and take this since its been a long time. Post-merge review from @ariard would also be welcome but I'd rather not wait on it if he's busy.

@TheBlueMatt
TheBlueMatt merged commit 3a0356f into lightningdevkit:mainMay 24, 2021
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 17, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 24, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 29, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we almost certainly want
`Channel::is_live()` to include channels which are simply pending a
monitor update, as some clients may update monitors asynchronously,
thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -
instead of having to ensure that we always have a matching holding
cell free any time we add something to the holding cell, we can
simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jun 30, 2021
We use `Channel::is_live()` to gate inclusion of a channel in
`ChannelManager::list_usable_channels()` and when sending an
HTLC to select whether a channel is available for
forwarding through/sending to.
In both of these cases, we should consider a channel `is_live()` when
they are pending a monitor update. Some clients may update monitors
asynchronously, thus we may simply be waiting a short duration for a
monitor update to complete, and shouldn't fail all forwarding HTLCs
during that time.
After lightningdevkit#851, we always ensure any holding cells are free'd when
sending P2P messages, making this change much more trivially
correct - instead of having to ensure that we always free the holding
cell when a channel becomes live again after adding something to the
holding cell, we can simply rely on the fact that it always happens.
Fixeslightningdevkit#661.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@TheBlueMatt@jkczyz@valentinewallace@ariard