Skip to content

Fail holding-cell AddHTLCs on Channel deser to match disconnection - #754

Closed
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic
Closed

Fail holding-cell AddHTLCs on Channel deser to match disconnection#754
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Note that this bug was found thanks to the changes in #753, but its reasonable to take this by itself for 0.0.12 without 753.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.

Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.

We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.

@TheBlueMattTheBlueMatt modified the milestone: 0.0.12Nov 19, 2020
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Note that I only think we need to take this for 0.0.12 because of the risk of forwarding something when we don't have a current chain - the assertion is only a debug assertion, and as far as I can tell the behavior even when hitting it is correct.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.
Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.
We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.
@TheBlueMatt
TheBlueMattforce-pushed the 2020-11-holding-cell-add-panic branch from 4956ad6 to 1ecee17CompareNovember 19, 2020 01:23
@codecov

codecovBot commented Nov 19, 2020

Copy link
Copy Markdown

Codecov Report

Merging #754 (728ead6) into main (4e82003) will increase coverage by 0.04%.
The diff coverage is 93.85%.

Impacted file tree graph

@@ Coverage Diff @@## main #754 +/- ##
==========================================
+ Coverage 91.45% 91.49% +0.04% 
==========================================
Files 37 37 Lines 22249 22340 +91 ==========================================
+ Hits 20347 20440 +93 + Misses 1902 1900 -2 
Impacted FilesCoverage Δ
lightning/src/ln/channel.rs88.08% <63.63%> (+0.64%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.51% <96.70%> (-0.06%)⬇️
lightning/src/ln/channelmanager.rs85.41% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_tests.rs96.94% <0.00%> (-0.23%)⬇️

Continue to review full report at Codecov.

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

@valentinewallace

valentinewallace commented Nov 20, 2020

Copy link
Copy Markdown
Contributor

In terms of "concept," the one question I have is whether this could lead to unexpected behavior for users. It's not obvious that reading state from disk would lead to failing HTLCs back.

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

(edit: possibly relevant tweet: https://twitter.com/jaredpalmer/status/1171415929865064449?s=20)

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

I'm a little confused by this - you have to provide the ChannelMonitor data to ChannelManager during deserialization and if a channel isn't in the same state in both then the channel will be closed. Doing some kind of late-updates is definitely not allowed by our API.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

Hopefully not. I tend to subscribe to the "having clean shutdown logic lulls you into a false sense of security" - user apps crash, especially on mobile, and getting a shutdown() function to be called reliably is a difficult problem. Either way we have to ensure that we support reloading after a crash, so might as well just make that the default.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Discussed it offline for a while with @valentinewallace, in short I think this at least doesn't make sense because we should be going a different direction - solve #661 and the debug-assert failure by not failing-back holding cell HTLCs just because monitor updating has paused or we went to disk, and think harder about how to handle pre-chain-sync load-time HTLC relaying more generally (probably for now just say that you shouldn't connect to peers until chain is synced).

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.

2 participants

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

Fail holding-cell AddHTLCs on Channel deser to match disconnection - #754

Closed
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic
Closed

Fail holding-cell AddHTLCs on Channel deser to match disconnection#754
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Note that this bug was found thanks to the changes in #753, but its reasonable to take this by itself for 0.0.12 without 753.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.

Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.

We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.

@TheBlueMattTheBlueMatt modified the milestone: 0.0.12Nov 19, 2020
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Note that I only think we need to take this for 0.0.12 because of the risk of forwarding something when we don't have a current chain - the assertion is only a debug assertion, and as far as I can tell the behavior even when hitting it is correct.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.
Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.
We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.
@TheBlueMatt
TheBlueMattforce-pushed the 2020-11-holding-cell-add-panic branch from 4956ad6 to 1ecee17CompareNovember 19, 2020 01:23
@codecov

codecovBot commented Nov 19, 2020

Copy link
Copy Markdown

Codecov Report

Merging #754 (728ead6) into main (4e82003) will increase coverage by 0.04%.
The diff coverage is 93.85%.

Impacted file tree graph

@@ Coverage Diff @@## main #754 +/- ##
==========================================
+ Coverage 91.45% 91.49% +0.04% 
==========================================
Files 37 37 Lines 22249 22340 +91 ==========================================
+ Hits 20347 20440 +93 + Misses 1902 1900 -2 
Impacted FilesCoverage Δ
lightning/src/ln/channel.rs88.08% <63.63%> (+0.64%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.51% <96.70%> (-0.06%)⬇️
lightning/src/ln/channelmanager.rs85.41% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_tests.rs96.94% <0.00%> (-0.23%)⬇️

Continue to review full report at Codecov.

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

@valentinewallace

valentinewallace commented Nov 20, 2020

Copy link
Copy Markdown
Contributor

In terms of "concept," the one question I have is whether this could lead to unexpected behavior for users. It's not obvious that reading state from disk would lead to failing HTLCs back.

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

(edit: possibly relevant tweet: https://twitter.com/jaredpalmer/status/1171415929865064449?s=20)

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

I'm a little confused by this - you have to provide the ChannelMonitor data to ChannelManager during deserialization and if a channel isn't in the same state in both then the channel will be closed. Doing some kind of late-updates is definitely not allowed by our API.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

Hopefully not. I tend to subscribe to the "having clean shutdown logic lulls you into a false sense of security" - user apps crash, especially on mobile, and getting a shutdown() function to be called reliably is a difficult problem. Either way we have to ensure that we support reloading after a crash, so might as well just make that the default.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Discussed it offline for a while with @valentinewallace, in short I think this at least doesn't make sense because we should be going a different direction - solve #661 and the debug-assert failure by not failing-back holding cell HTLCs just because monitor updating has paused or we went to disk, and think harder about how to handle pre-chain-sync load-time HTLC relaying more generally (probably for now just say that you shouldn't connect to peers until chain is synced).

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.

2 participants

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

Fail holding-cell AddHTLCs on Channel deser to match disconnection - #754

Closed
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic
Closed

Fail holding-cell AddHTLCs on Channel deser to match disconnection#754
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Note that this bug was found thanks to the changes in #753, but its reasonable to take this by itself for 0.0.12 without 753.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.

Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.

We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.

@TheBlueMattTheBlueMatt modified the milestone: 0.0.12Nov 19, 2020
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Note that I only think we need to take this for 0.0.12 because of the risk of forwarding something when we don't have a current chain - the assertion is only a debug assertion, and as far as I can tell the behavior even when hitting it is correct.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.
Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.
We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.
@TheBlueMatt
TheBlueMattforce-pushed the 2020-11-holding-cell-add-panic branch from 4956ad6 to 1ecee17CompareNovember 19, 2020 01:23
@codecov

codecovBot commented Nov 19, 2020

Copy link
Copy Markdown

Codecov Report

Merging #754 (728ead6) into main (4e82003) will increase coverage by 0.04%.
The diff coverage is 93.85%.

Impacted file tree graph

@@ Coverage Diff @@## main #754 +/- ##
==========================================
+ Coverage 91.45% 91.49% +0.04% 
==========================================
Files 37 37 Lines 22249 22340 +91 ==========================================
+ Hits 20347 20440 +93 + Misses 1902 1900 -2 
Impacted FilesCoverage Δ
lightning/src/ln/channel.rs88.08% <63.63%> (+0.64%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.51% <96.70%> (-0.06%)⬇️
lightning/src/ln/channelmanager.rs85.41% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_tests.rs96.94% <0.00%> (-0.23%)⬇️

Continue to review full report at Codecov.

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

@valentinewallace

valentinewallace commented Nov 20, 2020

Copy link
Copy Markdown
Contributor

In terms of "concept," the one question I have is whether this could lead to unexpected behavior for users. It's not obvious that reading state from disk would lead to failing HTLCs back.

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

(edit: possibly relevant tweet: https://twitter.com/jaredpalmer/status/1171415929865064449?s=20)

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

I'm a little confused by this - you have to provide the ChannelMonitor data to ChannelManager during deserialization and if a channel isn't in the same state in both then the channel will be closed. Doing some kind of late-updates is definitely not allowed by our API.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

Hopefully not. I tend to subscribe to the "having clean shutdown logic lulls you into a false sense of security" - user apps crash, especially on mobile, and getting a shutdown() function to be called reliably is a difficult problem. Either way we have to ensure that we support reloading after a crash, so might as well just make that the default.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Discussed it offline for a while with @valentinewallace, in short I think this at least doesn't make sense because we should be going a different direction - solve #661 and the debug-assert failure by not failing-back holding cell HTLCs just because monitor updating has paused or we went to disk, and think harder about how to handle pre-chain-sync load-time HTLC relaying more generally (probably for now just say that you shouldn't connect to peers until chain is synced).

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.

2 participants

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

Fail holding-cell AddHTLCs on Channel deser to match disconnection - #754

Closed
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic
Closed

Fail holding-cell AddHTLCs on Channel deser to match disconnection#754
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Note that this bug was found thanks to the changes in #753, but its reasonable to take this by itself for 0.0.12 without 753.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.

Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.

We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.

@TheBlueMattTheBlueMatt modified the milestone: 0.0.12Nov 19, 2020
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Note that I only think we need to take this for 0.0.12 because of the risk of forwarding something when we don't have a current chain - the assertion is only a debug assertion, and as far as I can tell the behavior even when hitting it is correct.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.
Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.
We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.
@TheBlueMatt
TheBlueMattforce-pushed the 2020-11-holding-cell-add-panic branch from 4956ad6 to 1ecee17CompareNovember 19, 2020 01:23
@codecov

codecovBot commented Nov 19, 2020

Copy link
Copy Markdown

Codecov Report

Merging #754 (728ead6) into main (4e82003) will increase coverage by 0.04%.
The diff coverage is 93.85%.

Impacted file tree graph

@@ Coverage Diff @@## main #754 +/- ##
==========================================
+ Coverage 91.45% 91.49% +0.04% 
==========================================
Files 37 37 Lines 22249 22340 +91 ==========================================
+ Hits 20347 20440 +93 + Misses 1902 1900 -2 
Impacted FilesCoverage Δ
lightning/src/ln/channel.rs88.08% <63.63%> (+0.64%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.51% <96.70%> (-0.06%)⬇️
lightning/src/ln/channelmanager.rs85.41% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_tests.rs96.94% <0.00%> (-0.23%)⬇️

Continue to review full report at Codecov.

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

@valentinewallace

valentinewallace commented Nov 20, 2020

Copy link
Copy Markdown
Contributor

In terms of "concept," the one question I have is whether this could lead to unexpected behavior for users. It's not obvious that reading state from disk would lead to failing HTLCs back.

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

(edit: possibly relevant tweet: https://twitter.com/jaredpalmer/status/1171415929865064449?s=20)

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

I'm a little confused by this - you have to provide the ChannelMonitor data to ChannelManager during deserialization and if a channel isn't in the same state in both then the channel will be closed. Doing some kind of late-updates is definitely not allowed by our API.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

Hopefully not. I tend to subscribe to the "having clean shutdown logic lulls you into a false sense of security" - user apps crash, especially on mobile, and getting a shutdown() function to be called reliably is a difficult problem. Either way we have to ensure that we support reloading after a crash, so might as well just make that the default.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Discussed it offline for a while with @valentinewallace, in short I think this at least doesn't make sense because we should be going a different direction - solve #661 and the debug-assert failure by not failing-back holding cell HTLCs just because monitor updating has paused or we went to disk, and think harder about how to handle pre-chain-sync load-time HTLC relaying more generally (probably for now just say that you shouldn't connect to peers until chain is synced).

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.

2 participants

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

Fail holding-cell AddHTLCs on Channel deser to match disconnection - #754

Closed
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic
Closed

Fail holding-cell AddHTLCs on Channel deser to match disconnection#754
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Note that this bug was found thanks to the changes in #753, but its reasonable to take this by itself for 0.0.12 without 753.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.

Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.

We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.

@TheBlueMattTheBlueMatt modified the milestone: 0.0.12Nov 19, 2020
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Note that I only think we need to take this for 0.0.12 because of the risk of forwarding something when we don't have a current chain - the assertion is only a debug assertion, and as far as I can tell the behavior even when hitting it is correct.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.
Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.
We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.
@TheBlueMatt
TheBlueMattforce-pushed the 2020-11-holding-cell-add-panic branch from 4956ad6 to 1ecee17CompareNovember 19, 2020 01:23
@codecov

codecovBot commented Nov 19, 2020

Copy link
Copy Markdown

Codecov Report

Merging #754 (728ead6) into main (4e82003) will increase coverage by 0.04%.
The diff coverage is 93.85%.

Impacted file tree graph

@@ Coverage Diff @@## main #754 +/- ##
==========================================
+ Coverage 91.45% 91.49% +0.04% 
==========================================
Files 37 37 Lines 22249 22340 +91 ==========================================
+ Hits 20347 20440 +93 + Misses 1902 1900 -2 
Impacted FilesCoverage Δ
lightning/src/ln/channel.rs88.08% <63.63%> (+0.64%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.51% <96.70%> (-0.06%)⬇️
lightning/src/ln/channelmanager.rs85.41% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_tests.rs96.94% <0.00%> (-0.23%)⬇️

Continue to review full report at Codecov.

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

@valentinewallace

valentinewallace commented Nov 20, 2020

Copy link
Copy Markdown
Contributor

In terms of "concept," the one question I have is whether this could lead to unexpected behavior for users. It's not obvious that reading state from disk would lead to failing HTLCs back.

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

(edit: possibly relevant tweet: https://twitter.com/jaredpalmer/status/1171415929865064449?s=20)

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

I'm a little confused by this - you have to provide the ChannelMonitor data to ChannelManager during deserialization and if a channel isn't in the same state in both then the channel will be closed. Doing some kind of late-updates is definitely not allowed by our API.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

Hopefully not. I tend to subscribe to the "having clean shutdown logic lulls you into a false sense of security" - user apps crash, especially on mobile, and getting a shutdown() function to be called reliably is a difficult problem. Either way we have to ensure that we support reloading after a crash, so might as well just make that the default.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Discussed it offline for a while with @valentinewallace, in short I think this at least doesn't make sense because we should be going a different direction - solve #661 and the debug-assert failure by not failing-back holding cell HTLCs just because monitor updating has paused or we went to disk, and think harder about how to handle pre-chain-sync load-time HTLC relaying more generally (probably for now just say that you shouldn't connect to peers until chain is synced).

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.

2 participants

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

Fail holding-cell AddHTLCs on Channel deser to match disconnection - #754

Closed
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic
Closed

Fail holding-cell AddHTLCs on Channel deser to match disconnection#754
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Note that this bug was found thanks to the changes in #753, but its reasonable to take this by itself for 0.0.12 without 753.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.

Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.

We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.

@TheBlueMattTheBlueMatt modified the milestone: 0.0.12Nov 19, 2020
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Note that I only think we need to take this for 0.0.12 because of the risk of forwarding something when we don't have a current chain - the assertion is only a debug assertion, and as far as I can tell the behavior even when hitting it is correct.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.
Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.
We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.
@TheBlueMatt
TheBlueMattforce-pushed the 2020-11-holding-cell-add-panic branch from 4956ad6 to 1ecee17CompareNovember 19, 2020 01:23
@codecov

codecovBot commented Nov 19, 2020

Copy link
Copy Markdown

Codecov Report

Merging #754 (728ead6) into main (4e82003) will increase coverage by 0.04%.
The diff coverage is 93.85%.

Impacted file tree graph

@@ Coverage Diff @@## main #754 +/- ##
==========================================
+ Coverage 91.45% 91.49% +0.04% 
==========================================
Files 37 37 Lines 22249 22340 +91 ==========================================
+ Hits 20347 20440 +93 + Misses 1902 1900 -2 
Impacted FilesCoverage Δ
lightning/src/ln/channel.rs88.08% <63.63%> (+0.64%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.51% <96.70%> (-0.06%)⬇️
lightning/src/ln/channelmanager.rs85.41% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_tests.rs96.94% <0.00%> (-0.23%)⬇️

Continue to review full report at Codecov.

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

@valentinewallace

valentinewallace commented Nov 20, 2020

Copy link
Copy Markdown
Contributor

In terms of "concept," the one question I have is whether this could lead to unexpected behavior for users. It's not obvious that reading state from disk would lead to failing HTLCs back.

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

(edit: possibly relevant tweet: https://twitter.com/jaredpalmer/status/1171415929865064449?s=20)

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

I'm a little confused by this - you have to provide the ChannelMonitor data to ChannelManager during deserialization and if a channel isn't in the same state in both then the channel will be closed. Doing some kind of late-updates is definitely not allowed by our API.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

Hopefully not. I tend to subscribe to the "having clean shutdown logic lulls you into a false sense of security" - user apps crash, especially on mobile, and getting a shutdown() function to be called reliably is a difficult problem. Either way we have to ensure that we support reloading after a crash, so might as well just make that the default.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Discussed it offline for a while with @valentinewallace, in short I think this at least doesn't make sense because we should be going a different direction - solve #661 and the debug-assert failure by not failing-back holding cell HTLCs just because monitor updating has paused or we went to disk, and think harder about how to handle pre-chain-sync load-time HTLC relaying more generally (probably for now just say that you shouldn't connect to peers until chain is synced).

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.

2 participants

@TheBlueMatt@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fail holding-cell AddHTLCs on Channel deser to match disconnection by TheBlueMatt · Pull Request #754 · lightningdevkit/rust-lightning · GitHub
Skip to content

Fail holding-cell AddHTLCs on Channel deser to match disconnection - #754

Closed
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic
Closed

Fail holding-cell AddHTLCs on Channel deser to match disconnection#754
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Note that this bug was found thanks to the changes in #753, but its reasonable to take this by itself for 0.0.12 without 753.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.

Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.

We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.

@TheBlueMattTheBlueMatt modified the milestone: 0.0.12Nov 19, 2020
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Note that I only think we need to take this for 0.0.12 because of the risk of forwarding something when we don't have a current chain - the assertion is only a debug assertion, and as far as I can tell the behavior even when hitting it is correct.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.
Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.
We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.
@TheBlueMatt
TheBlueMattforce-pushed the 2020-11-holding-cell-add-panic branch from 4956ad6 to 1ecee17CompareNovember 19, 2020 01:23
@codecov

codecovBot commented Nov 19, 2020

Copy link
Copy Markdown

Codecov Report

Merging #754 (728ead6) into main (4e82003) will increase coverage by 0.04%.
The diff coverage is 93.85%.

Impacted file tree graph

@@ Coverage Diff @@## main #754 +/- ##
==========================================
+ Coverage 91.45% 91.49% +0.04% 
==========================================
Files 37 37 Lines 22249 22340 +91 ==========================================
+ Hits 20347 20440 +93 + Misses 1902 1900 -2 
Impacted FilesCoverage Δ
lightning/src/ln/channel.rs88.08% <63.63%> (+0.64%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.51% <96.70%> (-0.06%)⬇️
lightning/src/ln/channelmanager.rs85.41% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_tests.rs96.94% <0.00%> (-0.23%)⬇️

Continue to review full report at Codecov.

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

@valentinewallace

valentinewallace commented Nov 20, 2020

Copy link
Copy Markdown
Contributor

In terms of "concept," the one question I have is whether this could lead to unexpected behavior for users. It's not obvious that reading state from disk would lead to failing HTLCs back.

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

(edit: possibly relevant tweet: https://twitter.com/jaredpalmer/status/1171415929865064449?s=20)

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

I'm a little confused by this - you have to provide the ChannelMonitor data to ChannelManager during deserialization and if a channel isn't in the same state in both then the channel will be closed. Doing some kind of late-updates is definitely not allowed by our API.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

Hopefully not. I tend to subscribe to the "having clean shutdown logic lulls you into a false sense of security" - user apps crash, especially on mobile, and getting a shutdown() function to be called reliably is a difficult problem. Either way we have to ensure that we support reloading after a crash, so might as well just make that the default.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Discussed it offline for a while with @valentinewallace, in short I think this at least doesn't make sense because we should be going a different direction - solve #661 and the debug-assert failure by not failing-back holding cell HTLCs just because monitor updating has paused or we went to disk, and think harder about how to handle pre-chain-sync load-time HTLC relaying more generally (probably for now just say that you shouldn't connect to peers until chain is synced).

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.

2 participants

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

Fail holding-cell AddHTLCs on Channel deser to match disconnection - #754

Closed
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic
Closed

Fail holding-cell AddHTLCs on Channel deser to match disconnection#754
TheBlueMatt wants to merge 1 commit into
lightningdevkit:mainfrom
TheBlueMatt:2020-11-holding-cell-add-panic

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Note that this bug was found thanks to the changes in #753, but its reasonable to take this by itself for 0.0.12 without 753.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.

Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.

We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.

@TheBlueMattTheBlueMatt modified the milestone: 0.0.12Nov 19, 2020
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Note that I only think we need to take this for 0.0.12 because of the risk of forwarding something when we don't have a current chain - the assertion is only a debug assertion, and as far as I can tell the behavior even when hitting it is correct.

As Channel::write says in the comment at the top: "we write out as
if remove_uncommitted_htlcs_and_mark_paused had just been called",
except that we previously deliberately included holding-cell
AddHTLC events in the serialization. On the flip side, in
remove_uncommitted_htlcs_and_mark_paused, we removed pending
AddHTLC events under the assumption that, if we can't forward
something ASAP, its better to fail it back to the origin than to
sit on it for a while.
Given there's likely to be just as large a time-lag between
ser/deserialization as between when a peer dis/reconnects, there
isn't much of a reason for this difference. Worse, we debug_assert
that there are no pending AddHTLC holding cell events when doing a
reconnect, so any tests or fuzzers which deserialized a
ChannelManager with AddHTLC events would panic.
We resolve this by adding logic to fail any holding-cell AddHTLC
events upon deserialization, in part because trying to forward it
before we're sure we have an up-to-date chain is somewhat risky -
the sender may have already gone to chain while our upstream has
not.
@TheBlueMatt
TheBlueMattforce-pushed the 2020-11-holding-cell-add-panic branch from 4956ad6 to 1ecee17CompareNovember 19, 2020 01:23
@codecov

codecovBot commented Nov 19, 2020

Copy link
Copy Markdown

Codecov Report

Merging #754 (728ead6) into main (4e82003) will increase coverage by 0.04%.
The diff coverage is 93.85%.

Impacted file tree graph

@@ Coverage Diff @@## main #754 +/- ##
==========================================
+ Coverage 91.45% 91.49% +0.04% 
==========================================
Files 37 37 Lines 22249 22340 +91 ==========================================
+ Hits 20347 20440 +93 + Misses 1902 1900 -2 
Impacted FilesCoverage Δ
lightning/src/ln/channel.rs88.08% <63.63%> (+0.64%)⬆️
lightning/src/ln/chanmon_update_fail_tests.rs97.51% <96.70%> (-0.06%)⬇️
lightning/src/ln/channelmanager.rs85.41% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_tests.rs96.94% <0.00%> (-0.23%)⬇️

Continue to review full report at Codecov.

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

@valentinewallace

valentinewallace commented Nov 20, 2020

Copy link
Copy Markdown
Contributor

In terms of "concept," the one question I have is whether this could lead to unexpected behavior for users. It's not obvious that reading state from disk would lead to failing HTLCs back.

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

(edit: possibly relevant tweet: https://twitter.com/jaredpalmer/status/1171415929865064449?s=20)

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

For example, if a user has a method that reads all of RL's info from disk, and they batch ChannelMonitor updates such that they do have to read from disk periodically while the node is still running (inefficient on their part but maybe plausible), then after this PR, HTLCs would be unexpectedly failed backwards while the node is running.

I'm a little confused by this - you have to provide the ChannelMonitor data to ChannelManager during deserialization and if a channel isn't in the same state in both then the channel will be closed. Doing some kind of late-updates is definitely not allowed by our API.

Maybe this PR speaks to a need for ChannelManager to have a shutdown() function, so it'd have the opportunity to fail HTLCs back on shutdown? (then we could also persist to disk one last time on shutdown, which might be nice)

Hopefully not. I tend to subscribe to the "having clean shutdown logic lulls you into a false sense of security" - user apps crash, especially on mobile, and getting a shutdown() function to be called reliably is a difficult problem. Either way we have to ensure that we support reloading after a crash, so might as well just make that the default.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Discussed it offline for a while with @valentinewallace, in short I think this at least doesn't make sense because we should be going a different direction - solve #661 and the debug-assert failure by not failing-back holding cell HTLCs just because monitor updating has paused or we went to disk, and think harder about how to handle pre-chain-sync load-time HTLC relaying more generally (probably for now just say that you shouldn't connect to peers until chain is synced).

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.

2 participants

@TheBlueMatt@valentinewallace