OutputSweeper: Delay pruning until monitors have likely been archived - #3559

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements
Jan 27, 2025
Merged

OutputSweeper: Delay pruning until monitors have likely been archived #3559
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, we would prune tracked descriptors once we see a spend hit
ANTI_REORG_DELAY = 6 confirmations. However, this could lead to a
scenario where lingering ChannelMonitors waiting to be archived would
still regenerate and replay Event::SpendableOutputs, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.

Here, we therefore keep the tracked descriptors around for longer, in
particular at least ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.

use core::ops::Deref;

/// The number of blocks we wait before we prune the tracked spendable outputs.
pub const PRUNE_DELAY_BLOCKS: u32 = ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY;

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.

how likely is it that 4032 blocks was insufficient, but 4038 was?

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.

Well, the idea here is that we start pruning after the monitors have been archived. For that to work we need to wait at least 4032 blocks, but of course we're not super sure how soon after the threshold has been met user will actually call archive_fully_resolved_monitors, so I added the reorg delay on top. Could be debatable one way or another.

FWIW, I alternatively considered introducing a new MonitorArchivalComplete event type, but given that we on purpose keep the archival a best-effort thing it seemed wrong to go this way.

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.

pretty unlikely, but holding onto them doesn't really cost us anything, so might as well...

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.

yeah, I would have though max(4032, 6) would have also worked, but given that it indeed costs nothing…

@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, one comment.

Comment threadlightning/src/util/sweep.rs Outdated
.. previously we just used the 4032 magic number, here we put it in a
`pub const` that is reusable elsewhere.
Previously, we would prune tracked descriptors once we see a spend hit
`ANTI_REORG_DELAY = 6` confirmations. However, this could lead to a
scenario where lingering `ChannelMonitor`s waiting to be archived would
still regenerate and replay `Event::SpendableOutput`s, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.
Here, we therefore keep the tracked descriptors around for longer, in
particular at least `ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038`
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.
@tnull
tnullforce-pushed the 2025-01-sweeper-improvements branch from 0aa813f to 84412ccCompareJanuary 27, 2025 08:56
Comment threadlightning/src/chain/channelmonitor.rs
@tnull
tnull merged commit 79267d3 into lightningdevkit:mainJan 27, 2025
@TheBlueMattTheBlueMatt mentioned this pull request Jan 28, 2025
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Backported in #3567

TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jan 29, 2025
v0.1.1 - Jan 28, 2025 - "Onchain Matters"
API Updates
===========
* A `ChannelManager::send_payment_with_route` was (re-)added, with semantics
similar to `ChannelManager::send_payment` (rather than like the pre-0.1
`send_payent_with_route`, lightningdevkit#3534).
* `RawBolt11Invoice::{to,from}_raw` were added (lightningdevkit#3549).
Bug Fixes
=========
* HTLCs which were forwarded where the inbound edge times out within the next
three blocks will have the inbound HTLC failed backwards irrespective of the
status of the outbound HTLC. This avoids the peer force-closing the channel
(and claiming the inbound edge HTLC on-chain) even if we have not yet managed
to claim the outbound edge on chain (lightningdevkit#3556).
* On restart, replay of `Event::SpendableOutput`s could have caused
`OutputSweeper` to generate double-spending transactions, making it unable to
claim any delayed claims. This was resolved by retaining old claims for more
than four weeks after they are claimed on-chain to detect replays (lightningdevkit#3559).
* Fixed the additional feerate we will pay each time we RBF on-chain claims to
match the Bitcoin Core policy (1 sat/vB) instead of 16 sats/vB (lightningdevkit#3457).
* Fixed a cased where a custom `Router` which returns an invalid `Route`,
provided to `ChannelManager`, can result in an outbound payment remaining
pending forever despite no HTLCs being pending (lightningdevkit#3531).
Security
========
0.1.1 fixes a denial-of-service vulnerability allowing channel counterparties to
cause force-closure of unrelated channels.
* If a malicious channel counterparty force-closes a channel, broadcasting a
revoked commitment transaction while the channel at closure time included
multiple non-dust forwarded outbound HTLCs with identical payment hashes and
amounts, failure to fail the HTLCs backwards could cause the channels on
which we recieved the corresponding inbound HTLCs to be force-closed. Note
that we'll receive, at a minimum, the malicious counterparty's reserve value
when they broadcast the stale commitment (lightningdevkit#3556). Thanks to Matt Morehouse for
reporting this issue.
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.

3 participants

@tnull@TheBlueMatt@arik-so
, '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

OutputSweeper: Delay pruning until monitors have likely been archived - #3559

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements
Jan 27, 2025
Merged

OutputSweeper: Delay pruning until monitors have likely been archived #3559
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, we would prune tracked descriptors once we see a spend hit
ANTI_REORG_DELAY = 6 confirmations. However, this could lead to a
scenario where lingering ChannelMonitors waiting to be archived would
still regenerate and replay Event::SpendableOutputs, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.

Here, we therefore keep the tracked descriptors around for longer, in
particular at least ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.

use core::ops::Deref;

/// The number of blocks we wait before we prune the tracked spendable outputs.
pub const PRUNE_DELAY_BLOCKS: u32 = ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY;

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.

how likely is it that 4032 blocks was insufficient, but 4038 was?

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.

Well, the idea here is that we start pruning after the monitors have been archived. For that to work we need to wait at least 4032 blocks, but of course we're not super sure how soon after the threshold has been met user will actually call archive_fully_resolved_monitors, so I added the reorg delay on top. Could be debatable one way or another.

FWIW, I alternatively considered introducing a new MonitorArchivalComplete event type, but given that we on purpose keep the archival a best-effort thing it seemed wrong to go this way.

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.

pretty unlikely, but holding onto them doesn't really cost us anything, so might as well...

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.

yeah, I would have though max(4032, 6) would have also worked, but given that it indeed costs nothing…

@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, one comment.

Comment threadlightning/src/util/sweep.rs Outdated
.. previously we just used the 4032 magic number, here we put it in a
`pub const` that is reusable elsewhere.
Previously, we would prune tracked descriptors once we see a spend hit
`ANTI_REORG_DELAY = 6` confirmations. However, this could lead to a
scenario where lingering `ChannelMonitor`s waiting to be archived would
still regenerate and replay `Event::SpendableOutput`s, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.
Here, we therefore keep the tracked descriptors around for longer, in
particular at least `ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038`
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.
@tnull
tnullforce-pushed the 2025-01-sweeper-improvements branch from 0aa813f to 84412ccCompareJanuary 27, 2025 08:56
Comment threadlightning/src/chain/channelmonitor.rs
@tnull
tnull merged commit 79267d3 into lightningdevkit:mainJan 27, 2025
@TheBlueMattTheBlueMatt mentioned this pull request Jan 28, 2025
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Backported in #3567

TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jan 29, 2025
v0.1.1 - Jan 28, 2025 - "Onchain Matters"
API Updates
===========
* A `ChannelManager::send_payment_with_route` was (re-)added, with semantics
similar to `ChannelManager::send_payment` (rather than like the pre-0.1
`send_payent_with_route`, lightningdevkit#3534).
* `RawBolt11Invoice::{to,from}_raw` were added (lightningdevkit#3549).
Bug Fixes
=========
* HTLCs which were forwarded where the inbound edge times out within the next
three blocks will have the inbound HTLC failed backwards irrespective of the
status of the outbound HTLC. This avoids the peer force-closing the channel
(and claiming the inbound edge HTLC on-chain) even if we have not yet managed
to claim the outbound edge on chain (lightningdevkit#3556).
* On restart, replay of `Event::SpendableOutput`s could have caused
`OutputSweeper` to generate double-spending transactions, making it unable to
claim any delayed claims. This was resolved by retaining old claims for more
than four weeks after they are claimed on-chain to detect replays (lightningdevkit#3559).
* Fixed the additional feerate we will pay each time we RBF on-chain claims to
match the Bitcoin Core policy (1 sat/vB) instead of 16 sats/vB (lightningdevkit#3457).
* Fixed a cased where a custom `Router` which returns an invalid `Route`,
provided to `ChannelManager`, can result in an outbound payment remaining
pending forever despite no HTLCs being pending (lightningdevkit#3531).
Security
========
0.1.1 fixes a denial-of-service vulnerability allowing channel counterparties to
cause force-closure of unrelated channels.
* If a malicious channel counterparty force-closes a channel, broadcasting a
revoked commitment transaction while the channel at closure time included
multiple non-dust forwarded outbound HTLCs with identical payment hashes and
amounts, failure to fail the HTLCs backwards could cause the channels on
which we recieved the corresponding inbound HTLCs to be force-closed. Note
that we'll receive, at a minimum, the malicious counterparty's reserve value
when they broadcast the stale commitment (lightningdevkit#3556). Thanks to Matt Morehouse for
reporting this issue.
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.

3 participants

@tnull@TheBlueMatt@arik-so
, '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

OutputSweeper: Delay pruning until monitors have likely been archived - #3559

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements
Jan 27, 2025
Merged

OutputSweeper: Delay pruning until monitors have likely been archived #3559
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, we would prune tracked descriptors once we see a spend hit
ANTI_REORG_DELAY = 6 confirmations. However, this could lead to a
scenario where lingering ChannelMonitors waiting to be archived would
still regenerate and replay Event::SpendableOutputs, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.

Here, we therefore keep the tracked descriptors around for longer, in
particular at least ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.

use core::ops::Deref;

/// The number of blocks we wait before we prune the tracked spendable outputs.
pub const PRUNE_DELAY_BLOCKS: u32 = ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY;

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.

how likely is it that 4032 blocks was insufficient, but 4038 was?

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.

Well, the idea here is that we start pruning after the monitors have been archived. For that to work we need to wait at least 4032 blocks, but of course we're not super sure how soon after the threshold has been met user will actually call archive_fully_resolved_monitors, so I added the reorg delay on top. Could be debatable one way or another.

FWIW, I alternatively considered introducing a new MonitorArchivalComplete event type, but given that we on purpose keep the archival a best-effort thing it seemed wrong to go this way.

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.

pretty unlikely, but holding onto them doesn't really cost us anything, so might as well...

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.

yeah, I would have though max(4032, 6) would have also worked, but given that it indeed costs nothing…

@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, one comment.

Comment threadlightning/src/util/sweep.rs Outdated
.. previously we just used the 4032 magic number, here we put it in a
`pub const` that is reusable elsewhere.
Previously, we would prune tracked descriptors once we see a spend hit
`ANTI_REORG_DELAY = 6` confirmations. However, this could lead to a
scenario where lingering `ChannelMonitor`s waiting to be archived would
still regenerate and replay `Event::SpendableOutput`s, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.
Here, we therefore keep the tracked descriptors around for longer, in
particular at least `ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038`
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.
@tnull
tnullforce-pushed the 2025-01-sweeper-improvements branch from 0aa813f to 84412ccCompareJanuary 27, 2025 08:56
Comment threadlightning/src/chain/channelmonitor.rs
@tnull
tnull merged commit 79267d3 into lightningdevkit:mainJan 27, 2025
@TheBlueMattTheBlueMatt mentioned this pull request Jan 28, 2025
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Backported in #3567

TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jan 29, 2025
v0.1.1 - Jan 28, 2025 - "Onchain Matters"
API Updates
===========
* A `ChannelManager::send_payment_with_route` was (re-)added, with semantics
similar to `ChannelManager::send_payment` (rather than like the pre-0.1
`send_payent_with_route`, lightningdevkit#3534).
* `RawBolt11Invoice::{to,from}_raw` were added (lightningdevkit#3549).
Bug Fixes
=========
* HTLCs which were forwarded where the inbound edge times out within the next
three blocks will have the inbound HTLC failed backwards irrespective of the
status of the outbound HTLC. This avoids the peer force-closing the channel
(and claiming the inbound edge HTLC on-chain) even if we have not yet managed
to claim the outbound edge on chain (lightningdevkit#3556).
* On restart, replay of `Event::SpendableOutput`s could have caused
`OutputSweeper` to generate double-spending transactions, making it unable to
claim any delayed claims. This was resolved by retaining old claims for more
than four weeks after they are claimed on-chain to detect replays (lightningdevkit#3559).
* Fixed the additional feerate we will pay each time we RBF on-chain claims to
match the Bitcoin Core policy (1 sat/vB) instead of 16 sats/vB (lightningdevkit#3457).
* Fixed a cased where a custom `Router` which returns an invalid `Route`,
provided to `ChannelManager`, can result in an outbound payment remaining
pending forever despite no HTLCs being pending (lightningdevkit#3531).
Security
========
0.1.1 fixes a denial-of-service vulnerability allowing channel counterparties to
cause force-closure of unrelated channels.
* If a malicious channel counterparty force-closes a channel, broadcasting a
revoked commitment transaction while the channel at closure time included
multiple non-dust forwarded outbound HTLCs with identical payment hashes and
amounts, failure to fail the HTLCs backwards could cause the channels on
which we recieved the corresponding inbound HTLCs to be force-closed. Note
that we'll receive, at a minimum, the malicious counterparty's reserve value
when they broadcast the stale commitment (lightningdevkit#3556). Thanks to Matt Morehouse for
reporting this issue.
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.

3 participants

@tnull@TheBlueMatt@arik-so
, '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

OutputSweeper: Delay pruning until monitors have likely been archived - #3559

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements
Jan 27, 2025
Merged

OutputSweeper: Delay pruning until monitors have likely been archived #3559
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, we would prune tracked descriptors once we see a spend hit
ANTI_REORG_DELAY = 6 confirmations. However, this could lead to a
scenario where lingering ChannelMonitors waiting to be archived would
still regenerate and replay Event::SpendableOutputs, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.

Here, we therefore keep the tracked descriptors around for longer, in
particular at least ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.

use core::ops::Deref;

/// The number of blocks we wait before we prune the tracked spendable outputs.
pub const PRUNE_DELAY_BLOCKS: u32 = ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY;

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.

how likely is it that 4032 blocks was insufficient, but 4038 was?

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.

Well, the idea here is that we start pruning after the monitors have been archived. For that to work we need to wait at least 4032 blocks, but of course we're not super sure how soon after the threshold has been met user will actually call archive_fully_resolved_monitors, so I added the reorg delay on top. Could be debatable one way or another.

FWIW, I alternatively considered introducing a new MonitorArchivalComplete event type, but given that we on purpose keep the archival a best-effort thing it seemed wrong to go this way.

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.

pretty unlikely, but holding onto them doesn't really cost us anything, so might as well...

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.

yeah, I would have though max(4032, 6) would have also worked, but given that it indeed costs nothing…

@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, one comment.

Comment threadlightning/src/util/sweep.rs Outdated
.. previously we just used the 4032 magic number, here we put it in a
`pub const` that is reusable elsewhere.
Previously, we would prune tracked descriptors once we see a spend hit
`ANTI_REORG_DELAY = 6` confirmations. However, this could lead to a
scenario where lingering `ChannelMonitor`s waiting to be archived would
still regenerate and replay `Event::SpendableOutput`s, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.
Here, we therefore keep the tracked descriptors around for longer, in
particular at least `ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038`
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.
@tnull
tnullforce-pushed the 2025-01-sweeper-improvements branch from 0aa813f to 84412ccCompareJanuary 27, 2025 08:56
Comment threadlightning/src/chain/channelmonitor.rs
@tnull
tnull merged commit 79267d3 into lightningdevkit:mainJan 27, 2025
@TheBlueMattTheBlueMatt mentioned this pull request Jan 28, 2025
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Backported in #3567

TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jan 29, 2025
v0.1.1 - Jan 28, 2025 - "Onchain Matters"
API Updates
===========
* A `ChannelManager::send_payment_with_route` was (re-)added, with semantics
similar to `ChannelManager::send_payment` (rather than like the pre-0.1
`send_payent_with_route`, lightningdevkit#3534).
* `RawBolt11Invoice::{to,from}_raw` were added (lightningdevkit#3549).
Bug Fixes
=========
* HTLCs which were forwarded where the inbound edge times out within the next
three blocks will have the inbound HTLC failed backwards irrespective of the
status of the outbound HTLC. This avoids the peer force-closing the channel
(and claiming the inbound edge HTLC on-chain) even if we have not yet managed
to claim the outbound edge on chain (lightningdevkit#3556).
* On restart, replay of `Event::SpendableOutput`s could have caused
`OutputSweeper` to generate double-spending transactions, making it unable to
claim any delayed claims. This was resolved by retaining old claims for more
than four weeks after they are claimed on-chain to detect replays (lightningdevkit#3559).
* Fixed the additional feerate we will pay each time we RBF on-chain claims to
match the Bitcoin Core policy (1 sat/vB) instead of 16 sats/vB (lightningdevkit#3457).
* Fixed a cased where a custom `Router` which returns an invalid `Route`,
provided to `ChannelManager`, can result in an outbound payment remaining
pending forever despite no HTLCs being pending (lightningdevkit#3531).
Security
========
0.1.1 fixes a denial-of-service vulnerability allowing channel counterparties to
cause force-closure of unrelated channels.
* If a malicious channel counterparty force-closes a channel, broadcasting a
revoked commitment transaction while the channel at closure time included
multiple non-dust forwarded outbound HTLCs with identical payment hashes and
amounts, failure to fail the HTLCs backwards could cause the channels on
which we recieved the corresponding inbound HTLCs to be force-closed. Note
that we'll receive, at a minimum, the malicious counterparty's reserve value
when they broadcast the stale commitment (lightningdevkit#3556). Thanks to Matt Morehouse for
reporting this issue.
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.

3 participants

@tnull@TheBlueMatt@arik-so
, '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

OutputSweeper: Delay pruning until monitors have likely been archived - #3559

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements
Jan 27, 2025
Merged

OutputSweeper: Delay pruning until monitors have likely been archived #3559
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, we would prune tracked descriptors once we see a spend hit
ANTI_REORG_DELAY = 6 confirmations. However, this could lead to a
scenario where lingering ChannelMonitors waiting to be archived would
still regenerate and replay Event::SpendableOutputs, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.

Here, we therefore keep the tracked descriptors around for longer, in
particular at least ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.

use core::ops::Deref;

/// The number of blocks we wait before we prune the tracked spendable outputs.
pub const PRUNE_DELAY_BLOCKS: u32 = ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY;

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.

how likely is it that 4032 blocks was insufficient, but 4038 was?

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.

Well, the idea here is that we start pruning after the monitors have been archived. For that to work we need to wait at least 4032 blocks, but of course we're not super sure how soon after the threshold has been met user will actually call archive_fully_resolved_monitors, so I added the reorg delay on top. Could be debatable one way or another.

FWIW, I alternatively considered introducing a new MonitorArchivalComplete event type, but given that we on purpose keep the archival a best-effort thing it seemed wrong to go this way.

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.

pretty unlikely, but holding onto them doesn't really cost us anything, so might as well...

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.

yeah, I would have though max(4032, 6) would have also worked, but given that it indeed costs nothing…

@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, one comment.

Comment threadlightning/src/util/sweep.rs Outdated
.. previously we just used the 4032 magic number, here we put it in a
`pub const` that is reusable elsewhere.
Previously, we would prune tracked descriptors once we see a spend hit
`ANTI_REORG_DELAY = 6` confirmations. However, this could lead to a
scenario where lingering `ChannelMonitor`s waiting to be archived would
still regenerate and replay `Event::SpendableOutput`s, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.
Here, we therefore keep the tracked descriptors around for longer, in
particular at least `ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038`
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.
@tnull
tnullforce-pushed the 2025-01-sweeper-improvements branch from 0aa813f to 84412ccCompareJanuary 27, 2025 08:56
Comment threadlightning/src/chain/channelmonitor.rs
@tnull
tnull merged commit 79267d3 into lightningdevkit:mainJan 27, 2025
@TheBlueMattTheBlueMatt mentioned this pull request Jan 28, 2025
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Backported in #3567

TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jan 29, 2025
v0.1.1 - Jan 28, 2025 - "Onchain Matters"
API Updates
===========
* A `ChannelManager::send_payment_with_route` was (re-)added, with semantics
similar to `ChannelManager::send_payment` (rather than like the pre-0.1
`send_payent_with_route`, lightningdevkit#3534).
* `RawBolt11Invoice::{to,from}_raw` were added (lightningdevkit#3549).
Bug Fixes
=========
* HTLCs which were forwarded where the inbound edge times out within the next
three blocks will have the inbound HTLC failed backwards irrespective of the
status of the outbound HTLC. This avoids the peer force-closing the channel
(and claiming the inbound edge HTLC on-chain) even if we have not yet managed
to claim the outbound edge on chain (lightningdevkit#3556).
* On restart, replay of `Event::SpendableOutput`s could have caused
`OutputSweeper` to generate double-spending transactions, making it unable to
claim any delayed claims. This was resolved by retaining old claims for more
than four weeks after they are claimed on-chain to detect replays (lightningdevkit#3559).
* Fixed the additional feerate we will pay each time we RBF on-chain claims to
match the Bitcoin Core policy (1 sat/vB) instead of 16 sats/vB (lightningdevkit#3457).
* Fixed a cased where a custom `Router` which returns an invalid `Route`,
provided to `ChannelManager`, can result in an outbound payment remaining
pending forever despite no HTLCs being pending (lightningdevkit#3531).
Security
========
0.1.1 fixes a denial-of-service vulnerability allowing channel counterparties to
cause force-closure of unrelated channels.
* If a malicious channel counterparty force-closes a channel, broadcasting a
revoked commitment transaction while the channel at closure time included
multiple non-dust forwarded outbound HTLCs with identical payment hashes and
amounts, failure to fail the HTLCs backwards could cause the channels on
which we recieved the corresponding inbound HTLCs to be force-closed. Note
that we'll receive, at a minimum, the malicious counterparty's reserve value
when they broadcast the stale commitment (lightningdevkit#3556). Thanks to Matt Morehouse for
reporting this issue.
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.

3 participants

@tnull@TheBlueMatt@arik-so
, '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

OutputSweeper: Delay pruning until monitors have likely been archived - #3559

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements
Jan 27, 2025
Merged

OutputSweeper: Delay pruning until monitors have likely been archived #3559
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, we would prune tracked descriptors once we see a spend hit
ANTI_REORG_DELAY = 6 confirmations. However, this could lead to a
scenario where lingering ChannelMonitors waiting to be archived would
still regenerate and replay Event::SpendableOutputs, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.

Here, we therefore keep the tracked descriptors around for longer, in
particular at least ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.

use core::ops::Deref;

/// The number of blocks we wait before we prune the tracked spendable outputs.
pub const PRUNE_DELAY_BLOCKS: u32 = ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY;

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.

how likely is it that 4032 blocks was insufficient, but 4038 was?

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.

Well, the idea here is that we start pruning after the monitors have been archived. For that to work we need to wait at least 4032 blocks, but of course we're not super sure how soon after the threshold has been met user will actually call archive_fully_resolved_monitors, so I added the reorg delay on top. Could be debatable one way or another.

FWIW, I alternatively considered introducing a new MonitorArchivalComplete event type, but given that we on purpose keep the archival a best-effort thing it seemed wrong to go this way.

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.

pretty unlikely, but holding onto them doesn't really cost us anything, so might as well...

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.

yeah, I would have though max(4032, 6) would have also worked, but given that it indeed costs nothing…

@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, one comment.

Comment threadlightning/src/util/sweep.rs Outdated
.. previously we just used the 4032 magic number, here we put it in a
`pub const` that is reusable elsewhere.
Previously, we would prune tracked descriptors once we see a spend hit
`ANTI_REORG_DELAY = 6` confirmations. However, this could lead to a
scenario where lingering `ChannelMonitor`s waiting to be archived would
still regenerate and replay `Event::SpendableOutput`s, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.
Here, we therefore keep the tracked descriptors around for longer, in
particular at least `ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038`
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.
@tnull
tnullforce-pushed the 2025-01-sweeper-improvements branch from 0aa813f to 84412ccCompareJanuary 27, 2025 08:56
Comment threadlightning/src/chain/channelmonitor.rs
@tnull
tnull merged commit 79267d3 into lightningdevkit:mainJan 27, 2025
@TheBlueMattTheBlueMatt mentioned this pull request Jan 28, 2025
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Backported in #3567

TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jan 29, 2025
v0.1.1 - Jan 28, 2025 - "Onchain Matters"
API Updates
===========
* A `ChannelManager::send_payment_with_route` was (re-)added, with semantics
similar to `ChannelManager::send_payment` (rather than like the pre-0.1
`send_payent_with_route`, lightningdevkit#3534).
* `RawBolt11Invoice::{to,from}_raw` were added (lightningdevkit#3549).
Bug Fixes
=========
* HTLCs which were forwarded where the inbound edge times out within the next
three blocks will have the inbound HTLC failed backwards irrespective of the
status of the outbound HTLC. This avoids the peer force-closing the channel
(and claiming the inbound edge HTLC on-chain) even if we have not yet managed
to claim the outbound edge on chain (lightningdevkit#3556).
* On restart, replay of `Event::SpendableOutput`s could have caused
`OutputSweeper` to generate double-spending transactions, making it unable to
claim any delayed claims. This was resolved by retaining old claims for more
than four weeks after they are claimed on-chain to detect replays (lightningdevkit#3559).
* Fixed the additional feerate we will pay each time we RBF on-chain claims to
match the Bitcoin Core policy (1 sat/vB) instead of 16 sats/vB (lightningdevkit#3457).
* Fixed a cased where a custom `Router` which returns an invalid `Route`,
provided to `ChannelManager`, can result in an outbound payment remaining
pending forever despite no HTLCs being pending (lightningdevkit#3531).
Security
========
0.1.1 fixes a denial-of-service vulnerability allowing channel counterparties to
cause force-closure of unrelated channels.
* If a malicious channel counterparty force-closes a channel, broadcasting a
revoked commitment transaction while the channel at closure time included
multiple non-dust forwarded outbound HTLCs with identical payment hashes and
amounts, failure to fail the HTLCs backwards could cause the channels on
which we recieved the corresponding inbound HTLCs to be force-closed. Note
that we'll receive, at a minimum, the malicious counterparty's reserve value
when they broadcast the stale commitment (lightningdevkit#3556). Thanks to Matt Morehouse for
reporting this issue.
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.

3 participants

@tnull@TheBlueMatt@arik-so
, '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

OutputSweeper: Delay pruning until monitors have likely been archived - #3559

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements
Jan 27, 2025
Merged

OutputSweeper: Delay pruning until monitors have likely been archived #3559
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, we would prune tracked descriptors once we see a spend hit
ANTI_REORG_DELAY = 6 confirmations. However, this could lead to a
scenario where lingering ChannelMonitors waiting to be archived would
still regenerate and replay Event::SpendableOutputs, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.

Here, we therefore keep the tracked descriptors around for longer, in
particular at least ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.

use core::ops::Deref;

/// The number of blocks we wait before we prune the tracked spendable outputs.
pub const PRUNE_DELAY_BLOCKS: u32 = ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY;

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.

how likely is it that 4032 blocks was insufficient, but 4038 was?

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.

Well, the idea here is that we start pruning after the monitors have been archived. For that to work we need to wait at least 4032 blocks, but of course we're not super sure how soon after the threshold has been met user will actually call archive_fully_resolved_monitors, so I added the reorg delay on top. Could be debatable one way or another.

FWIW, I alternatively considered introducing a new MonitorArchivalComplete event type, but given that we on purpose keep the archival a best-effort thing it seemed wrong to go this way.

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.

pretty unlikely, but holding onto them doesn't really cost us anything, so might as well...

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.

yeah, I would have though max(4032, 6) would have also worked, but given that it indeed costs nothing…

@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, one comment.

Comment threadlightning/src/util/sweep.rs Outdated
.. previously we just used the 4032 magic number, here we put it in a
`pub const` that is reusable elsewhere.
Previously, we would prune tracked descriptors once we see a spend hit
`ANTI_REORG_DELAY = 6` confirmations. However, this could lead to a
scenario where lingering `ChannelMonitor`s waiting to be archived would
still regenerate and replay `Event::SpendableOutput`s, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.
Here, we therefore keep the tracked descriptors around for longer, in
particular at least `ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038`
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.
@tnull
tnullforce-pushed the 2025-01-sweeper-improvements branch from 0aa813f to 84412ccCompareJanuary 27, 2025 08:56
Comment threadlightning/src/chain/channelmonitor.rs
@tnull
tnull merged commit 79267d3 into lightningdevkit:mainJan 27, 2025
@TheBlueMattTheBlueMatt mentioned this pull request Jan 28, 2025
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Backported in #3567

TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jan 29, 2025
v0.1.1 - Jan 28, 2025 - "Onchain Matters"
API Updates
===========
* A `ChannelManager::send_payment_with_route` was (re-)added, with semantics
similar to `ChannelManager::send_payment` (rather than like the pre-0.1
`send_payent_with_route`, lightningdevkit#3534).
* `RawBolt11Invoice::{to,from}_raw` were added (lightningdevkit#3549).
Bug Fixes
=========
* HTLCs which were forwarded where the inbound edge times out within the next
three blocks will have the inbound HTLC failed backwards irrespective of the
status of the outbound HTLC. This avoids the peer force-closing the channel
(and claiming the inbound edge HTLC on-chain) even if we have not yet managed
to claim the outbound edge on chain (lightningdevkit#3556).
* On restart, replay of `Event::SpendableOutput`s could have caused
`OutputSweeper` to generate double-spending transactions, making it unable to
claim any delayed claims. This was resolved by retaining old claims for more
than four weeks after they are claimed on-chain to detect replays (lightningdevkit#3559).
* Fixed the additional feerate we will pay each time we RBF on-chain claims to
match the Bitcoin Core policy (1 sat/vB) instead of 16 sats/vB (lightningdevkit#3457).
* Fixed a cased where a custom `Router` which returns an invalid `Route`,
provided to `ChannelManager`, can result in an outbound payment remaining
pending forever despite no HTLCs being pending (lightningdevkit#3531).
Security
========
0.1.1 fixes a denial-of-service vulnerability allowing channel counterparties to
cause force-closure of unrelated channels.
* If a malicious channel counterparty force-closes a channel, broadcasting a
revoked commitment transaction while the channel at closure time included
multiple non-dust forwarded outbound HTLCs with identical payment hashes and
amounts, failure to fail the HTLCs backwards could cause the channels on
which we recieved the corresponding inbound HTLCs to be force-closed. Note
that we'll receive, at a minimum, the malicious counterparty's reserve value
when they broadcast the stale commitment (lightningdevkit#3556). Thanks to Matt Morehouse for
reporting this issue.
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.

3 participants

@tnull@TheBlueMatt@arik-so
, '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

OutputSweeper: Delay pruning until monitors have likely been archived - #3559

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements
Jan 27, 2025
Merged

OutputSweeper: Delay pruning until monitors have likely been archived #3559
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-01-sweeper-improvements

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, we would prune tracked descriptors once we see a spend hit
ANTI_REORG_DELAY = 6 confirmations. However, this could lead to a
scenario where lingering ChannelMonitors waiting to be archived would
still regenerate and replay Event::SpendableOutputs, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.

Here, we therefore keep the tracked descriptors around for longer, in
particular at least ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.

use core::ops::Deref;

/// The number of blocks we wait before we prune the tracked spendable outputs.
pub const PRUNE_DELAY_BLOCKS: u32 = ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY;

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.

how likely is it that 4032 blocks was insufficient, but 4038 was?

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.

Well, the idea here is that we start pruning after the monitors have been archived. For that to work we need to wait at least 4032 blocks, but of course we're not super sure how soon after the threshold has been met user will actually call archive_fully_resolved_monitors, so I added the reorg delay on top. Could be debatable one way or another.

FWIW, I alternatively considered introducing a new MonitorArchivalComplete event type, but given that we on purpose keep the archival a best-effort thing it seemed wrong to go this way.

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.

pretty unlikely, but holding onto them doesn't really cost us anything, so might as well...

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.

yeah, I would have though max(4032, 6) would have also worked, but given that it indeed costs nothing…

@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, one comment.

Comment threadlightning/src/util/sweep.rs Outdated
.. previously we just used the 4032 magic number, here we put it in a
`pub const` that is reusable elsewhere.
Previously, we would prune tracked descriptors once we see a spend hit
`ANTI_REORG_DELAY = 6` confirmations. However, this could lead to a
scenario where lingering `ChannelMonitor`s waiting to be archived would
still regenerate and replay `Event::SpendableOutput`s, i.e., we would
re-add the same (now unspendable due to be actually being already spent)
outputs again after having intially pruned them.
Here, we therefore keep the tracked descriptors around for longer, in
particular at least `ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY = 4038`
confirmations, at which point we assume the lingering monitors to have
been likely archived, and it's 'safe' for us to also forget about the
descriptors.
@tnull
tnullforce-pushed the 2025-01-sweeper-improvements branch from 0aa813f to 84412ccCompareJanuary 27, 2025 08:56
Comment threadlightning/src/chain/channelmonitor.rs
@tnull
tnull merged commit 79267d3 into lightningdevkit:mainJan 27, 2025
@TheBlueMattTheBlueMatt mentioned this pull request Jan 28, 2025
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Backported in #3567

TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Jan 29, 2025
v0.1.1 - Jan 28, 2025 - "Onchain Matters"
API Updates
===========
* A `ChannelManager::send_payment_with_route` was (re-)added, with semantics
similar to `ChannelManager::send_payment` (rather than like the pre-0.1
`send_payent_with_route`, lightningdevkit#3534).
* `RawBolt11Invoice::{to,from}_raw` were added (lightningdevkit#3549).
Bug Fixes
=========
* HTLCs which were forwarded where the inbound edge times out within the next
three blocks will have the inbound HTLC failed backwards irrespective of the
status of the outbound HTLC. This avoids the peer force-closing the channel
(and claiming the inbound edge HTLC on-chain) even if we have not yet managed
to claim the outbound edge on chain (lightningdevkit#3556).
* On restart, replay of `Event::SpendableOutput`s could have caused
`OutputSweeper` to generate double-spending transactions, making it unable to
claim any delayed claims. This was resolved by retaining old claims for more
than four weeks after they are claimed on-chain to detect replays (lightningdevkit#3559).
* Fixed the additional feerate we will pay each time we RBF on-chain claims to
match the Bitcoin Core policy (1 sat/vB) instead of 16 sats/vB (lightningdevkit#3457).
* Fixed a cased where a custom `Router` which returns an invalid `Route`,
provided to `ChannelManager`, can result in an outbound payment remaining
pending forever despite no HTLCs being pending (lightningdevkit#3531).
Security
========
0.1.1 fixes a denial-of-service vulnerability allowing channel counterparties to
cause force-closure of unrelated channels.
* If a malicious channel counterparty force-closes a channel, broadcasting a
revoked commitment transaction while the channel at closure time included
multiple non-dust forwarded outbound HTLCs with identical payment hashes and
amounts, failure to fail the HTLCs backwards could cause the channels on
which we recieved the corresponding inbound HTLCs to be force-closed. Note
that we'll receive, at a minimum, the malicious counterparty's reserve value
when they broadcast the stale commitment (lightningdevkit#3556). Thanks to Matt Morehouse for
reporting this issue.
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.

3 participants

@tnull@TheBlueMatt@arik-so