Consider channels "live" even if they are awaiting a monitor update - #954

Merged
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails
Jul 1, 2021
Merged

Consider channels "live" even if they are awaiting a monitor update#954
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

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 #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.

Fixes#661.

This should make #949's tests a bit easier to implement.

@codecov

codecovBot commented Jun 17, 2021

Copy link
Copy Markdown

Codecov Report

Merging #954 (0534e8b) into main (2940099) will increase coverage by 1.47%.
The diff coverage is 91.30%.

❗ Current head 0534e8b differs from pull request most recent head b58c884. Consider uploading reports for the commit b58c884 to get more accurate results
Impacted file tree graph

@@ Coverage Diff @@## main #954 +/- ##
==========================================
+ Coverage 90.59% 92.06% +1.47% 
==========================================
Files 60 60 Lines 30423 39444 +9021 ==========================================
+ Hits 27561 36314 +8753 - Misses 2862 3130 +268 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs87.18% <ø> (+3.27%)⬆️
lightning/src/ln/channel.rs92.86% <87.50%> (+4.57%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.60% <91.58%> (-0.19%)⬇️
lightning/src/ln/wire.rs60.52% <0.00%> (-3.58%)⬇️
lightning/src/util/macro_logger.rs87.87% <0.00%> (-1.19%)⬇️
lightning/src/ln/functional_test_utils.rs94.79% <0.00%> (-0.03%)⬇️
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (+0.37%)⬆️
lightning/src/chain/chainmonitor.rs96.21% <0.00%> (+0.41%)⬆️
lightning/src/util/ser.rs91.70% <0.00%> (+0.43%)⬆️
... and 20 more

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 2940099...b58c884. Read the comment docs.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed a few comments left on other PRs, including #949 (comment)

@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.

Just need to fix send_htlc doc I believe, otherwise good to me

Comment threadlightning/src/ln/channel.rs
if (self.channel_state & (ChannelState::PeerDisconnected as u32)) != 0 {
// Note that this should never really happen, if we're !is_live() on receipt of an
// incoming HTLC for relay will result in us rejecting the HTLC and we won't allow
// the user to send directly into a !is_live() channel. However, if we

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This comment sounds weird, I still think we should be able to receive a HTLC even if the forwarding channel is knock-out, though we don't currently in decode_update_add_htlc_onion. Maybe possible after #680.

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.

Right, I suppose its reasonable to accept an HTLC for forwarding to a channel which is disconnected currently if you're a special type of node that can make the channel become connected soon (of course for a general purpose node we should definitely fail). There isn't a lot of harm in waiting to fail until the payment gets to the send_htlc call (instead of doing it immediately upon receipt), so I think I agree we should just drop it (but keep this return).

All that to say, I don't think anything needs to change in this PR, correct? The comment is still true today, though I agree we could change it in a followup and should change it after #680.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah I just believe this forwarding policy makes really sense if the next hop is owned by a mobile client, with a clear agreement "I'm going to be offline 99% of my time, please cache my HTLC" ? Or at least it would be worthy as an experimentation.

Right, it's more a good-place-to-clean-once-we-have-680.

@ariard

Copy link
Copy Markdown

Code Review ACK 8671aca

@TheBlueMattTheBlueMatt added this to the 0.0.99 milestone Jun 26, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Tagging as 0.0.99 as #949 depends on this and because this would be critical for any async-monitor-update routing node.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 8671aca to eab577cCompareJune 29, 2021 23:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree 8671aca5 eab577c0
$

@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.

Somethings aren't clear to me when reading the commit message / PR description:

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,

Either drop "almost certainly" or explain when this is not the case.

thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.

Reads strange. Start new sentence here and replace "causing" with "causes"?

After #851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -

What is "this" referring to? "this change"?

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.

Unclear to me what is meant by a "matching holding cell" (matching what?) and what "it always happens" is referring to.

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/chanmon_update_fail_tests.rs Outdated
commitment_signed_dance!(nodes[1], nodes[0], send_event.commitment_msg, false, true);
check_added_monitors!(nodes[1], 0);

let mut events_2 = nodes[1].node.get_and_clear_pending_msg_events();

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.

Is there a TL;DR to summarize the changes to this test to help in review?

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.

Because it is no longer the case that receiving an HTLC while we're pending a monitor update restoration causes the HTLC to fail, code which relied on that behavior broke. eg the first code hunk which was removed explicitly delivered an HTLC and expected it to fail, which no longer happens.

The big if-block diff hunk is because the two cases have converged somewhat - we now always have a payment in node[1]'s outbound holding cell.


*nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
assert!(nodes[1].node.claim_funds(payment_preimage_1));
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Temporary failure claiming HTLC, treating as success: Failed to update ChannelMonitor".to_string(), 1);

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.

Is there any way to assert the state of the channel rather than examining the log?

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.

This line was only moved, not created. the log assert is nice IMO as it checks the specific error, not just that one happened, though the observable state is still checked in that no new message was generated.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from eab577c to 0534e8bCompareJune 30, 2021 19:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

@jkczyz

Copy link
Copy Markdown
Contributor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

Looks good. Thanks for doing it!

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
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 0534e8b to b58c884CompareJune 30, 2021 23:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree -U1 0534e8b9 b58c8843
$

The full diff from the previous ack is still trivial, so will merge after CI:

$ git diff-tree -U1 -b eab577c0 b58c8843
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index ac293cc1..73fc5315 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -908,4 +908,4 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Attempt to forward a third payment but fail due to the second channel being unavailable
-	// for forwarding.
+	// Forward a third payment which will also be added to the holding cell, despite the channel
+	// being paused waiting a monitor update.
let (_, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[2]);
@@ -924,2 +924,8 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
+	// Call forward_pending_htlcs and check that the new HTLC was simply added to the holding cell
+	// and not forwarded.
+	expect_pending_htlcs_forwardable!(nodes[1]);
+	check_added_monitors!(nodes[1], 0);
+	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+
let (payment_preimage_4, payment_hash_4) = if test_ignore_second_cs {
@@ -942,8 +948,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Call forward_pending_htlcs first to make sure we don't have any issues attempting (and
-	// failing) to forward an HTLC while a channel is still awaiting monitor update restoration.
-	expect_pending_htlcs_forwardable!(nodes[1]);
-	check_added_monitors!(nodes[1], 0);
-	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
-
// Restore monitor updating, ensuring we immediately get a fail-back update and a
@@ -1048,2 +1048,3 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
}
+
assert_eq!(as_cs.update_add_htlcs.len(), 1);
@@ -1077,3 +1078,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-
expect_pending_htlcs_forwardable!(nodes[2]);
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 19e28d4a..26725111 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -3976,10 +3976,14 @@ impl<Signer: Sign> Channel<Signer> {
/// send_htlc_and_commit instead cause you'll want both messages at once.
-	/// This returns an option instead of a pure UpdateAddHTLC as we may be in a state where we
-	/// cannot add HTLCs on the wire.
-	/// In cases where we're waiting on the remote peer to send us a revoke_and_ack, if we did, we
-	/// wouldn't be able to determine what they actually ACK'ed when we do receive the
-	/// revoke_and_ack.
-	/// In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we may
-	/// not yet have sent the previous commitment update messages and will need to regenerate them.
-	/// You MUST call send_commitment prior to any other calls on this Channel
+	///
+	/// This returns an optional UpdateAddHTLC as we may be in a state where we cannot add HTLCs on
+	/// the wire:
+	/// * In cases where we're waiting on the remote peer to send us a revoke_and_ack, we
+	/// wouldn't be able to determine what they actually ACK'ed if we have two sets of updates
+	/// awaiting ACK.
+	/// * In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we
+	/// may not yet have sent the previous commitment update messages and will need to regenerate
+	/// them.
+	///
+	/// You MUST call send_commitment prior to calling any other methods on this Channel!
+	///
/// If an Err is returned, it's a ChannelError::Ignore!
$

@TheBlueMatt
TheBlueMatt merged commit 4353d4a into lightningdevkit:mainJul 1, 2021
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.

Spurious Forwarding Failures in Async Monitor Update Clients

3 participants

@TheBlueMatt@ariard@jkczyz
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n 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;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} 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

Consider channels "live" even if they are awaiting a monitor update - #954

Merged
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails
Jul 1, 2021
Merged

Consider channels "live" even if they are awaiting a monitor update#954
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

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 #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.

Fixes#661.

This should make #949's tests a bit easier to implement.

@codecov

codecovBot commented Jun 17, 2021

Copy link
Copy Markdown

Codecov Report

Merging #954 (0534e8b) into main (2940099) will increase coverage by 1.47%.
The diff coverage is 91.30%.

❗ Current head 0534e8b differs from pull request most recent head b58c884. Consider uploading reports for the commit b58c884 to get more accurate results
Impacted file tree graph

@@ Coverage Diff @@## main #954 +/- ##
==========================================
+ Coverage 90.59% 92.06% +1.47% 
==========================================
Files 60 60 Lines 30423 39444 +9021 ==========================================
+ Hits 27561 36314 +8753 - Misses 2862 3130 +268 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs87.18% <ø> (+3.27%)⬆️
lightning/src/ln/channel.rs92.86% <87.50%> (+4.57%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.60% <91.58%> (-0.19%)⬇️
lightning/src/ln/wire.rs60.52% <0.00%> (-3.58%)⬇️
lightning/src/util/macro_logger.rs87.87% <0.00%> (-1.19%)⬇️
lightning/src/ln/functional_test_utils.rs94.79% <0.00%> (-0.03%)⬇️
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (+0.37%)⬆️
lightning/src/chain/chainmonitor.rs96.21% <0.00%> (+0.41%)⬆️
lightning/src/util/ser.rs91.70% <0.00%> (+0.43%)⬆️
... and 20 more

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 2940099...b58c884. Read the comment docs.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed a few comments left on other PRs, including #949 (comment)

@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.

Just need to fix send_htlc doc I believe, otherwise good to me

Comment threadlightning/src/ln/channel.rs
if (self.channel_state & (ChannelState::PeerDisconnected as u32)) != 0 {
// Note that this should never really happen, if we're !is_live() on receipt of an
// incoming HTLC for relay will result in us rejecting the HTLC and we won't allow
// the user to send directly into a !is_live() channel. However, if we

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This comment sounds weird, I still think we should be able to receive a HTLC even if the forwarding channel is knock-out, though we don't currently in decode_update_add_htlc_onion. Maybe possible after #680.

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.

Right, I suppose its reasonable to accept an HTLC for forwarding to a channel which is disconnected currently if you're a special type of node that can make the channel become connected soon (of course for a general purpose node we should definitely fail). There isn't a lot of harm in waiting to fail until the payment gets to the send_htlc call (instead of doing it immediately upon receipt), so I think I agree we should just drop it (but keep this return).

All that to say, I don't think anything needs to change in this PR, correct? The comment is still true today, though I agree we could change it in a followup and should change it after #680.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah I just believe this forwarding policy makes really sense if the next hop is owned by a mobile client, with a clear agreement "I'm going to be offline 99% of my time, please cache my HTLC" ? Or at least it would be worthy as an experimentation.

Right, it's more a good-place-to-clean-once-we-have-680.

@ariard

Copy link
Copy Markdown

Code Review ACK 8671aca

@TheBlueMattTheBlueMatt added this to the 0.0.99 milestone Jun 26, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Tagging as 0.0.99 as #949 depends on this and because this would be critical for any async-monitor-update routing node.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 8671aca to eab577cCompareJune 29, 2021 23:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree 8671aca5 eab577c0
$

@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.

Somethings aren't clear to me when reading the commit message / PR description:

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,

Either drop "almost certainly" or explain when this is not the case.

thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.

Reads strange. Start new sentence here and replace "causing" with "causes"?

After #851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -

What is "this" referring to? "this change"?

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.

Unclear to me what is meant by a "matching holding cell" (matching what?) and what "it always happens" is referring to.

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/chanmon_update_fail_tests.rs Outdated
commitment_signed_dance!(nodes[1], nodes[0], send_event.commitment_msg, false, true);
check_added_monitors!(nodes[1], 0);

let mut events_2 = nodes[1].node.get_and_clear_pending_msg_events();

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.

Is there a TL;DR to summarize the changes to this test to help in review?

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.

Because it is no longer the case that receiving an HTLC while we're pending a monitor update restoration causes the HTLC to fail, code which relied on that behavior broke. eg the first code hunk which was removed explicitly delivered an HTLC and expected it to fail, which no longer happens.

The big if-block diff hunk is because the two cases have converged somewhat - we now always have a payment in node[1]'s outbound holding cell.


*nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
assert!(nodes[1].node.claim_funds(payment_preimage_1));
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Temporary failure claiming HTLC, treating as success: Failed to update ChannelMonitor".to_string(), 1);

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.

Is there any way to assert the state of the channel rather than examining the log?

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.

This line was only moved, not created. the log assert is nice IMO as it checks the specific error, not just that one happened, though the observable state is still checked in that no new message was generated.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from eab577c to 0534e8bCompareJune 30, 2021 19:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

@jkczyz

Copy link
Copy Markdown
Contributor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

Looks good. Thanks for doing it!

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
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 0534e8b to b58c884CompareJune 30, 2021 23:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree -U1 0534e8b9 b58c8843
$

The full diff from the previous ack is still trivial, so will merge after CI:

$ git diff-tree -U1 -b eab577c0 b58c8843
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index ac293cc1..73fc5315 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -908,4 +908,4 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Attempt to forward a third payment but fail due to the second channel being unavailable
-	// for forwarding.
+	// Forward a third payment which will also be added to the holding cell, despite the channel
+	// being paused waiting a monitor update.
let (_, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[2]);
@@ -924,2 +924,8 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
+	// Call forward_pending_htlcs and check that the new HTLC was simply added to the holding cell
+	// and not forwarded.
+	expect_pending_htlcs_forwardable!(nodes[1]);
+	check_added_monitors!(nodes[1], 0);
+	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+
let (payment_preimage_4, payment_hash_4) = if test_ignore_second_cs {
@@ -942,8 +948,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Call forward_pending_htlcs first to make sure we don't have any issues attempting (and
-	// failing) to forward an HTLC while a channel is still awaiting monitor update restoration.
-	expect_pending_htlcs_forwardable!(nodes[1]);
-	check_added_monitors!(nodes[1], 0);
-	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
-
// Restore monitor updating, ensuring we immediately get a fail-back update and a
@@ -1048,2 +1048,3 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
}
+
assert_eq!(as_cs.update_add_htlcs.len(), 1);
@@ -1077,3 +1078,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-
expect_pending_htlcs_forwardable!(nodes[2]);
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 19e28d4a..26725111 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -3976,10 +3976,14 @@ impl<Signer: Sign> Channel<Signer> {
/// send_htlc_and_commit instead cause you'll want both messages at once.
-	/// This returns an option instead of a pure UpdateAddHTLC as we may be in a state where we
-	/// cannot add HTLCs on the wire.
-	/// In cases where we're waiting on the remote peer to send us a revoke_and_ack, if we did, we
-	/// wouldn't be able to determine what they actually ACK'ed when we do receive the
-	/// revoke_and_ack.
-	/// In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we may
-	/// not yet have sent the previous commitment update messages and will need to regenerate them.
-	/// You MUST call send_commitment prior to any other calls on this Channel
+	///
+	/// This returns an optional UpdateAddHTLC as we may be in a state where we cannot add HTLCs on
+	/// the wire:
+	/// * In cases where we're waiting on the remote peer to send us a revoke_and_ack, we
+	/// wouldn't be able to determine what they actually ACK'ed if we have two sets of updates
+	/// awaiting ACK.
+	/// * In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we
+	/// may not yet have sent the previous commitment update messages and will need to regenerate
+	/// them.
+	///
+	/// You MUST call send_commitment prior to calling any other methods on this Channel!
+	///
/// If an Err is returned, it's a ChannelError::Ignore!
$

@TheBlueMatt
TheBlueMatt merged commit 4353d4a into lightningdevkit:mainJul 1, 2021
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.

Spurious Forwarding Failures in Async Monitor Update Clients

3 participants

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

Consider channels "live" even if they are awaiting a monitor update - #954

Merged
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails
Jul 1, 2021
Merged

Consider channels "live" even if they are awaiting a monitor update#954
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

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 #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.

Fixes#661.

This should make #949's tests a bit easier to implement.

@codecov

codecovBot commented Jun 17, 2021

Copy link
Copy Markdown

Codecov Report

Merging #954 (0534e8b) into main (2940099) will increase coverage by 1.47%.
The diff coverage is 91.30%.

❗ Current head 0534e8b differs from pull request most recent head b58c884. Consider uploading reports for the commit b58c884 to get more accurate results
Impacted file tree graph

@@ Coverage Diff @@## main #954 +/- ##
==========================================
+ Coverage 90.59% 92.06% +1.47% 
==========================================
Files 60 60 Lines 30423 39444 +9021 ==========================================
+ Hits 27561 36314 +8753 - Misses 2862 3130 +268 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs87.18% <ø> (+3.27%)⬆️
lightning/src/ln/channel.rs92.86% <87.50%> (+4.57%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.60% <91.58%> (-0.19%)⬇️
lightning/src/ln/wire.rs60.52% <0.00%> (-3.58%)⬇️
lightning/src/util/macro_logger.rs87.87% <0.00%> (-1.19%)⬇️
lightning/src/ln/functional_test_utils.rs94.79% <0.00%> (-0.03%)⬇️
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (+0.37%)⬆️
lightning/src/chain/chainmonitor.rs96.21% <0.00%> (+0.41%)⬆️
lightning/src/util/ser.rs91.70% <0.00%> (+0.43%)⬆️
... and 20 more

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 2940099...b58c884. Read the comment docs.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed a few comments left on other PRs, including #949 (comment)

@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.

Just need to fix send_htlc doc I believe, otherwise good to me

Comment threadlightning/src/ln/channel.rs
if (self.channel_state & (ChannelState::PeerDisconnected as u32)) != 0 {
// Note that this should never really happen, if we're !is_live() on receipt of an
// incoming HTLC for relay will result in us rejecting the HTLC and we won't allow
// the user to send directly into a !is_live() channel. However, if we

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This comment sounds weird, I still think we should be able to receive a HTLC even if the forwarding channel is knock-out, though we don't currently in decode_update_add_htlc_onion. Maybe possible after #680.

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.

Right, I suppose its reasonable to accept an HTLC for forwarding to a channel which is disconnected currently if you're a special type of node that can make the channel become connected soon (of course for a general purpose node we should definitely fail). There isn't a lot of harm in waiting to fail until the payment gets to the send_htlc call (instead of doing it immediately upon receipt), so I think I agree we should just drop it (but keep this return).

All that to say, I don't think anything needs to change in this PR, correct? The comment is still true today, though I agree we could change it in a followup and should change it after #680.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah I just believe this forwarding policy makes really sense if the next hop is owned by a mobile client, with a clear agreement "I'm going to be offline 99% of my time, please cache my HTLC" ? Or at least it would be worthy as an experimentation.

Right, it's more a good-place-to-clean-once-we-have-680.

@ariard

Copy link
Copy Markdown

Code Review ACK 8671aca

@TheBlueMattTheBlueMatt added this to the 0.0.99 milestone Jun 26, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Tagging as 0.0.99 as #949 depends on this and because this would be critical for any async-monitor-update routing node.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 8671aca to eab577cCompareJune 29, 2021 23:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree 8671aca5 eab577c0
$

@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.

Somethings aren't clear to me when reading the commit message / PR description:

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,

Either drop "almost certainly" or explain when this is not the case.

thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.

Reads strange. Start new sentence here and replace "causing" with "causes"?

After #851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -

What is "this" referring to? "this change"?

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.

Unclear to me what is meant by a "matching holding cell" (matching what?) and what "it always happens" is referring to.

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/chanmon_update_fail_tests.rs Outdated
commitment_signed_dance!(nodes[1], nodes[0], send_event.commitment_msg, false, true);
check_added_monitors!(nodes[1], 0);

let mut events_2 = nodes[1].node.get_and_clear_pending_msg_events();

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.

Is there a TL;DR to summarize the changes to this test to help in review?

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.

Because it is no longer the case that receiving an HTLC while we're pending a monitor update restoration causes the HTLC to fail, code which relied on that behavior broke. eg the first code hunk which was removed explicitly delivered an HTLC and expected it to fail, which no longer happens.

The big if-block diff hunk is because the two cases have converged somewhat - we now always have a payment in node[1]'s outbound holding cell.


*nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
assert!(nodes[1].node.claim_funds(payment_preimage_1));
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Temporary failure claiming HTLC, treating as success: Failed to update ChannelMonitor".to_string(), 1);

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.

Is there any way to assert the state of the channel rather than examining the log?

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.

This line was only moved, not created. the log assert is nice IMO as it checks the specific error, not just that one happened, though the observable state is still checked in that no new message was generated.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from eab577c to 0534e8bCompareJune 30, 2021 19:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

@jkczyz

Copy link
Copy Markdown
Contributor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

Looks good. Thanks for doing it!

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
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 0534e8b to b58c884CompareJune 30, 2021 23:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree -U1 0534e8b9 b58c8843
$

The full diff from the previous ack is still trivial, so will merge after CI:

$ git diff-tree -U1 -b eab577c0 b58c8843
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index ac293cc1..73fc5315 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -908,4 +908,4 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Attempt to forward a third payment but fail due to the second channel being unavailable
-	// for forwarding.
+	// Forward a third payment which will also be added to the holding cell, despite the channel
+	// being paused waiting a monitor update.
let (_, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[2]);
@@ -924,2 +924,8 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
+	// Call forward_pending_htlcs and check that the new HTLC was simply added to the holding cell
+	// and not forwarded.
+	expect_pending_htlcs_forwardable!(nodes[1]);
+	check_added_monitors!(nodes[1], 0);
+	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+
let (payment_preimage_4, payment_hash_4) = if test_ignore_second_cs {
@@ -942,8 +948,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Call forward_pending_htlcs first to make sure we don't have any issues attempting (and
-	// failing) to forward an HTLC while a channel is still awaiting monitor update restoration.
-	expect_pending_htlcs_forwardable!(nodes[1]);
-	check_added_monitors!(nodes[1], 0);
-	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
-
// Restore monitor updating, ensuring we immediately get a fail-back update and a
@@ -1048,2 +1048,3 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
}
+
assert_eq!(as_cs.update_add_htlcs.len(), 1);
@@ -1077,3 +1078,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-
expect_pending_htlcs_forwardable!(nodes[2]);
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 19e28d4a..26725111 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -3976,10 +3976,14 @@ impl<Signer: Sign> Channel<Signer> {
/// send_htlc_and_commit instead cause you'll want both messages at once.
-	/// This returns an option instead of a pure UpdateAddHTLC as we may be in a state where we
-	/// cannot add HTLCs on the wire.
-	/// In cases where we're waiting on the remote peer to send us a revoke_and_ack, if we did, we
-	/// wouldn't be able to determine what they actually ACK'ed when we do receive the
-	/// revoke_and_ack.
-	/// In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we may
-	/// not yet have sent the previous commitment update messages and will need to regenerate them.
-	/// You MUST call send_commitment prior to any other calls on this Channel
+	///
+	/// This returns an optional UpdateAddHTLC as we may be in a state where we cannot add HTLCs on
+	/// the wire:
+	/// * In cases where we're waiting on the remote peer to send us a revoke_and_ack, we
+	/// wouldn't be able to determine what they actually ACK'ed if we have two sets of updates
+	/// awaiting ACK.
+	/// * In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we
+	/// may not yet have sent the previous commitment update messages and will need to regenerate
+	/// them.
+	///
+	/// You MUST call send_commitment prior to calling any other methods on this Channel!
+	///
/// If an Err is returned, it's a ChannelError::Ignore!
$

@TheBlueMatt
TheBlueMatt merged commit 4353d4a into lightningdevkit:mainJul 1, 2021
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.

Spurious Forwarding Failures in Async Monitor Update Clients

3 participants

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

Consider channels "live" even if they are awaiting a monitor update - #954

Merged
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails
Jul 1, 2021
Merged

Consider channels "live" even if they are awaiting a monitor update#954
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

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 #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.

Fixes#661.

This should make #949's tests a bit easier to implement.

@codecov

codecovBot commented Jun 17, 2021

Copy link
Copy Markdown

Codecov Report

Merging #954 (0534e8b) into main (2940099) will increase coverage by 1.47%.
The diff coverage is 91.30%.

❗ Current head 0534e8b differs from pull request most recent head b58c884. Consider uploading reports for the commit b58c884 to get more accurate results
Impacted file tree graph

@@ Coverage Diff @@## main #954 +/- ##
==========================================
+ Coverage 90.59% 92.06% +1.47% 
==========================================
Files 60 60 Lines 30423 39444 +9021 ==========================================
+ Hits 27561 36314 +8753 - Misses 2862 3130 +268 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs87.18% <ø> (+3.27%)⬆️
lightning/src/ln/channel.rs92.86% <87.50%> (+4.57%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.60% <91.58%> (-0.19%)⬇️
lightning/src/ln/wire.rs60.52% <0.00%> (-3.58%)⬇️
lightning/src/util/macro_logger.rs87.87% <0.00%> (-1.19%)⬇️
lightning/src/ln/functional_test_utils.rs94.79% <0.00%> (-0.03%)⬇️
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (+0.37%)⬆️
lightning/src/chain/chainmonitor.rs96.21% <0.00%> (+0.41%)⬆️
lightning/src/util/ser.rs91.70% <0.00%> (+0.43%)⬆️
... and 20 more

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 2940099...b58c884. Read the comment docs.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed a few comments left on other PRs, including #949 (comment)

@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.

Just need to fix send_htlc doc I believe, otherwise good to me

Comment threadlightning/src/ln/channel.rs
if (self.channel_state & (ChannelState::PeerDisconnected as u32)) != 0 {
// Note that this should never really happen, if we're !is_live() on receipt of an
// incoming HTLC for relay will result in us rejecting the HTLC and we won't allow
// the user to send directly into a !is_live() channel. However, if we

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This comment sounds weird, I still think we should be able to receive a HTLC even if the forwarding channel is knock-out, though we don't currently in decode_update_add_htlc_onion. Maybe possible after #680.

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.

Right, I suppose its reasonable to accept an HTLC for forwarding to a channel which is disconnected currently if you're a special type of node that can make the channel become connected soon (of course for a general purpose node we should definitely fail). There isn't a lot of harm in waiting to fail until the payment gets to the send_htlc call (instead of doing it immediately upon receipt), so I think I agree we should just drop it (but keep this return).

All that to say, I don't think anything needs to change in this PR, correct? The comment is still true today, though I agree we could change it in a followup and should change it after #680.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah I just believe this forwarding policy makes really sense if the next hop is owned by a mobile client, with a clear agreement "I'm going to be offline 99% of my time, please cache my HTLC" ? Or at least it would be worthy as an experimentation.

Right, it's more a good-place-to-clean-once-we-have-680.

@ariard

Copy link
Copy Markdown

Code Review ACK 8671aca

@TheBlueMattTheBlueMatt added this to the 0.0.99 milestone Jun 26, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Tagging as 0.0.99 as #949 depends on this and because this would be critical for any async-monitor-update routing node.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 8671aca to eab577cCompareJune 29, 2021 23:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree 8671aca5 eab577c0
$

@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.

Somethings aren't clear to me when reading the commit message / PR description:

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,

Either drop "almost certainly" or explain when this is not the case.

thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.

Reads strange. Start new sentence here and replace "causing" with "causes"?

After #851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -

What is "this" referring to? "this change"?

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.

Unclear to me what is meant by a "matching holding cell" (matching what?) and what "it always happens" is referring to.

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/chanmon_update_fail_tests.rs Outdated
commitment_signed_dance!(nodes[1], nodes[0], send_event.commitment_msg, false, true);
check_added_monitors!(nodes[1], 0);

let mut events_2 = nodes[1].node.get_and_clear_pending_msg_events();

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.

Is there a TL;DR to summarize the changes to this test to help in review?

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.

Because it is no longer the case that receiving an HTLC while we're pending a monitor update restoration causes the HTLC to fail, code which relied on that behavior broke. eg the first code hunk which was removed explicitly delivered an HTLC and expected it to fail, which no longer happens.

The big if-block diff hunk is because the two cases have converged somewhat - we now always have a payment in node[1]'s outbound holding cell.


*nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
assert!(nodes[1].node.claim_funds(payment_preimage_1));
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Temporary failure claiming HTLC, treating as success: Failed to update ChannelMonitor".to_string(), 1);

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.

Is there any way to assert the state of the channel rather than examining the log?

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.

This line was only moved, not created. the log assert is nice IMO as it checks the specific error, not just that one happened, though the observable state is still checked in that no new message was generated.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from eab577c to 0534e8bCompareJune 30, 2021 19:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

@jkczyz

Copy link
Copy Markdown
Contributor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

Looks good. Thanks for doing it!

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
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 0534e8b to b58c884CompareJune 30, 2021 23:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree -U1 0534e8b9 b58c8843
$

The full diff from the previous ack is still trivial, so will merge after CI:

$ git diff-tree -U1 -b eab577c0 b58c8843
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index ac293cc1..73fc5315 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -908,4 +908,4 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Attempt to forward a third payment but fail due to the second channel being unavailable
-	// for forwarding.
+	// Forward a third payment which will also be added to the holding cell, despite the channel
+	// being paused waiting a monitor update.
let (_, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[2]);
@@ -924,2 +924,8 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
+	// Call forward_pending_htlcs and check that the new HTLC was simply added to the holding cell
+	// and not forwarded.
+	expect_pending_htlcs_forwardable!(nodes[1]);
+	check_added_monitors!(nodes[1], 0);
+	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+
let (payment_preimage_4, payment_hash_4) = if test_ignore_second_cs {
@@ -942,8 +948,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Call forward_pending_htlcs first to make sure we don't have any issues attempting (and
-	// failing) to forward an HTLC while a channel is still awaiting monitor update restoration.
-	expect_pending_htlcs_forwardable!(nodes[1]);
-	check_added_monitors!(nodes[1], 0);
-	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
-
// Restore monitor updating, ensuring we immediately get a fail-back update and a
@@ -1048,2 +1048,3 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
}
+
assert_eq!(as_cs.update_add_htlcs.len(), 1);
@@ -1077,3 +1078,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-
expect_pending_htlcs_forwardable!(nodes[2]);
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 19e28d4a..26725111 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -3976,10 +3976,14 @@ impl<Signer: Sign> Channel<Signer> {
/// send_htlc_and_commit instead cause you'll want both messages at once.
-	/// This returns an option instead of a pure UpdateAddHTLC as we may be in a state where we
-	/// cannot add HTLCs on the wire.
-	/// In cases where we're waiting on the remote peer to send us a revoke_and_ack, if we did, we
-	/// wouldn't be able to determine what they actually ACK'ed when we do receive the
-	/// revoke_and_ack.
-	/// In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we may
-	/// not yet have sent the previous commitment update messages and will need to regenerate them.
-	/// You MUST call send_commitment prior to any other calls on this Channel
+	///
+	/// This returns an optional UpdateAddHTLC as we may be in a state where we cannot add HTLCs on
+	/// the wire:
+	/// * In cases where we're waiting on the remote peer to send us a revoke_and_ack, we
+	/// wouldn't be able to determine what they actually ACK'ed if we have two sets of updates
+	/// awaiting ACK.
+	/// * In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we
+	/// may not yet have sent the previous commitment update messages and will need to regenerate
+	/// them.
+	///
+	/// You MUST call send_commitment prior to calling any other methods on this Channel!
+	///
/// If an Err is returned, it's a ChannelError::Ignore!
$

@TheBlueMatt
TheBlueMatt merged commit 4353d4a into lightningdevkit:mainJul 1, 2021
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.

Spurious Forwarding Failures in Async Monitor Update Clients

3 participants

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

Consider channels "live" even if they are awaiting a monitor update - #954

Merged
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails
Jul 1, 2021
Merged

Consider channels "live" even if they are awaiting a monitor update#954
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

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 #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.

Fixes#661.

This should make #949's tests a bit easier to implement.

@codecov

codecovBot commented Jun 17, 2021

Copy link
Copy Markdown

Codecov Report

Merging #954 (0534e8b) into main (2940099) will increase coverage by 1.47%.
The diff coverage is 91.30%.

❗ Current head 0534e8b differs from pull request most recent head b58c884. Consider uploading reports for the commit b58c884 to get more accurate results
Impacted file tree graph

@@ Coverage Diff @@## main #954 +/- ##
==========================================
+ Coverage 90.59% 92.06% +1.47% 
==========================================
Files 60 60 Lines 30423 39444 +9021 ==========================================
+ Hits 27561 36314 +8753 - Misses 2862 3130 +268 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs87.18% <ø> (+3.27%)⬆️
lightning/src/ln/channel.rs92.86% <87.50%> (+4.57%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.60% <91.58%> (-0.19%)⬇️
lightning/src/ln/wire.rs60.52% <0.00%> (-3.58%)⬇️
lightning/src/util/macro_logger.rs87.87% <0.00%> (-1.19%)⬇️
lightning/src/ln/functional_test_utils.rs94.79% <0.00%> (-0.03%)⬇️
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (+0.37%)⬆️
lightning/src/chain/chainmonitor.rs96.21% <0.00%> (+0.41%)⬆️
lightning/src/util/ser.rs91.70% <0.00%> (+0.43%)⬆️
... and 20 more

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 2940099...b58c884. Read the comment docs.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed a few comments left on other PRs, including #949 (comment)

@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.

Just need to fix send_htlc doc I believe, otherwise good to me

Comment threadlightning/src/ln/channel.rs
if (self.channel_state & (ChannelState::PeerDisconnected as u32)) != 0 {
// Note that this should never really happen, if we're !is_live() on receipt of an
// incoming HTLC for relay will result in us rejecting the HTLC and we won't allow
// the user to send directly into a !is_live() channel. However, if we

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This comment sounds weird, I still think we should be able to receive a HTLC even if the forwarding channel is knock-out, though we don't currently in decode_update_add_htlc_onion. Maybe possible after #680.

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.

Right, I suppose its reasonable to accept an HTLC for forwarding to a channel which is disconnected currently if you're a special type of node that can make the channel become connected soon (of course for a general purpose node we should definitely fail). There isn't a lot of harm in waiting to fail until the payment gets to the send_htlc call (instead of doing it immediately upon receipt), so I think I agree we should just drop it (but keep this return).

All that to say, I don't think anything needs to change in this PR, correct? The comment is still true today, though I agree we could change it in a followup and should change it after #680.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah I just believe this forwarding policy makes really sense if the next hop is owned by a mobile client, with a clear agreement "I'm going to be offline 99% of my time, please cache my HTLC" ? Or at least it would be worthy as an experimentation.

Right, it's more a good-place-to-clean-once-we-have-680.

@ariard

Copy link
Copy Markdown

Code Review ACK 8671aca

@TheBlueMattTheBlueMatt added this to the 0.0.99 milestone Jun 26, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Tagging as 0.0.99 as #949 depends on this and because this would be critical for any async-monitor-update routing node.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 8671aca to eab577cCompareJune 29, 2021 23:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree 8671aca5 eab577c0
$

@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.

Somethings aren't clear to me when reading the commit message / PR description:

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,

Either drop "almost certainly" or explain when this is not the case.

thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.

Reads strange. Start new sentence here and replace "causing" with "causes"?

After #851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -

What is "this" referring to? "this change"?

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.

Unclear to me what is meant by a "matching holding cell" (matching what?) and what "it always happens" is referring to.

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/chanmon_update_fail_tests.rs Outdated
commitment_signed_dance!(nodes[1], nodes[0], send_event.commitment_msg, false, true);
check_added_monitors!(nodes[1], 0);

let mut events_2 = nodes[1].node.get_and_clear_pending_msg_events();

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.

Is there a TL;DR to summarize the changes to this test to help in review?

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.

Because it is no longer the case that receiving an HTLC while we're pending a monitor update restoration causes the HTLC to fail, code which relied on that behavior broke. eg the first code hunk which was removed explicitly delivered an HTLC and expected it to fail, which no longer happens.

The big if-block diff hunk is because the two cases have converged somewhat - we now always have a payment in node[1]'s outbound holding cell.


*nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
assert!(nodes[1].node.claim_funds(payment_preimage_1));
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Temporary failure claiming HTLC, treating as success: Failed to update ChannelMonitor".to_string(), 1);

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.

Is there any way to assert the state of the channel rather than examining the log?

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.

This line was only moved, not created. the log assert is nice IMO as it checks the specific error, not just that one happened, though the observable state is still checked in that no new message was generated.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from eab577c to 0534e8bCompareJune 30, 2021 19:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

@jkczyz

Copy link
Copy Markdown
Contributor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

Looks good. Thanks for doing it!

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
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 0534e8b to b58c884CompareJune 30, 2021 23:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree -U1 0534e8b9 b58c8843
$

The full diff from the previous ack is still trivial, so will merge after CI:

$ git diff-tree -U1 -b eab577c0 b58c8843
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index ac293cc1..73fc5315 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -908,4 +908,4 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Attempt to forward a third payment but fail due to the second channel being unavailable
-	// for forwarding.
+	// Forward a third payment which will also be added to the holding cell, despite the channel
+	// being paused waiting a monitor update.
let (_, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[2]);
@@ -924,2 +924,8 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
+	// Call forward_pending_htlcs and check that the new HTLC was simply added to the holding cell
+	// and not forwarded.
+	expect_pending_htlcs_forwardable!(nodes[1]);
+	check_added_monitors!(nodes[1], 0);
+	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+
let (payment_preimage_4, payment_hash_4) = if test_ignore_second_cs {
@@ -942,8 +948,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Call forward_pending_htlcs first to make sure we don't have any issues attempting (and
-	// failing) to forward an HTLC while a channel is still awaiting monitor update restoration.
-	expect_pending_htlcs_forwardable!(nodes[1]);
-	check_added_monitors!(nodes[1], 0);
-	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
-
// Restore monitor updating, ensuring we immediately get a fail-back update and a
@@ -1048,2 +1048,3 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
}
+
assert_eq!(as_cs.update_add_htlcs.len(), 1);
@@ -1077,3 +1078,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-
expect_pending_htlcs_forwardable!(nodes[2]);
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 19e28d4a..26725111 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -3976,10 +3976,14 @@ impl<Signer: Sign> Channel<Signer> {
/// send_htlc_and_commit instead cause you'll want both messages at once.
-	/// This returns an option instead of a pure UpdateAddHTLC as we may be in a state where we
-	/// cannot add HTLCs on the wire.
-	/// In cases where we're waiting on the remote peer to send us a revoke_and_ack, if we did, we
-	/// wouldn't be able to determine what they actually ACK'ed when we do receive the
-	/// revoke_and_ack.
-	/// In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we may
-	/// not yet have sent the previous commitment update messages and will need to regenerate them.
-	/// You MUST call send_commitment prior to any other calls on this Channel
+	///
+	/// This returns an optional UpdateAddHTLC as we may be in a state where we cannot add HTLCs on
+	/// the wire:
+	/// * In cases where we're waiting on the remote peer to send us a revoke_and_ack, we
+	/// wouldn't be able to determine what they actually ACK'ed if we have two sets of updates
+	/// awaiting ACK.
+	/// * In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we
+	/// may not yet have sent the previous commitment update messages and will need to regenerate
+	/// them.
+	///
+	/// You MUST call send_commitment prior to calling any other methods on this Channel!
+	///
/// If an Err is returned, it's a ChannelError::Ignore!
$

@TheBlueMatt
TheBlueMatt merged commit 4353d4a into lightningdevkit:mainJul 1, 2021
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.

Spurious Forwarding Failures in Async Monitor Update Clients

3 participants

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

Consider channels "live" even if they are awaiting a monitor update - #954

Merged
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails
Jul 1, 2021
Merged

Consider channels "live" even if they are awaiting a monitor update#954
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

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 #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.

Fixes#661.

This should make #949's tests a bit easier to implement.

@codecov

codecovBot commented Jun 17, 2021

Copy link
Copy Markdown

Codecov Report

Merging #954 (0534e8b) into main (2940099) will increase coverage by 1.47%.
The diff coverage is 91.30%.

❗ Current head 0534e8b differs from pull request most recent head b58c884. Consider uploading reports for the commit b58c884 to get more accurate results
Impacted file tree graph

@@ Coverage Diff @@## main #954 +/- ##
==========================================
+ Coverage 90.59% 92.06% +1.47% 
==========================================
Files 60 60 Lines 30423 39444 +9021 ==========================================
+ Hits 27561 36314 +8753 - Misses 2862 3130 +268 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs87.18% <ø> (+3.27%)⬆️
lightning/src/ln/channel.rs92.86% <87.50%> (+4.57%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.60% <91.58%> (-0.19%)⬇️
lightning/src/ln/wire.rs60.52% <0.00%> (-3.58%)⬇️
lightning/src/util/macro_logger.rs87.87% <0.00%> (-1.19%)⬇️
lightning/src/ln/functional_test_utils.rs94.79% <0.00%> (-0.03%)⬇️
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (+0.37%)⬆️
lightning/src/chain/chainmonitor.rs96.21% <0.00%> (+0.41%)⬆️
lightning/src/util/ser.rs91.70% <0.00%> (+0.43%)⬆️
... and 20 more

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 2940099...b58c884. Read the comment docs.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed a few comments left on other PRs, including #949 (comment)

@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.

Just need to fix send_htlc doc I believe, otherwise good to me

Comment threadlightning/src/ln/channel.rs
if (self.channel_state & (ChannelState::PeerDisconnected as u32)) != 0 {
// Note that this should never really happen, if we're !is_live() on receipt of an
// incoming HTLC for relay will result in us rejecting the HTLC and we won't allow
// the user to send directly into a !is_live() channel. However, if we

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This comment sounds weird, I still think we should be able to receive a HTLC even if the forwarding channel is knock-out, though we don't currently in decode_update_add_htlc_onion. Maybe possible after #680.

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.

Right, I suppose its reasonable to accept an HTLC for forwarding to a channel which is disconnected currently if you're a special type of node that can make the channel become connected soon (of course for a general purpose node we should definitely fail). There isn't a lot of harm in waiting to fail until the payment gets to the send_htlc call (instead of doing it immediately upon receipt), so I think I agree we should just drop it (but keep this return).

All that to say, I don't think anything needs to change in this PR, correct? The comment is still true today, though I agree we could change it in a followup and should change it after #680.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah I just believe this forwarding policy makes really sense if the next hop is owned by a mobile client, with a clear agreement "I'm going to be offline 99% of my time, please cache my HTLC" ? Or at least it would be worthy as an experimentation.

Right, it's more a good-place-to-clean-once-we-have-680.

@ariard

Copy link
Copy Markdown

Code Review ACK 8671aca

@TheBlueMattTheBlueMatt added this to the 0.0.99 milestone Jun 26, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Tagging as 0.0.99 as #949 depends on this and because this would be critical for any async-monitor-update routing node.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 8671aca to eab577cCompareJune 29, 2021 23:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree 8671aca5 eab577c0
$

@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.

Somethings aren't clear to me when reading the commit message / PR description:

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,

Either drop "almost certainly" or explain when this is not the case.

thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.

Reads strange. Start new sentence here and replace "causing" with "causes"?

After #851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -

What is "this" referring to? "this change"?

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.

Unclear to me what is meant by a "matching holding cell" (matching what?) and what "it always happens" is referring to.

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/chanmon_update_fail_tests.rs Outdated
commitment_signed_dance!(nodes[1], nodes[0], send_event.commitment_msg, false, true);
check_added_monitors!(nodes[1], 0);

let mut events_2 = nodes[1].node.get_and_clear_pending_msg_events();

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.

Is there a TL;DR to summarize the changes to this test to help in review?

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.

Because it is no longer the case that receiving an HTLC while we're pending a monitor update restoration causes the HTLC to fail, code which relied on that behavior broke. eg the first code hunk which was removed explicitly delivered an HTLC and expected it to fail, which no longer happens.

The big if-block diff hunk is because the two cases have converged somewhat - we now always have a payment in node[1]'s outbound holding cell.


*nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
assert!(nodes[1].node.claim_funds(payment_preimage_1));
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Temporary failure claiming HTLC, treating as success: Failed to update ChannelMonitor".to_string(), 1);

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.

Is there any way to assert the state of the channel rather than examining the log?

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.

This line was only moved, not created. the log assert is nice IMO as it checks the specific error, not just that one happened, though the observable state is still checked in that no new message was generated.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from eab577c to 0534e8bCompareJune 30, 2021 19:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

@jkczyz

Copy link
Copy Markdown
Contributor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

Looks good. Thanks for doing it!

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
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 0534e8b to b58c884CompareJune 30, 2021 23:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree -U1 0534e8b9 b58c8843
$

The full diff from the previous ack is still trivial, so will merge after CI:

$ git diff-tree -U1 -b eab577c0 b58c8843
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index ac293cc1..73fc5315 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -908,4 +908,4 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Attempt to forward a third payment but fail due to the second channel being unavailable
-	// for forwarding.
+	// Forward a third payment which will also be added to the holding cell, despite the channel
+	// being paused waiting a monitor update.
let (_, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[2]);
@@ -924,2 +924,8 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
+	// Call forward_pending_htlcs and check that the new HTLC was simply added to the holding cell
+	// and not forwarded.
+	expect_pending_htlcs_forwardable!(nodes[1]);
+	check_added_monitors!(nodes[1], 0);
+	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+
let (payment_preimage_4, payment_hash_4) = if test_ignore_second_cs {
@@ -942,8 +948,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Call forward_pending_htlcs first to make sure we don't have any issues attempting (and
-	// failing) to forward an HTLC while a channel is still awaiting monitor update restoration.
-	expect_pending_htlcs_forwardable!(nodes[1]);
-	check_added_monitors!(nodes[1], 0);
-	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
-
// Restore monitor updating, ensuring we immediately get a fail-back update and a
@@ -1048,2 +1048,3 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
}
+
assert_eq!(as_cs.update_add_htlcs.len(), 1);
@@ -1077,3 +1078,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-
expect_pending_htlcs_forwardable!(nodes[2]);
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 19e28d4a..26725111 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -3976,10 +3976,14 @@ impl<Signer: Sign> Channel<Signer> {
/// send_htlc_and_commit instead cause you'll want both messages at once.
-	/// This returns an option instead of a pure UpdateAddHTLC as we may be in a state where we
-	/// cannot add HTLCs on the wire.
-	/// In cases where we're waiting on the remote peer to send us a revoke_and_ack, if we did, we
-	/// wouldn't be able to determine what they actually ACK'ed when we do receive the
-	/// revoke_and_ack.
-	/// In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we may
-	/// not yet have sent the previous commitment update messages and will need to regenerate them.
-	/// You MUST call send_commitment prior to any other calls on this Channel
+	///
+	/// This returns an optional UpdateAddHTLC as we may be in a state where we cannot add HTLCs on
+	/// the wire:
+	/// * In cases where we're waiting on the remote peer to send us a revoke_and_ack, we
+	/// wouldn't be able to determine what they actually ACK'ed if we have two sets of updates
+	/// awaiting ACK.
+	/// * In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we
+	/// may not yet have sent the previous commitment update messages and will need to regenerate
+	/// them.
+	///
+	/// You MUST call send_commitment prior to calling any other methods on this Channel!
+	///
/// If an Err is returned, it's a ChannelError::Ignore!
$

@TheBlueMatt
TheBlueMatt merged commit 4353d4a into lightningdevkit:mainJul 1, 2021
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.

Spurious Forwarding Failures in Async Monitor Update Clients

3 participants

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

Consider channels "live" even if they are awaiting a monitor update - #954

Merged
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails
Jul 1, 2021
Merged

Consider channels "live" even if they are awaiting a monitor update#954
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

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 #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.

Fixes#661.

This should make #949's tests a bit easier to implement.

@codecov

codecovBot commented Jun 17, 2021

Copy link
Copy Markdown

Codecov Report

Merging #954 (0534e8b) into main (2940099) will increase coverage by 1.47%.
The diff coverage is 91.30%.

❗ Current head 0534e8b differs from pull request most recent head b58c884. Consider uploading reports for the commit b58c884 to get more accurate results
Impacted file tree graph

@@ Coverage Diff @@## main #954 +/- ##
==========================================
+ Coverage 90.59% 92.06% +1.47% 
==========================================
Files 60 60 Lines 30423 39444 +9021 ==========================================
+ Hits 27561 36314 +8753 - Misses 2862 3130 +268 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs87.18% <ø> (+3.27%)⬆️
lightning/src/ln/channel.rs92.86% <87.50%> (+4.57%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.60% <91.58%> (-0.19%)⬇️
lightning/src/ln/wire.rs60.52% <0.00%> (-3.58%)⬇️
lightning/src/util/macro_logger.rs87.87% <0.00%> (-1.19%)⬇️
lightning/src/ln/functional_test_utils.rs94.79% <0.00%> (-0.03%)⬇️
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (+0.37%)⬆️
lightning/src/chain/chainmonitor.rs96.21% <0.00%> (+0.41%)⬆️
lightning/src/util/ser.rs91.70% <0.00%> (+0.43%)⬆️
... and 20 more

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 2940099...b58c884. Read the comment docs.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed a few comments left on other PRs, including #949 (comment)

@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.

Just need to fix send_htlc doc I believe, otherwise good to me

Comment threadlightning/src/ln/channel.rs
if (self.channel_state & (ChannelState::PeerDisconnected as u32)) != 0 {
// Note that this should never really happen, if we're !is_live() on receipt of an
// incoming HTLC for relay will result in us rejecting the HTLC and we won't allow
// the user to send directly into a !is_live() channel. However, if we

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This comment sounds weird, I still think we should be able to receive a HTLC even if the forwarding channel is knock-out, though we don't currently in decode_update_add_htlc_onion. Maybe possible after #680.

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.

Right, I suppose its reasonable to accept an HTLC for forwarding to a channel which is disconnected currently if you're a special type of node that can make the channel become connected soon (of course for a general purpose node we should definitely fail). There isn't a lot of harm in waiting to fail until the payment gets to the send_htlc call (instead of doing it immediately upon receipt), so I think I agree we should just drop it (but keep this return).

All that to say, I don't think anything needs to change in this PR, correct? The comment is still true today, though I agree we could change it in a followup and should change it after #680.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah I just believe this forwarding policy makes really sense if the next hop is owned by a mobile client, with a clear agreement "I'm going to be offline 99% of my time, please cache my HTLC" ? Or at least it would be worthy as an experimentation.

Right, it's more a good-place-to-clean-once-we-have-680.

@ariard

Copy link
Copy Markdown

Code Review ACK 8671aca

@TheBlueMattTheBlueMatt added this to the 0.0.99 milestone Jun 26, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Tagging as 0.0.99 as #949 depends on this and because this would be critical for any async-monitor-update routing node.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 8671aca to eab577cCompareJune 29, 2021 23:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree 8671aca5 eab577c0
$

@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.

Somethings aren't clear to me when reading the commit message / PR description:

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,

Either drop "almost certainly" or explain when this is not the case.

thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.

Reads strange. Start new sentence here and replace "causing" with "causes"?

After #851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -

What is "this" referring to? "this change"?

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.

Unclear to me what is meant by a "matching holding cell" (matching what?) and what "it always happens" is referring to.

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/chanmon_update_fail_tests.rs Outdated
commitment_signed_dance!(nodes[1], nodes[0], send_event.commitment_msg, false, true);
check_added_monitors!(nodes[1], 0);

let mut events_2 = nodes[1].node.get_and_clear_pending_msg_events();

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.

Is there a TL;DR to summarize the changes to this test to help in review?

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.

Because it is no longer the case that receiving an HTLC while we're pending a monitor update restoration causes the HTLC to fail, code which relied on that behavior broke. eg the first code hunk which was removed explicitly delivered an HTLC and expected it to fail, which no longer happens.

The big if-block diff hunk is because the two cases have converged somewhat - we now always have a payment in node[1]'s outbound holding cell.


*nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
assert!(nodes[1].node.claim_funds(payment_preimage_1));
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Temporary failure claiming HTLC, treating as success: Failed to update ChannelMonitor".to_string(), 1);

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.

Is there any way to assert the state of the channel rather than examining the log?

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.

This line was only moved, not created. the log assert is nice IMO as it checks the specific error, not just that one happened, though the observable state is still checked in that no new message was generated.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from eab577c to 0534e8bCompareJune 30, 2021 19:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

@jkczyz

Copy link
Copy Markdown
Contributor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

Looks good. Thanks for doing it!

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
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 0534e8b to b58c884CompareJune 30, 2021 23:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree -U1 0534e8b9 b58c8843
$

The full diff from the previous ack is still trivial, so will merge after CI:

$ git diff-tree -U1 -b eab577c0 b58c8843
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index ac293cc1..73fc5315 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -908,4 +908,4 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Attempt to forward a third payment but fail due to the second channel being unavailable
-	// for forwarding.
+	// Forward a third payment which will also be added to the holding cell, despite the channel
+	// being paused waiting a monitor update.
let (_, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[2]);
@@ -924,2 +924,8 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
+	// Call forward_pending_htlcs and check that the new HTLC was simply added to the holding cell
+	// and not forwarded.
+	expect_pending_htlcs_forwardable!(nodes[1]);
+	check_added_monitors!(nodes[1], 0);
+	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+
let (payment_preimage_4, payment_hash_4) = if test_ignore_second_cs {
@@ -942,8 +948,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Call forward_pending_htlcs first to make sure we don't have any issues attempting (and
-	// failing) to forward an HTLC while a channel is still awaiting monitor update restoration.
-	expect_pending_htlcs_forwardable!(nodes[1]);
-	check_added_monitors!(nodes[1], 0);
-	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
-
// Restore monitor updating, ensuring we immediately get a fail-back update and a
@@ -1048,2 +1048,3 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
}
+
assert_eq!(as_cs.update_add_htlcs.len(), 1);
@@ -1077,3 +1078,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-
expect_pending_htlcs_forwardable!(nodes[2]);
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 19e28d4a..26725111 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -3976,10 +3976,14 @@ impl<Signer: Sign> Channel<Signer> {
/// send_htlc_and_commit instead cause you'll want both messages at once.
-	/// This returns an option instead of a pure UpdateAddHTLC as we may be in a state where we
-	/// cannot add HTLCs on the wire.
-	/// In cases where we're waiting on the remote peer to send us a revoke_and_ack, if we did, we
-	/// wouldn't be able to determine what they actually ACK'ed when we do receive the
-	/// revoke_and_ack.
-	/// In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we may
-	/// not yet have sent the previous commitment update messages and will need to regenerate them.
-	/// You MUST call send_commitment prior to any other calls on this Channel
+	///
+	/// This returns an optional UpdateAddHTLC as we may be in a state where we cannot add HTLCs on
+	/// the wire:
+	/// * In cases where we're waiting on the remote peer to send us a revoke_and_ack, we
+	/// wouldn't be able to determine what they actually ACK'ed if we have two sets of updates
+	/// awaiting ACK.
+	/// * In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we
+	/// may not yet have sent the previous commitment update messages and will need to regenerate
+	/// them.
+	///
+	/// You MUST call send_commitment prior to calling any other methods on this Channel!
+	///
/// If an Err is returned, it's a ChannelError::Ignore!
$

@TheBlueMatt
TheBlueMatt merged commit 4353d4a into lightningdevkit:mainJul 1, 2021
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.

Spurious Forwarding Failures in Async Monitor Update Clients

3 participants

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

Consider channels "live" even if they are awaiting a monitor update - #954

Merged
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails
Jul 1, 2021
Merged

Consider channels "live" even if they are awaiting a monitor update#954
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2021-06-no-spurious-forward-fails

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

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 #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.

Fixes#661.

This should make #949's tests a bit easier to implement.

@codecov

codecovBot commented Jun 17, 2021

Copy link
Copy Markdown

Codecov Report

Merging #954 (0534e8b) into main (2940099) will increase coverage by 1.47%.
The diff coverage is 91.30%.

❗ Current head 0534e8b differs from pull request most recent head b58c884. Consider uploading reports for the commit b58c884 to get more accurate results
Impacted file tree graph

@@ Coverage Diff @@## main #954 +/- ##
==========================================
+ Coverage 90.59% 92.06% +1.47% 
==========================================
Files 60 60 Lines 30423 39444 +9021 ==========================================
+ Hits 27561 36314 +8753 - Misses 2862 3130 +268 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs87.18% <ø> (+3.27%)⬆️
lightning/src/ln/channel.rs92.86% <87.50%> (+4.57%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.60% <91.58%> (-0.19%)⬇️
lightning/src/ln/wire.rs60.52% <0.00%> (-3.58%)⬇️
lightning/src/util/macro_logger.rs87.87% <0.00%> (-1.19%)⬇️
lightning/src/ln/functional_test_utils.rs94.79% <0.00%> (-0.03%)⬇️
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (+0.37%)⬆️
lightning/src/chain/chainmonitor.rs96.21% <0.00%> (+0.41%)⬆️
lightning/src/util/ser.rs91.70% <0.00%> (+0.43%)⬆️
... and 20 more

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 2940099...b58c884. Read the comment docs.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed a few comments left on other PRs, including #949 (comment)

@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.

Just need to fix send_htlc doc I believe, otherwise good to me

Comment threadlightning/src/ln/channel.rs
if (self.channel_state & (ChannelState::PeerDisconnected as u32)) != 0 {
// Note that this should never really happen, if we're !is_live() on receipt of an
// incoming HTLC for relay will result in us rejecting the HTLC and we won't allow
// the user to send directly into a !is_live() channel. However, if we

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This comment sounds weird, I still think we should be able to receive a HTLC even if the forwarding channel is knock-out, though we don't currently in decode_update_add_htlc_onion. Maybe possible after #680.

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.

Right, I suppose its reasonable to accept an HTLC for forwarding to a channel which is disconnected currently if you're a special type of node that can make the channel become connected soon (of course for a general purpose node we should definitely fail). There isn't a lot of harm in waiting to fail until the payment gets to the send_htlc call (instead of doing it immediately upon receipt), so I think I agree we should just drop it (but keep this return).

All that to say, I don't think anything needs to change in this PR, correct? The comment is still true today, though I agree we could change it in a followup and should change it after #680.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah I just believe this forwarding policy makes really sense if the next hop is owned by a mobile client, with a clear agreement "I'm going to be offline 99% of my time, please cache my HTLC" ? Or at least it would be worthy as an experimentation.

Right, it's more a good-place-to-clean-once-we-have-680.

@ariard

Copy link
Copy Markdown

Code Review ACK 8671aca

@TheBlueMattTheBlueMatt added this to the 0.0.99 milestone Jun 26, 2021
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Tagging as 0.0.99 as #949 depends on this and because this would be critical for any async-monitor-update routing node.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 8671aca to eab577cCompareJune 29, 2021 23:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree 8671aca5 eab577c0
$

@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.

Somethings aren't clear to me when reading the commit message / PR description:

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,

Either drop "almost certainly" or explain when this is not the case.

thus any rejection of HTLCs based on a monitor update still pending
causing a race condition.

Reads strange. Start new sentence here and replace "causing" with "causes"?

After #851, we always ensure any holding cells are free'd when
sending P2P messages, making this much more trivially correct -

What is "this" referring to? "this change"?

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.

Unclear to me what is meant by a "matching holding cell" (matching what?) and what "it always happens" is referring to.

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/chanmon_update_fail_tests.rs Outdated
commitment_signed_dance!(nodes[1], nodes[0], send_event.commitment_msg, false, true);
check_added_monitors!(nodes[1], 0);

let mut events_2 = nodes[1].node.get_and_clear_pending_msg_events();

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.

Is there a TL;DR to summarize the changes to this test to help in review?

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.

Because it is no longer the case that receiving an HTLC while we're pending a monitor update restoration causes the HTLC to fail, code which relied on that behavior broke. eg the first code hunk which was removed explicitly delivered an HTLC and expected it to fail, which no longer happens.

The big if-block diff hunk is because the two cases have converged somewhat - we now always have a payment in node[1]'s outbound holding cell.


*nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
assert!(nodes[1].node.claim_funds(payment_preimage_1));
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Temporary failure claiming HTLC, treating as success: Failed to update ChannelMonitor".to_string(), 1);

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.

Is there any way to assert the state of the channel rather than examining the log?

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.

This line was only moved, not created. the log assert is nice IMO as it checks the specific error, not just that one happened, though the observable state is still checked in that no new message was generated.

@TheBlueMatt
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from eab577c to 0534e8bCompareJune 30, 2021 19:35
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

@jkczyz

Copy link
Copy Markdown
Contributor

I rewrote the commit message in the main commit (without changing its contents), let me know what you think.

Looks good. Thanks for doing it!

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
TheBlueMattforce-pushed the 2021-06-no-spurious-forward-fails branch from 0534e8b to b58c884CompareJune 30, 2021 23:15
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed with no changes:

$ git diff-tree -U1 0534e8b9 b58c8843
$

The full diff from the previous ack is still trivial, so will merge after CI:

$ git diff-tree -U1 -b eab577c0 b58c8843
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index ac293cc1..73fc5315 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -908,4 +908,4 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Attempt to forward a third payment but fail due to the second channel being unavailable
-	// for forwarding.
+	// Forward a third payment which will also be added to the holding cell, despite the channel
+	// being paused waiting a monitor update.
let (_, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[2]);
@@ -924,2 +924,8 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
+	// Call forward_pending_htlcs and check that the new HTLC was simply added to the holding cell
+	// and not forwarded.
+	expect_pending_htlcs_forwardable!(nodes[1]);
+	check_added_monitors!(nodes[1], 0);
+	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+
let (payment_preimage_4, payment_hash_4) = if test_ignore_second_cs {
@@ -942,8 +948,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-	// Call forward_pending_htlcs first to make sure we don't have any issues attempting (and
-	// failing) to forward an HTLC while a channel is still awaiting monitor update restoration.
-	expect_pending_htlcs_forwardable!(nodes[1]);
-	check_added_monitors!(nodes[1], 0);
-	assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
-
// Restore monitor updating, ensuring we immediately get a fail-back update and a
@@ -1048,2 +1048,3 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
}
+
assert_eq!(as_cs.update_add_htlcs.len(), 1);
@@ -1077,3 +1078,2 @@ fn do_test_monitor_update_fail_raa(test_ignore_second_cs: bool) {
-
expect_pending_htlcs_forwardable!(nodes[2]);
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 19e28d4a..26725111 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -3976,10 +3976,14 @@ impl<Signer: Sign> Channel<Signer> {
/// send_htlc_and_commit instead cause you'll want both messages at once.
-	/// This returns an option instead of a pure UpdateAddHTLC as we may be in a state where we
-	/// cannot add HTLCs on the wire.
-	/// In cases where we're waiting on the remote peer to send us a revoke_and_ack, if we did, we
-	/// wouldn't be able to determine what they actually ACK'ed when we do receive the
-	/// revoke_and_ack.
-	/// In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we may
-	/// not yet have sent the previous commitment update messages and will need to regenerate them.
-	/// You MUST call send_commitment prior to any other calls on this Channel
+	///
+	/// This returns an optional UpdateAddHTLC as we may be in a state where we cannot add HTLCs on
+	/// the wire:
+	/// * In cases where we're waiting on the remote peer to send us a revoke_and_ack, we
+	/// wouldn't be able to determine what they actually ACK'ed if we have two sets of updates
+	/// awaiting ACK.
+	/// * In cases where we're marked MonitorUpdateFailed, we cannot commit to a new state as we
+	/// may not yet have sent the previous commitment update messages and will need to regenerate
+	/// them.
+	///
+	/// You MUST call send_commitment prior to calling any other methods on this Channel!
+	///
/// If an Err is returned, it's a ChannelError::Ignore!
$

@TheBlueMatt
TheBlueMatt merged commit 4353d4a into lightningdevkit:mainJul 1, 2021
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.

Spurious Forwarding Failures in Async Monitor Update Clients

3 participants

@TheBlueMatt@ariard@jkczyz