Fix bug failing CS-RAA resend order on pending commitment signatures - #3149

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order
Jul 9, 2024
Merged

Fix bug failing CS-RAA resend order on pending commitment signatures#3149
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order

Conversation

@alecchendev

Copy link
Copy Markdown
Contributor

Across disconnects we may end up in a situation where we need to send a commitment_signed and then revoke_and_ack. We need to make sure that if the signer is pending for CS but not RAA, we don't screw up the order by sending the RAA first. We defer sending the RAA by setting the flag signer_pending_revoke_and_ack, which will lead to us generating an RAA upon signer_unblocked.

We test this for both the case where we send messages after a channel reestablish, as well as restoring a channel after persisting a monitor update asynchronously.

@codecov-commenter

codecov-commenter commented Jun 30, 2024

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 75.00000% with 9 lines in your changes missing coverage. Please review.

Project coverage is 90.60%. Comparing base (3ccf064) to head (315193d).
Report is 22 commits behind head on main.

FilesPatch %Lines
lightning/src/ln/channel.rs57.89%5 Missing and 3 partials ⚠️
lightning/src/ln/channelmanager.rs94.11%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #3149 +/- ##
==========================================
+ Coverage 89.81% 90.60% +0.79% 
==========================================
Files 121 121 Lines 99314 105075 +5761 Branches 99314 105075 +5761 ==========================================
+ Hits 89195 95205 +6010 + Misses 7514 7313 -201 + Partials 2605 2557 -48 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Good test, too, though I need to go look at the test coverage changes. I assume this was hit in prod? We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmmmm, I'm a bit skeptical of this. get_last_commitment_update_for_send is called in:

  • monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.
  • channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

In any case, we should almost certainly add a test for the two above cases.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

Comment threadlightning/src/ln/async_signer_tests.rs Outdated
Comment threadlightning/src/ln/async_signer_tests.rs Outdated

@alecchendevalecchendev left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I assume this was hit in prod?

Yep, have had a couple related issues here in the past

We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

SGTM!

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch from 536b6b4 to 119b703CompareJuly 3, 2024 02:16
Comment on lines +360 to +361
fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool,
enable_signer_before_monitor_completion: bool, enable_signer_before_reestablish: bool) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

after the latest additional tests this helper is a bit unwieldy, not sure the best way to address it. I could copy this test 4 times and get rid of the conditionals, but it adds a lot of filler...

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yaknow actually i realized for what these are testing it isn't necessary to include them in this test (they don't care about the CS-RAA ordering, just that the CS signer pending flag is cleared at the right time), i'll just move them to their own separate tests tomorrow

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

okay, fixed this, moved them into the peer disconnection test

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch 2 times, most recently from ca37547 to 221ef18CompareJuly 3, 2024 21:11

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

LGTM

revoke_and_ack,
funding_signed,
channel_ready,
order: self.context.resend_order.clone(),

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.

Nit: clone() is not needed on order

Suggested change
order:self.context.resend_order.clone(),
order:self.context.resend_order,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

hmm, i think it actually does require clone since RAACommitmentOrder doesn't implement Copy?

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.

OK. RAACommitmentOrder is a primitive enum, but it indeed does not have Copy trait. I tried it without and it worked, but to be safe for future changes, either the clone is needed or the enum should be marked explicitly Copy. It's fine with the clone.

Comment threadlightning/src/ln/channel.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Jul 8, 2024

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, feel free to squash the fixups.

let bs_second_commitment_signed = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);

// The rest of this is boilerplate for resolving the previous state.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

At least some of this may be replaceable with one of the inscruitable commitment_signed_dance variants.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

i feared this day would come...

I tried my best, but didn't get very far, it seems this is a little different from the normal exchange so i'm not sure how much more time i should spend trying:

nodes0 nodes1
uah ->
cs ->
raa ->
<- raa
<- cs
cs ->
raa ->
<- raa

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the first few messages (after the uah) could be, but if you fought with it a bit I'm not gonna nitpick.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Merging as the only changes since @optout21's "LGTM" are addressing his feedback.

@TheBlueMatt
TheBlueMatt merged commit f48a273 into lightningdevkit:mainJul 9, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@alecchendev@codecov-commenter@TheBlueMatt@optout21
, '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

Fix bug failing CS-RAA resend order on pending commitment signatures - #3149

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order
Jul 9, 2024
Merged

Fix bug failing CS-RAA resend order on pending commitment signatures#3149
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order

Conversation

@alecchendev

Copy link
Copy Markdown
Contributor

Across disconnects we may end up in a situation where we need to send a commitment_signed and then revoke_and_ack. We need to make sure that if the signer is pending for CS but not RAA, we don't screw up the order by sending the RAA first. We defer sending the RAA by setting the flag signer_pending_revoke_and_ack, which will lead to us generating an RAA upon signer_unblocked.

We test this for both the case where we send messages after a channel reestablish, as well as restoring a channel after persisting a monitor update asynchronously.

@codecov-commenter

codecov-commenter commented Jun 30, 2024

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 75.00000% with 9 lines in your changes missing coverage. Please review.

Project coverage is 90.60%. Comparing base (3ccf064) to head (315193d).
Report is 22 commits behind head on main.

FilesPatch %Lines
lightning/src/ln/channel.rs57.89%5 Missing and 3 partials ⚠️
lightning/src/ln/channelmanager.rs94.11%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #3149 +/- ##
==========================================
+ Coverage 89.81% 90.60% +0.79% 
==========================================
Files 121 121 Lines 99314 105075 +5761 Branches 99314 105075 +5761 ==========================================
+ Hits 89195 95205 +6010 + Misses 7514 7313 -201 + Partials 2605 2557 -48 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Good test, too, though I need to go look at the test coverage changes. I assume this was hit in prod? We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmmmm, I'm a bit skeptical of this. get_last_commitment_update_for_send is called in:

  • monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.
  • channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

In any case, we should almost certainly add a test for the two above cases.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

Comment threadlightning/src/ln/async_signer_tests.rs Outdated
Comment threadlightning/src/ln/async_signer_tests.rs Outdated

@alecchendevalecchendev left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I assume this was hit in prod?

Yep, have had a couple related issues here in the past

We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

SGTM!

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch from 536b6b4 to 119b703CompareJuly 3, 2024 02:16
Comment on lines +360 to +361
fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool,
enable_signer_before_monitor_completion: bool, enable_signer_before_reestablish: bool) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

after the latest additional tests this helper is a bit unwieldy, not sure the best way to address it. I could copy this test 4 times and get rid of the conditionals, but it adds a lot of filler...

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yaknow actually i realized for what these are testing it isn't necessary to include them in this test (they don't care about the CS-RAA ordering, just that the CS signer pending flag is cleared at the right time), i'll just move them to their own separate tests tomorrow

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

okay, fixed this, moved them into the peer disconnection test

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch 2 times, most recently from ca37547 to 221ef18CompareJuly 3, 2024 21:11

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

LGTM

revoke_and_ack,
funding_signed,
channel_ready,
order: self.context.resend_order.clone(),

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.

Nit: clone() is not needed on order

Suggested change
order:self.context.resend_order.clone(),
order:self.context.resend_order,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

hmm, i think it actually does require clone since RAACommitmentOrder doesn't implement Copy?

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.

OK. RAACommitmentOrder is a primitive enum, but it indeed does not have Copy trait. I tried it without and it worked, but to be safe for future changes, either the clone is needed or the enum should be marked explicitly Copy. It's fine with the clone.

Comment threadlightning/src/ln/channel.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Jul 8, 2024

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, feel free to squash the fixups.

let bs_second_commitment_signed = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);

// The rest of this is boilerplate for resolving the previous state.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

At least some of this may be replaceable with one of the inscruitable commitment_signed_dance variants.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

i feared this day would come...

I tried my best, but didn't get very far, it seems this is a little different from the normal exchange so i'm not sure how much more time i should spend trying:

nodes0 nodes1
uah ->
cs ->
raa ->
<- raa
<- cs
cs ->
raa ->
<- raa

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the first few messages (after the uah) could be, but if you fought with it a bit I'm not gonna nitpick.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Merging as the only changes since @optout21's "LGTM" are addressing his feedback.

@TheBlueMatt
TheBlueMatt merged commit f48a273 into lightningdevkit:mainJul 9, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@alecchendev@codecov-commenter@TheBlueMatt@optout21
, '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

Fix bug failing CS-RAA resend order on pending commitment signatures - #3149

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order
Jul 9, 2024
Merged

Fix bug failing CS-RAA resend order on pending commitment signatures#3149
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order

Conversation

@alecchendev

Copy link
Copy Markdown
Contributor

Across disconnects we may end up in a situation where we need to send a commitment_signed and then revoke_and_ack. We need to make sure that if the signer is pending for CS but not RAA, we don't screw up the order by sending the RAA first. We defer sending the RAA by setting the flag signer_pending_revoke_and_ack, which will lead to us generating an RAA upon signer_unblocked.

We test this for both the case where we send messages after a channel reestablish, as well as restoring a channel after persisting a monitor update asynchronously.

@codecov-commenter

codecov-commenter commented Jun 30, 2024

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 75.00000% with 9 lines in your changes missing coverage. Please review.

Project coverage is 90.60%. Comparing base (3ccf064) to head (315193d).
Report is 22 commits behind head on main.

FilesPatch %Lines
lightning/src/ln/channel.rs57.89%5 Missing and 3 partials ⚠️
lightning/src/ln/channelmanager.rs94.11%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #3149 +/- ##
==========================================
+ Coverage 89.81% 90.60% +0.79% 
==========================================
Files 121 121 Lines 99314 105075 +5761 Branches 99314 105075 +5761 ==========================================
+ Hits 89195 95205 +6010 + Misses 7514 7313 -201 + Partials 2605 2557 -48 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Good test, too, though I need to go look at the test coverage changes. I assume this was hit in prod? We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmmmm, I'm a bit skeptical of this. get_last_commitment_update_for_send is called in:

  • monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.
  • channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

In any case, we should almost certainly add a test for the two above cases.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

Comment threadlightning/src/ln/async_signer_tests.rs Outdated
Comment threadlightning/src/ln/async_signer_tests.rs Outdated

@alecchendevalecchendev left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I assume this was hit in prod?

Yep, have had a couple related issues here in the past

We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

SGTM!

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch from 536b6b4 to 119b703CompareJuly 3, 2024 02:16
Comment on lines +360 to +361
fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool,
enable_signer_before_monitor_completion: bool, enable_signer_before_reestablish: bool) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

after the latest additional tests this helper is a bit unwieldy, not sure the best way to address it. I could copy this test 4 times and get rid of the conditionals, but it adds a lot of filler...

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yaknow actually i realized for what these are testing it isn't necessary to include them in this test (they don't care about the CS-RAA ordering, just that the CS signer pending flag is cleared at the right time), i'll just move them to their own separate tests tomorrow

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

okay, fixed this, moved them into the peer disconnection test

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch 2 times, most recently from ca37547 to 221ef18CompareJuly 3, 2024 21:11

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

LGTM

revoke_and_ack,
funding_signed,
channel_ready,
order: self.context.resend_order.clone(),

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.

Nit: clone() is not needed on order

Suggested change
order:self.context.resend_order.clone(),
order:self.context.resend_order,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

hmm, i think it actually does require clone since RAACommitmentOrder doesn't implement Copy?

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.

OK. RAACommitmentOrder is a primitive enum, but it indeed does not have Copy trait. I tried it without and it worked, but to be safe for future changes, either the clone is needed or the enum should be marked explicitly Copy. It's fine with the clone.

Comment threadlightning/src/ln/channel.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Jul 8, 2024

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, feel free to squash the fixups.

let bs_second_commitment_signed = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);

// The rest of this is boilerplate for resolving the previous state.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

At least some of this may be replaceable with one of the inscruitable commitment_signed_dance variants.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

i feared this day would come...

I tried my best, but didn't get very far, it seems this is a little different from the normal exchange so i'm not sure how much more time i should spend trying:

nodes0 nodes1
uah ->
cs ->
raa ->
<- raa
<- cs
cs ->
raa ->
<- raa

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the first few messages (after the uah) could be, but if you fought with it a bit I'm not gonna nitpick.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Merging as the only changes since @optout21's "LGTM" are addressing his feedback.

@TheBlueMatt
TheBlueMatt merged commit f48a273 into lightningdevkit:mainJul 9, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@alecchendev@codecov-commenter@TheBlueMatt@optout21
, '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

Fix bug failing CS-RAA resend order on pending commitment signatures - #3149

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order
Jul 9, 2024
Merged

Fix bug failing CS-RAA resend order on pending commitment signatures#3149
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order

Conversation

@alecchendev

Copy link
Copy Markdown
Contributor

Across disconnects we may end up in a situation where we need to send a commitment_signed and then revoke_and_ack. We need to make sure that if the signer is pending for CS but not RAA, we don't screw up the order by sending the RAA first. We defer sending the RAA by setting the flag signer_pending_revoke_and_ack, which will lead to us generating an RAA upon signer_unblocked.

We test this for both the case where we send messages after a channel reestablish, as well as restoring a channel after persisting a monitor update asynchronously.

@codecov-commenter

codecov-commenter commented Jun 30, 2024

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 75.00000% with 9 lines in your changes missing coverage. Please review.

Project coverage is 90.60%. Comparing base (3ccf064) to head (315193d).
Report is 22 commits behind head on main.

FilesPatch %Lines
lightning/src/ln/channel.rs57.89%5 Missing and 3 partials ⚠️
lightning/src/ln/channelmanager.rs94.11%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #3149 +/- ##
==========================================
+ Coverage 89.81% 90.60% +0.79% 
==========================================
Files 121 121 Lines 99314 105075 +5761 Branches 99314 105075 +5761 ==========================================
+ Hits 89195 95205 +6010 + Misses 7514 7313 -201 + Partials 2605 2557 -48 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Good test, too, though I need to go look at the test coverage changes. I assume this was hit in prod? We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmmmm, I'm a bit skeptical of this. get_last_commitment_update_for_send is called in:

  • monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.
  • channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

In any case, we should almost certainly add a test for the two above cases.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

Comment threadlightning/src/ln/async_signer_tests.rs Outdated
Comment threadlightning/src/ln/async_signer_tests.rs Outdated

@alecchendevalecchendev left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I assume this was hit in prod?

Yep, have had a couple related issues here in the past

We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

SGTM!

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch from 536b6b4 to 119b703CompareJuly 3, 2024 02:16
Comment on lines +360 to +361
fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool,
enable_signer_before_monitor_completion: bool, enable_signer_before_reestablish: bool) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

after the latest additional tests this helper is a bit unwieldy, not sure the best way to address it. I could copy this test 4 times and get rid of the conditionals, but it adds a lot of filler...

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yaknow actually i realized for what these are testing it isn't necessary to include them in this test (they don't care about the CS-RAA ordering, just that the CS signer pending flag is cleared at the right time), i'll just move them to their own separate tests tomorrow

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

okay, fixed this, moved them into the peer disconnection test

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch 2 times, most recently from ca37547 to 221ef18CompareJuly 3, 2024 21:11

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

LGTM

revoke_and_ack,
funding_signed,
channel_ready,
order: self.context.resend_order.clone(),

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.

Nit: clone() is not needed on order

Suggested change
order:self.context.resend_order.clone(),
order:self.context.resend_order,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

hmm, i think it actually does require clone since RAACommitmentOrder doesn't implement Copy?

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.

OK. RAACommitmentOrder is a primitive enum, but it indeed does not have Copy trait. I tried it without and it worked, but to be safe for future changes, either the clone is needed or the enum should be marked explicitly Copy. It's fine with the clone.

Comment threadlightning/src/ln/channel.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Jul 8, 2024

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, feel free to squash the fixups.

let bs_second_commitment_signed = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);

// The rest of this is boilerplate for resolving the previous state.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

At least some of this may be replaceable with one of the inscruitable commitment_signed_dance variants.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

i feared this day would come...

I tried my best, but didn't get very far, it seems this is a little different from the normal exchange so i'm not sure how much more time i should spend trying:

nodes0 nodes1
uah ->
cs ->
raa ->
<- raa
<- cs
cs ->
raa ->
<- raa

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the first few messages (after the uah) could be, but if you fought with it a bit I'm not gonna nitpick.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Merging as the only changes since @optout21's "LGTM" are addressing his feedback.

@TheBlueMatt
TheBlueMatt merged commit f48a273 into lightningdevkit:mainJul 9, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@alecchendev@codecov-commenter@TheBlueMatt@optout21
, '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

Fix bug failing CS-RAA resend order on pending commitment signatures - #3149

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order
Jul 9, 2024
Merged

Fix bug failing CS-RAA resend order on pending commitment signatures#3149
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order

Conversation

@alecchendev

Copy link
Copy Markdown
Contributor

Across disconnects we may end up in a situation where we need to send a commitment_signed and then revoke_and_ack. We need to make sure that if the signer is pending for CS but not RAA, we don't screw up the order by sending the RAA first. We defer sending the RAA by setting the flag signer_pending_revoke_and_ack, which will lead to us generating an RAA upon signer_unblocked.

We test this for both the case where we send messages after a channel reestablish, as well as restoring a channel after persisting a monitor update asynchronously.

@codecov-commenter

codecov-commenter commented Jun 30, 2024

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 75.00000% with 9 lines in your changes missing coverage. Please review.

Project coverage is 90.60%. Comparing base (3ccf064) to head (315193d).
Report is 22 commits behind head on main.

FilesPatch %Lines
lightning/src/ln/channel.rs57.89%5 Missing and 3 partials ⚠️
lightning/src/ln/channelmanager.rs94.11%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #3149 +/- ##
==========================================
+ Coverage 89.81% 90.60% +0.79% 
==========================================
Files 121 121 Lines 99314 105075 +5761 Branches 99314 105075 +5761 ==========================================
+ Hits 89195 95205 +6010 + Misses 7514 7313 -201 + Partials 2605 2557 -48 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Good test, too, though I need to go look at the test coverage changes. I assume this was hit in prod? We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmmmm, I'm a bit skeptical of this. get_last_commitment_update_for_send is called in:

  • monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.
  • channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

In any case, we should almost certainly add a test for the two above cases.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

Comment threadlightning/src/ln/async_signer_tests.rs Outdated
Comment threadlightning/src/ln/async_signer_tests.rs Outdated

@alecchendevalecchendev left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I assume this was hit in prod?

Yep, have had a couple related issues here in the past

We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

SGTM!

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch from 536b6b4 to 119b703CompareJuly 3, 2024 02:16
Comment on lines +360 to +361
fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool,
enable_signer_before_monitor_completion: bool, enable_signer_before_reestablish: bool) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

after the latest additional tests this helper is a bit unwieldy, not sure the best way to address it. I could copy this test 4 times and get rid of the conditionals, but it adds a lot of filler...

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yaknow actually i realized for what these are testing it isn't necessary to include them in this test (they don't care about the CS-RAA ordering, just that the CS signer pending flag is cleared at the right time), i'll just move them to their own separate tests tomorrow

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

okay, fixed this, moved them into the peer disconnection test

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch 2 times, most recently from ca37547 to 221ef18CompareJuly 3, 2024 21:11

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

LGTM

revoke_and_ack,
funding_signed,
channel_ready,
order: self.context.resend_order.clone(),

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.

Nit: clone() is not needed on order

Suggested change
order:self.context.resend_order.clone(),
order:self.context.resend_order,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

hmm, i think it actually does require clone since RAACommitmentOrder doesn't implement Copy?

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.

OK. RAACommitmentOrder is a primitive enum, but it indeed does not have Copy trait. I tried it without and it worked, but to be safe for future changes, either the clone is needed or the enum should be marked explicitly Copy. It's fine with the clone.

Comment threadlightning/src/ln/channel.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Jul 8, 2024

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, feel free to squash the fixups.

let bs_second_commitment_signed = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);

// The rest of this is boilerplate for resolving the previous state.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

At least some of this may be replaceable with one of the inscruitable commitment_signed_dance variants.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

i feared this day would come...

I tried my best, but didn't get very far, it seems this is a little different from the normal exchange so i'm not sure how much more time i should spend trying:

nodes0 nodes1
uah ->
cs ->
raa ->
<- raa
<- cs
cs ->
raa ->
<- raa

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the first few messages (after the uah) could be, but if you fought with it a bit I'm not gonna nitpick.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Merging as the only changes since @optout21's "LGTM" are addressing his feedback.

@TheBlueMatt
TheBlueMatt merged commit f48a273 into lightningdevkit:mainJul 9, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@alecchendev@codecov-commenter@TheBlueMatt@optout21
, '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

Fix bug failing CS-RAA resend order on pending commitment signatures - #3149

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order
Jul 9, 2024
Merged

Fix bug failing CS-RAA resend order on pending commitment signatures#3149
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order

Conversation

@alecchendev

Copy link
Copy Markdown
Contributor

Across disconnects we may end up in a situation where we need to send a commitment_signed and then revoke_and_ack. We need to make sure that if the signer is pending for CS but not RAA, we don't screw up the order by sending the RAA first. We defer sending the RAA by setting the flag signer_pending_revoke_and_ack, which will lead to us generating an RAA upon signer_unblocked.

We test this for both the case where we send messages after a channel reestablish, as well as restoring a channel after persisting a monitor update asynchronously.

@codecov-commenter

codecov-commenter commented Jun 30, 2024

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 75.00000% with 9 lines in your changes missing coverage. Please review.

Project coverage is 90.60%. Comparing base (3ccf064) to head (315193d).
Report is 22 commits behind head on main.

FilesPatch %Lines
lightning/src/ln/channel.rs57.89%5 Missing and 3 partials ⚠️
lightning/src/ln/channelmanager.rs94.11%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #3149 +/- ##
==========================================
+ Coverage 89.81% 90.60% +0.79% 
==========================================
Files 121 121 Lines 99314 105075 +5761 Branches 99314 105075 +5761 ==========================================
+ Hits 89195 95205 +6010 + Misses 7514 7313 -201 + Partials 2605 2557 -48 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Good test, too, though I need to go look at the test coverage changes. I assume this was hit in prod? We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmmmm, I'm a bit skeptical of this. get_last_commitment_update_for_send is called in:

  • monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.
  • channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

In any case, we should almost certainly add a test for the two above cases.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

Comment threadlightning/src/ln/async_signer_tests.rs Outdated
Comment threadlightning/src/ln/async_signer_tests.rs Outdated

@alecchendevalecchendev left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I assume this was hit in prod?

Yep, have had a couple related issues here in the past

We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

SGTM!

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch from 536b6b4 to 119b703CompareJuly 3, 2024 02:16
Comment on lines +360 to +361
fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool,
enable_signer_before_monitor_completion: bool, enable_signer_before_reestablish: bool) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

after the latest additional tests this helper is a bit unwieldy, not sure the best way to address it. I could copy this test 4 times and get rid of the conditionals, but it adds a lot of filler...

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yaknow actually i realized for what these are testing it isn't necessary to include them in this test (they don't care about the CS-RAA ordering, just that the CS signer pending flag is cleared at the right time), i'll just move them to their own separate tests tomorrow

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

okay, fixed this, moved them into the peer disconnection test

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch 2 times, most recently from ca37547 to 221ef18CompareJuly 3, 2024 21:11

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

LGTM

revoke_and_ack,
funding_signed,
channel_ready,
order: self.context.resend_order.clone(),

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.

Nit: clone() is not needed on order

Suggested change
order:self.context.resend_order.clone(),
order:self.context.resend_order,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

hmm, i think it actually does require clone since RAACommitmentOrder doesn't implement Copy?

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.

OK. RAACommitmentOrder is a primitive enum, but it indeed does not have Copy trait. I tried it without and it worked, but to be safe for future changes, either the clone is needed or the enum should be marked explicitly Copy. It's fine with the clone.

Comment threadlightning/src/ln/channel.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Jul 8, 2024

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, feel free to squash the fixups.

let bs_second_commitment_signed = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);

// The rest of this is boilerplate for resolving the previous state.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

At least some of this may be replaceable with one of the inscruitable commitment_signed_dance variants.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

i feared this day would come...

I tried my best, but didn't get very far, it seems this is a little different from the normal exchange so i'm not sure how much more time i should spend trying:

nodes0 nodes1
uah ->
cs ->
raa ->
<- raa
<- cs
cs ->
raa ->
<- raa

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the first few messages (after the uah) could be, but if you fought with it a bit I'm not gonna nitpick.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Merging as the only changes since @optout21's "LGTM" are addressing his feedback.

@TheBlueMatt
TheBlueMatt merged commit f48a273 into lightningdevkit:mainJul 9, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@alecchendev@codecov-commenter@TheBlueMatt@optout21
, '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

Fix bug failing CS-RAA resend order on pending commitment signatures - #3149

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order
Jul 9, 2024
Merged

Fix bug failing CS-RAA resend order on pending commitment signatures#3149
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order

Conversation

@alecchendev

Copy link
Copy Markdown
Contributor

Across disconnects we may end up in a situation where we need to send a commitment_signed and then revoke_and_ack. We need to make sure that if the signer is pending for CS but not RAA, we don't screw up the order by sending the RAA first. We defer sending the RAA by setting the flag signer_pending_revoke_and_ack, which will lead to us generating an RAA upon signer_unblocked.

We test this for both the case where we send messages after a channel reestablish, as well as restoring a channel after persisting a monitor update asynchronously.

@codecov-commenter

codecov-commenter commented Jun 30, 2024

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 75.00000% with 9 lines in your changes missing coverage. Please review.

Project coverage is 90.60%. Comparing base (3ccf064) to head (315193d).
Report is 22 commits behind head on main.

FilesPatch %Lines
lightning/src/ln/channel.rs57.89%5 Missing and 3 partials ⚠️
lightning/src/ln/channelmanager.rs94.11%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #3149 +/- ##
==========================================
+ Coverage 89.81% 90.60% +0.79% 
==========================================
Files 121 121 Lines 99314 105075 +5761 Branches 99314 105075 +5761 ==========================================
+ Hits 89195 95205 +6010 + Misses 7514 7313 -201 + Partials 2605 2557 -48 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Good test, too, though I need to go look at the test coverage changes. I assume this was hit in prod? We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmmmm, I'm a bit skeptical of this. get_last_commitment_update_for_send is called in:

  • monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.
  • channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

In any case, we should almost certainly add a test for the two above cases.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

Comment threadlightning/src/ln/async_signer_tests.rs Outdated
Comment threadlightning/src/ln/async_signer_tests.rs Outdated

@alecchendevalecchendev left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I assume this was hit in prod?

Yep, have had a couple related issues here in the past

We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

SGTM!

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch from 536b6b4 to 119b703CompareJuly 3, 2024 02:16
Comment on lines +360 to +361
fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool,
enable_signer_before_monitor_completion: bool, enable_signer_before_reestablish: bool) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

after the latest additional tests this helper is a bit unwieldy, not sure the best way to address it. I could copy this test 4 times and get rid of the conditionals, but it adds a lot of filler...

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yaknow actually i realized for what these are testing it isn't necessary to include them in this test (they don't care about the CS-RAA ordering, just that the CS signer pending flag is cleared at the right time), i'll just move them to their own separate tests tomorrow

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

okay, fixed this, moved them into the peer disconnection test

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch 2 times, most recently from ca37547 to 221ef18CompareJuly 3, 2024 21:11

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

LGTM

revoke_and_ack,
funding_signed,
channel_ready,
order: self.context.resend_order.clone(),

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.

Nit: clone() is not needed on order

Suggested change
order:self.context.resend_order.clone(),
order:self.context.resend_order,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

hmm, i think it actually does require clone since RAACommitmentOrder doesn't implement Copy?

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.

OK. RAACommitmentOrder is a primitive enum, but it indeed does not have Copy trait. I tried it without and it worked, but to be safe for future changes, either the clone is needed or the enum should be marked explicitly Copy. It's fine with the clone.

Comment threadlightning/src/ln/channel.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Jul 8, 2024

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, feel free to squash the fixups.

let bs_second_commitment_signed = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);

// The rest of this is boilerplate for resolving the previous state.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

At least some of this may be replaceable with one of the inscruitable commitment_signed_dance variants.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

i feared this day would come...

I tried my best, but didn't get very far, it seems this is a little different from the normal exchange so i'm not sure how much more time i should spend trying:

nodes0 nodes1
uah ->
cs ->
raa ->
<- raa
<- cs
cs ->
raa ->
<- raa

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the first few messages (after the uah) could be, but if you fought with it a bit I'm not gonna nitpick.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Merging as the only changes since @optout21's "LGTM" are addressing his feedback.

@TheBlueMatt
TheBlueMatt merged commit f48a273 into lightningdevkit:mainJul 9, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@alecchendev@codecov-commenter@TheBlueMatt@optout21
, '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

Fix bug failing CS-RAA resend order on pending commitment signatures - #3149

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order
Jul 9, 2024
Merged

Fix bug failing CS-RAA resend order on pending commitment signatures#3149
TheBlueMatt merged 2 commits into
lightningdevkit:mainfrom
alecchendev:2024-06-async-sign-cs-raa-order

Conversation

@alecchendev

Copy link
Copy Markdown
Contributor

Across disconnects we may end up in a situation where we need to send a commitment_signed and then revoke_and_ack. We need to make sure that if the signer is pending for CS but not RAA, we don't screw up the order by sending the RAA first. We defer sending the RAA by setting the flag signer_pending_revoke_and_ack, which will lead to us generating an RAA upon signer_unblocked.

We test this for both the case where we send messages after a channel reestablish, as well as restoring a channel after persisting a monitor update asynchronously.

@codecov-commenter

codecov-commenter commented Jun 30, 2024

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 75.00000% with 9 lines in your changes missing coverage. Please review.

Project coverage is 90.60%. Comparing base (3ccf064) to head (315193d).
Report is 22 commits behind head on main.

FilesPatch %Lines
lightning/src/ln/channel.rs57.89%5 Missing and 3 partials ⚠️
lightning/src/ln/channelmanager.rs94.11%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #3149 +/- ##
==========================================
+ Coverage 89.81% 90.60% +0.79% 
==========================================
Files 121 121 Lines 99314 105075 +5761 Branches 99314 105075 +5761 ==========================================
+ Hits 89195 95205 +6010 + Misses 7514 7313 -201 + Partials 2605 2557 -48 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Good test, too, though I need to go look at the test coverage changes. I assume this was hit in prod? We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmmmm, I'm a bit skeptical of this. get_last_commitment_update_for_send is called in:

  • monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.
  • channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

In any case, we should almost certainly add a test for the two above cases.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

Comment threadlightning/src/ln/async_signer_tests.rs Outdated
Comment threadlightning/src/ln/async_signer_tests.rs Outdated

@alecchendevalecchendev left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I assume this was hit in prod?

Yep, have had a couple related issues here in the past

We should get the rest of the async signing stuff upstream so we can spend some cycles fuzzing.

SGTM!

let commitment_signed = if let Ok(update) = self.send_commitment_no_state_update(logger).map(|(cu, _)| cu) {
if self.context.signer_pending_commitment_update {
log_trace!(logger, "Commitment update generated: clearing signer_pending_commitment_update");
self.context.signer_pending_commitment_update = false;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

monitor_updating_restored, which implies to me I think maybe if the user is doing both async monitor updating and async signing they may end up double-sending CS if (a) an update to the channel generates a monitor and signing update, (b) the signer completes but before it calls signer_maybe_unblocked (c) the monitor persistence completes, causing get_last_commitment_update_for_send to return a valid commitment which we send to our counterparty, then (d) the user calls signer_maybe_unblocked and we send it again, possibly causing the peer to FC on us.

I added a test for this, and I was expecting it to fail and need to change things, but it worked. I realized, i think it won't double send because before the monitor update completes it never calls the signer method, so the pending flag is never set. Even though it works, I think I'll change it back, I can see how setting the flags whenever the signer method is called is less prone to accidental double sends.

channel_reestablish, where I think we can hit a similar double-send when reconnecting to our counterparty where we (a) get a new update to sign, (b) the peer disconnects + reconnects, sending us their channel_reestablish, then (c) the user calls signer_maybe_unblocked which causes us to re-send the CS. This one may actually already be broken today, and implies we should be unsetting signer_pending_commitment_update when a peer disconnects.

great catch! yea this fails with my change. changing it back!

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch from 536b6b4 to 119b703CompareJuly 3, 2024 02:16
Comment on lines +360 to +361
fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool,
enable_signer_before_monitor_completion: bool, enable_signer_before_reestablish: bool) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

after the latest additional tests this helper is a bit unwieldy, not sure the best way to address it. I could copy this test 4 times and get rid of the conditionals, but it adds a lot of filler...

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yaknow actually i realized for what these are testing it isn't necessary to include them in this test (they don't care about the CS-RAA ordering, just that the CS signer pending flag is cleared at the right time), i'll just move them to their own separate tests tomorrow

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

okay, fixed this, moved them into the peer disconnection test

@alecchendev
alecchendevforce-pushed the 2024-06-async-sign-cs-raa-order branch 2 times, most recently from ca37547 to 221ef18CompareJuly 3, 2024 21:11

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

LGTM

revoke_and_ack,
funding_signed,
channel_ready,
order: self.context.resend_order.clone(),

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.

Nit: clone() is not needed on order

Suggested change
order:self.context.resend_order.clone(),
order:self.context.resend_order,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

hmm, i think it actually does require clone since RAACommitmentOrder doesn't implement Copy?

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.

OK. RAACommitmentOrder is a primitive enum, but it indeed does not have Copy trait. I tried it without and it worked, but to be safe for future changes, either the clone is needed or the enum should be marked explicitly Copy. It's fine with the clone.

Comment threadlightning/src/ln/channel.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Jul 8, 2024

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, feel free to squash the fixups.

let bs_second_commitment_signed = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);

// The rest of this is boilerplate for resolving the previous state.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

At least some of this may be replaceable with one of the inscruitable commitment_signed_dance variants.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

i feared this day would come...

I tried my best, but didn't get very far, it seems this is a little different from the normal exchange so i'm not sure how much more time i should spend trying:

nodes0 nodes1
uah ->
cs ->
raa ->
<- raa
<- cs
cs ->
raa ->
<- raa

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the first few messages (after the uah) could be, but if you fought with it a bit I'm not gonna nitpick.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Merging as the only changes since @optout21's "LGTM" are addressing his feedback.

@TheBlueMatt
TheBlueMatt merged commit f48a273 into lightningdevkit:mainJul 9, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@alecchendev@codecov-commenter@TheBlueMatt@optout21