Skip to content

Introduce new BumpTransactionEvent variant HTLCResolution - #1825

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event
Dec 7, 2022
Merged

Introduce new BumpTransactionEvent variant HTLCResolution#1825
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

This PR continues the work laid out in #1689 and introduces a new BumpTransactionEvent variant: HTLCResolution. Similarly, this event is to be consumed by users and indicates that a channel's commitment transaction with unresolved HTLCs has confirmed onchain, requiring HTLC transactions to be broadcast with additional inputs and/or outputs attached to satisfy feerate demands at the time of broadcast.

Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 20fff12 to 51418abCompareNovember 4, 2022 19:37
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 51418ab to 9fbd465CompareNovember 16, 2022 00:47
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 9fbd465 to 5a257d0CompareNovember 18, 2022 01:01
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 5a257d0 to 88c0356CompareNovember 18, 2022 01:36
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Rebased on latest to address an import conflict.

@codecov-commenter

codecov-commenter commented Nov 18, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.57% // Head: 90.50% // Decreases project coverage by -0.07%⚠️

Coverage data is based on head (6a99960) compared to base (36e6023).
Patch coverage: 86.98% of modified lines in pull request are covered.

❗ Current head 6a99960 differs from pull request most recent head ec1f334. Consider uploading reports for the commit ec1f334 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1825 +/- ##
==========================================
- Coverage 90.57% 90.50% -0.08% 
==========================================
Files 91 91 Lines 48556 48731 +175 Branches 48556 48731 +175 ==========================================
+ Hits 43982 44102 +120 - Misses 4574 4629 +55 
Impacted FilesCoverage Δ
lightning/src/chain/keysinterface.rs83.14% <ø> (ø)
lightning/src/util/enforcing_trait_impls.rs82.92% <ø> (ø)
lightning/src/util/events.rs25.45% <ø> (ø)
lightning/src/chain/onchaintx.rs92.73% <66.66%> (-2.62%)⬇️
lightning/src/chain/package.rs91.76% <68.57%> (-1.09%)⬇️
lightning/src/ln/chan_utils.rs93.61% <94.59%> (-0.02%)⬇️
lightning/src/chain/channelmonitor.rs91.04% <98.63%> (+0.11%)⬆️
lightning/src/ln/onion_utils.rs93.56% <0.00%> (-1.37%)⬇️
lightning/src/ln/functional_tests.rs96.65% <0.00%> (-0.35%)⬇️
... and 5 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 88c0356 to c8be80fCompareNovember 22, 2022 01:27
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Pushed a new update addressing some remaining comments and a missing change noted by @ariard in #1860.

arik-so
arik-so previously approved these changes Nov 22, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from dd94ec2 to a6eff8aCompareNovember 22, 2022 20:34
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Squashed and rebased on latest to resolve a conflict.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from a6eff8a to f62e96fCompareNovember 23, 2022 19:00
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

> git diff -U1 a6eff8a f62e96f

diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index b70121ba..cb31783d 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -2651,13 +2651,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
- macro_rules! ignore_error {
- ( $thing : expr ) => {
- match $thing {
- Ok(a) => a,
- Err(_) => return (Vec::new(), None)
- }
- };
- }
-
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); };
- let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
+ let per_commitment_key = match SecretKey::from_slice(&secret) {
+ Ok(key) => key,
+ Err(_) => return (Vec::new(), None)
+ };
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
@@ -2667,6 +2661,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let mut outputs_to_watch = None;
+ // Previously, we would only claim HTLCs from revoked HTLC transactions if they had 1 input
+ // with a witness of 5 elements and 1 output. This wasn't enough for anchor outputs, as the
+ // counterparty can now aggregate multiple HTLCs into a single transaction thanks to
+ // `SIGHASH_SINGLE` remote signatures, leading us to not claim any HTLCs upon seeing a
+ // confirmed revoked HTLC transaction (for more details, see
+ // https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html).
+ //
+ // We make sure we're not vulnerable to this case by checking all inputs of the transaction,
+ // and claim those which spend the commitment transaction, have a witness of 5 elements, and
+ // have a corresponding output at the same index within the transaction.
for (idx, input) in tx.input.iter().enumerate() {
- // HTLC transactions always spend an output on the commitment transaction with a witness
- // of 5 elements. The HTLC input will always have a corresponding output at the same
- // index within the transaction.
if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() {

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from f62e96f to e5a61f2CompareNovember 28, 2022 16:38
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

@TheBlueMatt I addressed your remaining comments. Will open a new PR to address the serialization changes discussed in #1825 (comment).

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

I left a handful of comments, ranging from mildly annoyed at what we have to trivial nits, but only one (the question about the 0th output) actually needs a response for this to land, I think.

Comment threadlightning/src/chain/keysinterface.rs Outdated
/// Note that this should only be used to sign HTLC transactions from channels supporting anchor
/// outputs after all additional inputs/outputs have been added to the transaction.
fn sign_holder_htlc_transaction(
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,

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.

For both this and sign_holder_anchor_input I'm really not a fan of passing in a full tx-to-sign. I'm not sure what to do about it, though, we don't really want to constrain the tx contents really. Luckily segwit commits to the amount, but I could see someone forgetting to check the fee, for example. I guess we're really relying on the other input being signed as "the" security control here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm not following -- we need to pass in the full transaction since it includes the additional fee inputs/outputs and our local signature is SIGHASH_ALL.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess I was thinking more that we could pass the set of non-HTLC inputs, the output script and fee and then have the siger recreate the transaction. This avoids having to calculate the fee, check the locktime, etc. In general the principle for the signing interfaces should be (but isn't always) that we pass the user the things they need to construct the object to sign (and a utility method to do so) and make them do the construction. That way everything they need to validate is shoved in their face and they can't avoid looking at it (and hopefully remember to check it). Given our other HTLC-sign transactions already have this mistake it's fine for now, but in the future we should refactor all those methods to do the HTLC tx construction on the signer's end.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/chan_utils.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from b9a485f to f26f235CompareDecember 1, 2022 00:32
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Updated docs and rebased on top of #1887 since it gets rid of some Results on HTLCDescriptor methods.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See last comment.

ClaimEvent::BumpHTLC {
target_feerate_sat_per_1000_weight, htlcs,
} => {
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Here I think we can debug_assert!(seff.onchain_tx_handler.opt_anchors()).

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.

We can probably replace most #[cfg(anchors)] with this assertion so will leave it for a follow-up.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_anchor_input`]: crate::chain::keysinterface::BaseSign::sign_holder_anchor_input
/// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness
ChannelClose {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note if you would like to add a commit to recall this enum field FundingUnilateralResolution. A ChannelClose in Lightning parlance is really ambiguous as it designates also the cooperative case. This cooperative case itself could be a target for fee-bumping in the future (e.g splicing support). A small bikeshedding as we're around.

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.

If we want to rename it, let's do it in a followup. This PR has been delayed enough and isn't even touching this code.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_htlc_transaction`]: crate::chain::keysinterface::BaseSign::sign_holder_htlc_transaction
/// [`HTLCDescriptor::tx_input_witness`]: HTLCDescriptor::tx_input_witness
HTLCResolution {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There is no current indication on the level of external fee-bumping reserves a user is supposed to keep. For HTLC resolution, we would have to encompass the worst-case of holder's max_accepted_htlcs + counterparty's max_accepted_htlcs * 706 WU (for post-anchor HTLC-Success) * worst-historical mempool feerate. And this is only per-channel. Anyway, I don't know where we would like to start document those requirements here, in #1860 or even after ?

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.

Let's do it later.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase, think we can land this today or tomorrow?

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from 148c04c to 575b2afCompareDecember 6, 2022 20:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like CI is sad? Feel free to squash the fixups.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 575b2af to 12e0310CompareDecember 6, 2022 22:16
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

There was a stray else causing the failures. Test suite passes locally now so we should be good.

@ariard

Copy link
Copy Markdown

On my side, I'll try to review back tomorrow. Checking the full transaction processing flow from ChainMonitor.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 12e0310 to 6a99960CompareDecember 7, 2022 00:14
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@TheBlueMattTheBlueMatt mentioned this pull request Dec 7, 2022
Previously, this method assumed that all HTLC transactions have 1 input
and 1 output, with the sole input having a witness of 5 elements. This
will no longer be the case for HTLC transactions on channels with
anchors outputs since additional inputs and outputs can be attached to
them to allow fee bumping.
This is only a name change, there is no change in behavior.
Now that our txids will no longer be stable for package claims that
require external funds to be allocated, we transition to a 32-byte array
identifier to remain compatible with them.
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 6a99960 to ec1f334CompareDecember 7, 2022 00:48

@arik-soarik-so left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I really like the comments explaining the potential vulnerability that might arise with anchor HTLC transactions.

Comment threadlightning/src/chain/onchaintx.rs
@TheBlueMatt
TheBlueMatt merged commit eea56e9 into lightningdevkit:mainDec 7, 2022
@tnulltnull mentioned this pull request Dec 7, 2022

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See second-last comment.

Comment threadlightning/src/chain/channelmonitor.rs
// Since there may be multiple HTLCs (all from the same commitment) being
// claimed by the counterparty within the same transaction, and
// `check_spend_counterparty_htlc` already checks for all of them, we can
// safely break from our loop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is assumption is broken, and per se our code will still miss the detection and claim of revoked second-stage HTLC transactions.

With SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, the second-stage HTLC transaction claims can be aggregated across commitment transactions, as there is no commitment to the spend txid in the shared transaction fields (e.g the parent txid could be in the nLocktime field). Our logic iterate on all the inputs, and at the first one with a confirmed commitment txid in counterparty_commitment_txn_on_chain, we'll call in check_spend_counterparty_htlc() L3028. There, we iterate against all inputs and if the input outpoint txid is different from the passed commitment_txid, we skip it further processing. Once we're out of check_spend_counterparty_htlc(), we end up here and break from the for control flow, am I correct ? At the very least I don't think we have a test coverage for this case.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

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.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

Note, here we're checking the second-stage HTLC transactions, and we're filtering out the input/output pair to be claimed based on the spent commitment transaction id. Post-anchor output, second-stage HTLC transactions are signed with SIGHASH_SINGLE enabling aggregation of HTLC claims from the same commitment transaction, but there is no constraint restraining the aggregation of HTLC claims across commitment transactions. Even if for one ChannelMonitor there is a single commitment_txid that can confirms.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

This is a correct, the claim of HTLCs is a per-ChannelMonitor responsibility, and I don't think there is a way open by malleability where the aggregated HTLC claims crafted by our counterparty could blind our parsing logic. Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

Since the ChannelMonitor is already on a per-channel basis, the comment really means "all from the same commitment for this channel". I agree it could definitely be re-worded better though.

Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Doesn't seem like there's much we can do other than document it? Even then, I would imagine all LDK users want to use our ChainMonitor as is. Rather than re-implementing it for their own needs, they just need to implement the trait dependencies on ChainMonitor.

@ariardariardDec 8, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Doesn't seem like there's much we can do other than document it?

More documentation of our assumptions is always better. I think with the extended flexibility offered by our interfaces we have far less visibility on how it could be re-implemented by users rather than a monolithic Lightning node directly consuming from standard Core interfaces. This is raising the bar for funds safety, as implicit assumptions of our low-level code could be silently broken. Beyond, we might have in the future to re-implement a ChainMonitor for "monitor-replica" support, a bit more sophisticated than the standard one.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulino deleted the anchors-bump-htlc-resolution-event branch December 7, 2022 18:33
TheBlueMatt added a commit that referenced this pull request Dec 12, 2022
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.

5 participants

@wpaulino@codecov-commenter@TheBlueMatt@ariard@arik-so
, '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" + '
Introduce new BumpTransactionEvent variant HTLCResolution by wpaulino · Pull Request #1825 · lightningdevkit/rust-lightning · GitHub
Skip to content

Introduce new BumpTransactionEvent variant HTLCResolution - #1825

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event
Dec 7, 2022
Merged

Introduce new BumpTransactionEvent variant HTLCResolution#1825
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

This PR continues the work laid out in #1689 and introduces a new BumpTransactionEvent variant: HTLCResolution. Similarly, this event is to be consumed by users and indicates that a channel's commitment transaction with unresolved HTLCs has confirmed onchain, requiring HTLC transactions to be broadcast with additional inputs and/or outputs attached to satisfy feerate demands at the time of broadcast.

Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 20fff12 to 51418abCompareNovember 4, 2022 19:37
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 51418ab to 9fbd465CompareNovember 16, 2022 00:47
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 9fbd465 to 5a257d0CompareNovember 18, 2022 01:01
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 5a257d0 to 88c0356CompareNovember 18, 2022 01:36
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Rebased on latest to address an import conflict.

@codecov-commenter

codecov-commenter commented Nov 18, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.57% // Head: 90.50% // Decreases project coverage by -0.07%⚠️

Coverage data is based on head (6a99960) compared to base (36e6023).
Patch coverage: 86.98% of modified lines in pull request are covered.

❗ Current head 6a99960 differs from pull request most recent head ec1f334. Consider uploading reports for the commit ec1f334 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1825 +/- ##
==========================================
- Coverage 90.57% 90.50% -0.08% 
==========================================
Files 91 91 Lines 48556 48731 +175 Branches 48556 48731 +175 ==========================================
+ Hits 43982 44102 +120 - Misses 4574 4629 +55 
Impacted FilesCoverage Δ
lightning/src/chain/keysinterface.rs83.14% <ø> (ø)
lightning/src/util/enforcing_trait_impls.rs82.92% <ø> (ø)
lightning/src/util/events.rs25.45% <ø> (ø)
lightning/src/chain/onchaintx.rs92.73% <66.66%> (-2.62%)⬇️
lightning/src/chain/package.rs91.76% <68.57%> (-1.09%)⬇️
lightning/src/ln/chan_utils.rs93.61% <94.59%> (-0.02%)⬇️
lightning/src/chain/channelmonitor.rs91.04% <98.63%> (+0.11%)⬆️
lightning/src/ln/onion_utils.rs93.56% <0.00%> (-1.37%)⬇️
lightning/src/ln/functional_tests.rs96.65% <0.00%> (-0.35%)⬇️
... and 5 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 88c0356 to c8be80fCompareNovember 22, 2022 01:27
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Pushed a new update addressing some remaining comments and a missing change noted by @ariard in #1860.

arik-so
arik-so previously approved these changes Nov 22, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from dd94ec2 to a6eff8aCompareNovember 22, 2022 20:34
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Squashed and rebased on latest to resolve a conflict.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from a6eff8a to f62e96fCompareNovember 23, 2022 19:00
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

> git diff -U1 a6eff8a f62e96f

diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index b70121ba..cb31783d 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -2651,13 +2651,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
- macro_rules! ignore_error {
- ( $thing : expr ) => {
- match $thing {
- Ok(a) => a,
- Err(_) => return (Vec::new(), None)
- }
- };
- }
-
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); };
- let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
+ let per_commitment_key = match SecretKey::from_slice(&secret) {
+ Ok(key) => key,
+ Err(_) => return (Vec::new(), None)
+ };
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
@@ -2667,6 +2661,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let mut outputs_to_watch = None;
+ // Previously, we would only claim HTLCs from revoked HTLC transactions if they had 1 input
+ // with a witness of 5 elements and 1 output. This wasn't enough for anchor outputs, as the
+ // counterparty can now aggregate multiple HTLCs into a single transaction thanks to
+ // `SIGHASH_SINGLE` remote signatures, leading us to not claim any HTLCs upon seeing a
+ // confirmed revoked HTLC transaction (for more details, see
+ // https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html).
+ //
+ // We make sure we're not vulnerable to this case by checking all inputs of the transaction,
+ // and claim those which spend the commitment transaction, have a witness of 5 elements, and
+ // have a corresponding output at the same index within the transaction.
for (idx, input) in tx.input.iter().enumerate() {
- // HTLC transactions always spend an output on the commitment transaction with a witness
- // of 5 elements. The HTLC input will always have a corresponding output at the same
- // index within the transaction.
if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() {

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from f62e96f to e5a61f2CompareNovember 28, 2022 16:38
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

@TheBlueMatt I addressed your remaining comments. Will open a new PR to address the serialization changes discussed in #1825 (comment).

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

I left a handful of comments, ranging from mildly annoyed at what we have to trivial nits, but only one (the question about the 0th output) actually needs a response for this to land, I think.

Comment threadlightning/src/chain/keysinterface.rs Outdated
/// Note that this should only be used to sign HTLC transactions from channels supporting anchor
/// outputs after all additional inputs/outputs have been added to the transaction.
fn sign_holder_htlc_transaction(
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,

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.

For both this and sign_holder_anchor_input I'm really not a fan of passing in a full tx-to-sign. I'm not sure what to do about it, though, we don't really want to constrain the tx contents really. Luckily segwit commits to the amount, but I could see someone forgetting to check the fee, for example. I guess we're really relying on the other input being signed as "the" security control here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm not following -- we need to pass in the full transaction since it includes the additional fee inputs/outputs and our local signature is SIGHASH_ALL.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess I was thinking more that we could pass the set of non-HTLC inputs, the output script and fee and then have the siger recreate the transaction. This avoids having to calculate the fee, check the locktime, etc. In general the principle for the signing interfaces should be (but isn't always) that we pass the user the things they need to construct the object to sign (and a utility method to do so) and make them do the construction. That way everything they need to validate is shoved in their face and they can't avoid looking at it (and hopefully remember to check it). Given our other HTLC-sign transactions already have this mistake it's fine for now, but in the future we should refactor all those methods to do the HTLC tx construction on the signer's end.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/chan_utils.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from b9a485f to f26f235CompareDecember 1, 2022 00:32
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Updated docs and rebased on top of #1887 since it gets rid of some Results on HTLCDescriptor methods.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See last comment.

ClaimEvent::BumpHTLC {
target_feerate_sat_per_1000_weight, htlcs,
} => {
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Here I think we can debug_assert!(seff.onchain_tx_handler.opt_anchors()).

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.

We can probably replace most #[cfg(anchors)] with this assertion so will leave it for a follow-up.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_anchor_input`]: crate::chain::keysinterface::BaseSign::sign_holder_anchor_input
/// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness
ChannelClose {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note if you would like to add a commit to recall this enum field FundingUnilateralResolution. A ChannelClose in Lightning parlance is really ambiguous as it designates also the cooperative case. This cooperative case itself could be a target for fee-bumping in the future (e.g splicing support). A small bikeshedding as we're around.

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.

If we want to rename it, let's do it in a followup. This PR has been delayed enough and isn't even touching this code.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_htlc_transaction`]: crate::chain::keysinterface::BaseSign::sign_holder_htlc_transaction
/// [`HTLCDescriptor::tx_input_witness`]: HTLCDescriptor::tx_input_witness
HTLCResolution {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There is no current indication on the level of external fee-bumping reserves a user is supposed to keep. For HTLC resolution, we would have to encompass the worst-case of holder's max_accepted_htlcs + counterparty's max_accepted_htlcs * 706 WU (for post-anchor HTLC-Success) * worst-historical mempool feerate. And this is only per-channel. Anyway, I don't know where we would like to start document those requirements here, in #1860 or even after ?

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.

Let's do it later.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase, think we can land this today or tomorrow?

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from 148c04c to 575b2afCompareDecember 6, 2022 20:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like CI is sad? Feel free to squash the fixups.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 575b2af to 12e0310CompareDecember 6, 2022 22:16
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

There was a stray else causing the failures. Test suite passes locally now so we should be good.

@ariard

Copy link
Copy Markdown

On my side, I'll try to review back tomorrow. Checking the full transaction processing flow from ChainMonitor.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 12e0310 to 6a99960CompareDecember 7, 2022 00:14
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@TheBlueMattTheBlueMatt mentioned this pull request Dec 7, 2022
Previously, this method assumed that all HTLC transactions have 1 input
and 1 output, with the sole input having a witness of 5 elements. This
will no longer be the case for HTLC transactions on channels with
anchors outputs since additional inputs and outputs can be attached to
them to allow fee bumping.
This is only a name change, there is no change in behavior.
Now that our txids will no longer be stable for package claims that
require external funds to be allocated, we transition to a 32-byte array
identifier to remain compatible with them.
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 6a99960 to ec1f334CompareDecember 7, 2022 00:48

@arik-soarik-so left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I really like the comments explaining the potential vulnerability that might arise with anchor HTLC transactions.

Comment threadlightning/src/chain/onchaintx.rs
@TheBlueMatt
TheBlueMatt merged commit eea56e9 into lightningdevkit:mainDec 7, 2022
@tnulltnull mentioned this pull request Dec 7, 2022

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See second-last comment.

Comment threadlightning/src/chain/channelmonitor.rs
// Since there may be multiple HTLCs (all from the same commitment) being
// claimed by the counterparty within the same transaction, and
// `check_spend_counterparty_htlc` already checks for all of them, we can
// safely break from our loop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is assumption is broken, and per se our code will still miss the detection and claim of revoked second-stage HTLC transactions.

With SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, the second-stage HTLC transaction claims can be aggregated across commitment transactions, as there is no commitment to the spend txid in the shared transaction fields (e.g the parent txid could be in the nLocktime field). Our logic iterate on all the inputs, and at the first one with a confirmed commitment txid in counterparty_commitment_txn_on_chain, we'll call in check_spend_counterparty_htlc() L3028. There, we iterate against all inputs and if the input outpoint txid is different from the passed commitment_txid, we skip it further processing. Once we're out of check_spend_counterparty_htlc(), we end up here and break from the for control flow, am I correct ? At the very least I don't think we have a test coverage for this case.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

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.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

Note, here we're checking the second-stage HTLC transactions, and we're filtering out the input/output pair to be claimed based on the spent commitment transaction id. Post-anchor output, second-stage HTLC transactions are signed with SIGHASH_SINGLE enabling aggregation of HTLC claims from the same commitment transaction, but there is no constraint restraining the aggregation of HTLC claims across commitment transactions. Even if for one ChannelMonitor there is a single commitment_txid that can confirms.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

This is a correct, the claim of HTLCs is a per-ChannelMonitor responsibility, and I don't think there is a way open by malleability where the aggregated HTLC claims crafted by our counterparty could blind our parsing logic. Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

Since the ChannelMonitor is already on a per-channel basis, the comment really means "all from the same commitment for this channel". I agree it could definitely be re-worded better though.

Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Doesn't seem like there's much we can do other than document it? Even then, I would imagine all LDK users want to use our ChainMonitor as is. Rather than re-implementing it for their own needs, they just need to implement the trait dependencies on ChainMonitor.

@ariardariardDec 8, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Doesn't seem like there's much we can do other than document it?

More documentation of our assumptions is always better. I think with the extended flexibility offered by our interfaces we have far less visibility on how it could be re-implemented by users rather than a monolithic Lightning node directly consuming from standard Core interfaces. This is raising the bar for funds safety, as implicit assumptions of our low-level code could be silently broken. Beyond, we might have in the future to re-implement a ChainMonitor for "monitor-replica" support, a bit more sophisticated than the standard one.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulino deleted the anchors-bump-htlc-resolution-event branch December 7, 2022 18:33
TheBlueMatt added a commit that referenced this pull request Dec 12, 2022
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.

5 participants

@wpaulino@codecov-commenter@TheBlueMatt@ariard@arik-so
, '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('^' + ".*" + ' Introduce new BumpTransactionEvent variant HTLCResolution by wpaulino · Pull Request #1825 · lightningdevkit/rust-lightning · GitHub
Skip to content

Introduce new BumpTransactionEvent variant HTLCResolution - #1825

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event
Dec 7, 2022
Merged

Introduce new BumpTransactionEvent variant HTLCResolution#1825
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

This PR continues the work laid out in #1689 and introduces a new BumpTransactionEvent variant: HTLCResolution. Similarly, this event is to be consumed by users and indicates that a channel's commitment transaction with unresolved HTLCs has confirmed onchain, requiring HTLC transactions to be broadcast with additional inputs and/or outputs attached to satisfy feerate demands at the time of broadcast.

Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 20fff12 to 51418abCompareNovember 4, 2022 19:37
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 51418ab to 9fbd465CompareNovember 16, 2022 00:47
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 9fbd465 to 5a257d0CompareNovember 18, 2022 01:01
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 5a257d0 to 88c0356CompareNovember 18, 2022 01:36
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Rebased on latest to address an import conflict.

@codecov-commenter

codecov-commenter commented Nov 18, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.57% // Head: 90.50% // Decreases project coverage by -0.07%⚠️

Coverage data is based on head (6a99960) compared to base (36e6023).
Patch coverage: 86.98% of modified lines in pull request are covered.

❗ Current head 6a99960 differs from pull request most recent head ec1f334. Consider uploading reports for the commit ec1f334 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1825 +/- ##
==========================================
- Coverage 90.57% 90.50% -0.08% 
==========================================
Files 91 91 Lines 48556 48731 +175 Branches 48556 48731 +175 ==========================================
+ Hits 43982 44102 +120 - Misses 4574 4629 +55 
Impacted FilesCoverage Δ
lightning/src/chain/keysinterface.rs83.14% <ø> (ø)
lightning/src/util/enforcing_trait_impls.rs82.92% <ø> (ø)
lightning/src/util/events.rs25.45% <ø> (ø)
lightning/src/chain/onchaintx.rs92.73% <66.66%> (-2.62%)⬇️
lightning/src/chain/package.rs91.76% <68.57%> (-1.09%)⬇️
lightning/src/ln/chan_utils.rs93.61% <94.59%> (-0.02%)⬇️
lightning/src/chain/channelmonitor.rs91.04% <98.63%> (+0.11%)⬆️
lightning/src/ln/onion_utils.rs93.56% <0.00%> (-1.37%)⬇️
lightning/src/ln/functional_tests.rs96.65% <0.00%> (-0.35%)⬇️
... and 5 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 88c0356 to c8be80fCompareNovember 22, 2022 01:27
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Pushed a new update addressing some remaining comments and a missing change noted by @ariard in #1860.

arik-so
arik-so previously approved these changes Nov 22, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from dd94ec2 to a6eff8aCompareNovember 22, 2022 20:34
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Squashed and rebased on latest to resolve a conflict.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from a6eff8a to f62e96fCompareNovember 23, 2022 19:00
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

> git diff -U1 a6eff8a f62e96f

diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index b70121ba..cb31783d 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -2651,13 +2651,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
- macro_rules! ignore_error {
- ( $thing : expr ) => {
- match $thing {
- Ok(a) => a,
- Err(_) => return (Vec::new(), None)
- }
- };
- }
-
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); };
- let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
+ let per_commitment_key = match SecretKey::from_slice(&secret) {
+ Ok(key) => key,
+ Err(_) => return (Vec::new(), None)
+ };
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
@@ -2667,6 +2661,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let mut outputs_to_watch = None;
+ // Previously, we would only claim HTLCs from revoked HTLC transactions if they had 1 input
+ // with a witness of 5 elements and 1 output. This wasn't enough for anchor outputs, as the
+ // counterparty can now aggregate multiple HTLCs into a single transaction thanks to
+ // `SIGHASH_SINGLE` remote signatures, leading us to not claim any HTLCs upon seeing a
+ // confirmed revoked HTLC transaction (for more details, see
+ // https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html).
+ //
+ // We make sure we're not vulnerable to this case by checking all inputs of the transaction,
+ // and claim those which spend the commitment transaction, have a witness of 5 elements, and
+ // have a corresponding output at the same index within the transaction.
for (idx, input) in tx.input.iter().enumerate() {
- // HTLC transactions always spend an output on the commitment transaction with a witness
- // of 5 elements. The HTLC input will always have a corresponding output at the same
- // index within the transaction.
if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() {

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from f62e96f to e5a61f2CompareNovember 28, 2022 16:38
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

@TheBlueMatt I addressed your remaining comments. Will open a new PR to address the serialization changes discussed in #1825 (comment).

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

I left a handful of comments, ranging from mildly annoyed at what we have to trivial nits, but only one (the question about the 0th output) actually needs a response for this to land, I think.

Comment threadlightning/src/chain/keysinterface.rs Outdated
/// Note that this should only be used to sign HTLC transactions from channels supporting anchor
/// outputs after all additional inputs/outputs have been added to the transaction.
fn sign_holder_htlc_transaction(
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,

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.

For both this and sign_holder_anchor_input I'm really not a fan of passing in a full tx-to-sign. I'm not sure what to do about it, though, we don't really want to constrain the tx contents really. Luckily segwit commits to the amount, but I could see someone forgetting to check the fee, for example. I guess we're really relying on the other input being signed as "the" security control here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm not following -- we need to pass in the full transaction since it includes the additional fee inputs/outputs and our local signature is SIGHASH_ALL.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess I was thinking more that we could pass the set of non-HTLC inputs, the output script and fee and then have the siger recreate the transaction. This avoids having to calculate the fee, check the locktime, etc. In general the principle for the signing interfaces should be (but isn't always) that we pass the user the things they need to construct the object to sign (and a utility method to do so) and make them do the construction. That way everything they need to validate is shoved in their face and they can't avoid looking at it (and hopefully remember to check it). Given our other HTLC-sign transactions already have this mistake it's fine for now, but in the future we should refactor all those methods to do the HTLC tx construction on the signer's end.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/chan_utils.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from b9a485f to f26f235CompareDecember 1, 2022 00:32
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Updated docs and rebased on top of #1887 since it gets rid of some Results on HTLCDescriptor methods.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See last comment.

ClaimEvent::BumpHTLC {
target_feerate_sat_per_1000_weight, htlcs,
} => {
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Here I think we can debug_assert!(seff.onchain_tx_handler.opt_anchors()).

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.

We can probably replace most #[cfg(anchors)] with this assertion so will leave it for a follow-up.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_anchor_input`]: crate::chain::keysinterface::BaseSign::sign_holder_anchor_input
/// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness
ChannelClose {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note if you would like to add a commit to recall this enum field FundingUnilateralResolution. A ChannelClose in Lightning parlance is really ambiguous as it designates also the cooperative case. This cooperative case itself could be a target for fee-bumping in the future (e.g splicing support). A small bikeshedding as we're around.

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.

If we want to rename it, let's do it in a followup. This PR has been delayed enough and isn't even touching this code.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_htlc_transaction`]: crate::chain::keysinterface::BaseSign::sign_holder_htlc_transaction
/// [`HTLCDescriptor::tx_input_witness`]: HTLCDescriptor::tx_input_witness
HTLCResolution {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There is no current indication on the level of external fee-bumping reserves a user is supposed to keep. For HTLC resolution, we would have to encompass the worst-case of holder's max_accepted_htlcs + counterparty's max_accepted_htlcs * 706 WU (for post-anchor HTLC-Success) * worst-historical mempool feerate. And this is only per-channel. Anyway, I don't know where we would like to start document those requirements here, in #1860 or even after ?

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.

Let's do it later.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase, think we can land this today or tomorrow?

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from 148c04c to 575b2afCompareDecember 6, 2022 20:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like CI is sad? Feel free to squash the fixups.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 575b2af to 12e0310CompareDecember 6, 2022 22:16
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

There was a stray else causing the failures. Test suite passes locally now so we should be good.

@ariard

Copy link
Copy Markdown

On my side, I'll try to review back tomorrow. Checking the full transaction processing flow from ChainMonitor.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 12e0310 to 6a99960CompareDecember 7, 2022 00:14
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@TheBlueMattTheBlueMatt mentioned this pull request Dec 7, 2022
Previously, this method assumed that all HTLC transactions have 1 input
and 1 output, with the sole input having a witness of 5 elements. This
will no longer be the case for HTLC transactions on channels with
anchors outputs since additional inputs and outputs can be attached to
them to allow fee bumping.
This is only a name change, there is no change in behavior.
Now that our txids will no longer be stable for package claims that
require external funds to be allocated, we transition to a 32-byte array
identifier to remain compatible with them.
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 6a99960 to ec1f334CompareDecember 7, 2022 00:48

@arik-soarik-so left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I really like the comments explaining the potential vulnerability that might arise with anchor HTLC transactions.

Comment threadlightning/src/chain/onchaintx.rs
@TheBlueMatt
TheBlueMatt merged commit eea56e9 into lightningdevkit:mainDec 7, 2022
@tnulltnull mentioned this pull request Dec 7, 2022

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See second-last comment.

Comment threadlightning/src/chain/channelmonitor.rs
// Since there may be multiple HTLCs (all from the same commitment) being
// claimed by the counterparty within the same transaction, and
// `check_spend_counterparty_htlc` already checks for all of them, we can
// safely break from our loop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is assumption is broken, and per se our code will still miss the detection and claim of revoked second-stage HTLC transactions.

With SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, the second-stage HTLC transaction claims can be aggregated across commitment transactions, as there is no commitment to the spend txid in the shared transaction fields (e.g the parent txid could be in the nLocktime field). Our logic iterate on all the inputs, and at the first one with a confirmed commitment txid in counterparty_commitment_txn_on_chain, we'll call in check_spend_counterparty_htlc() L3028. There, we iterate against all inputs and if the input outpoint txid is different from the passed commitment_txid, we skip it further processing. Once we're out of check_spend_counterparty_htlc(), we end up here and break from the for control flow, am I correct ? At the very least I don't think we have a test coverage for this case.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

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.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

Note, here we're checking the second-stage HTLC transactions, and we're filtering out the input/output pair to be claimed based on the spent commitment transaction id. Post-anchor output, second-stage HTLC transactions are signed with SIGHASH_SINGLE enabling aggregation of HTLC claims from the same commitment transaction, but there is no constraint restraining the aggregation of HTLC claims across commitment transactions. Even if for one ChannelMonitor there is a single commitment_txid that can confirms.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

This is a correct, the claim of HTLCs is a per-ChannelMonitor responsibility, and I don't think there is a way open by malleability where the aggregated HTLC claims crafted by our counterparty could blind our parsing logic. Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

Since the ChannelMonitor is already on a per-channel basis, the comment really means "all from the same commitment for this channel". I agree it could definitely be re-worded better though.

Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Doesn't seem like there's much we can do other than document it? Even then, I would imagine all LDK users want to use our ChainMonitor as is. Rather than re-implementing it for their own needs, they just need to implement the trait dependencies on ChainMonitor.

@ariardariardDec 8, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Doesn't seem like there's much we can do other than document it?

More documentation of our assumptions is always better. I think with the extended flexibility offered by our interfaces we have far less visibility on how it could be re-implemented by users rather than a monolithic Lightning node directly consuming from standard Core interfaces. This is raising the bar for funds safety, as implicit assumptions of our low-level code could be silently broken. Beyond, we might have in the future to re-implement a ChainMonitor for "monitor-replica" support, a bit more sophisticated than the standard one.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulino deleted the anchors-bump-htlc-resolution-event branch December 7, 2022 18:33
TheBlueMatt added a commit that referenced this pull request Dec 12, 2022
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.

5 participants

@wpaulino@codecov-commenter@TheBlueMatt@ariard@arik-so
, '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('^' + ".*" + ' Introduce new BumpTransactionEvent variant HTLCResolution by wpaulino · Pull Request #1825 · lightningdevkit/rust-lightning · GitHub
Skip to content

Introduce new BumpTransactionEvent variant HTLCResolution - #1825

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event
Dec 7, 2022
Merged

Introduce new BumpTransactionEvent variant HTLCResolution#1825
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

This PR continues the work laid out in #1689 and introduces a new BumpTransactionEvent variant: HTLCResolution. Similarly, this event is to be consumed by users and indicates that a channel's commitment transaction with unresolved HTLCs has confirmed onchain, requiring HTLC transactions to be broadcast with additional inputs and/or outputs attached to satisfy feerate demands at the time of broadcast.

Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 20fff12 to 51418abCompareNovember 4, 2022 19:37
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 51418ab to 9fbd465CompareNovember 16, 2022 00:47
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 9fbd465 to 5a257d0CompareNovember 18, 2022 01:01
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 5a257d0 to 88c0356CompareNovember 18, 2022 01:36
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Rebased on latest to address an import conflict.

@codecov-commenter

codecov-commenter commented Nov 18, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.57% // Head: 90.50% // Decreases project coverage by -0.07%⚠️

Coverage data is based on head (6a99960) compared to base (36e6023).
Patch coverage: 86.98% of modified lines in pull request are covered.

❗ Current head 6a99960 differs from pull request most recent head ec1f334. Consider uploading reports for the commit ec1f334 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1825 +/- ##
==========================================
- Coverage 90.57% 90.50% -0.08% 
==========================================
Files 91 91 Lines 48556 48731 +175 Branches 48556 48731 +175 ==========================================
+ Hits 43982 44102 +120 - Misses 4574 4629 +55 
Impacted FilesCoverage Δ
lightning/src/chain/keysinterface.rs83.14% <ø> (ø)
lightning/src/util/enforcing_trait_impls.rs82.92% <ø> (ø)
lightning/src/util/events.rs25.45% <ø> (ø)
lightning/src/chain/onchaintx.rs92.73% <66.66%> (-2.62%)⬇️
lightning/src/chain/package.rs91.76% <68.57%> (-1.09%)⬇️
lightning/src/ln/chan_utils.rs93.61% <94.59%> (-0.02%)⬇️
lightning/src/chain/channelmonitor.rs91.04% <98.63%> (+0.11%)⬆️
lightning/src/ln/onion_utils.rs93.56% <0.00%> (-1.37%)⬇️
lightning/src/ln/functional_tests.rs96.65% <0.00%> (-0.35%)⬇️
... and 5 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 88c0356 to c8be80fCompareNovember 22, 2022 01:27
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Pushed a new update addressing some remaining comments and a missing change noted by @ariard in #1860.

arik-so
arik-so previously approved these changes Nov 22, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from dd94ec2 to a6eff8aCompareNovember 22, 2022 20:34
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Squashed and rebased on latest to resolve a conflict.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from a6eff8a to f62e96fCompareNovember 23, 2022 19:00
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

> git diff -U1 a6eff8a f62e96f

diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index b70121ba..cb31783d 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -2651,13 +2651,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
- macro_rules! ignore_error {
- ( $thing : expr ) => {
- match $thing {
- Ok(a) => a,
- Err(_) => return (Vec::new(), None)
- }
- };
- }
-
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); };
- let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
+ let per_commitment_key = match SecretKey::from_slice(&secret) {
+ Ok(key) => key,
+ Err(_) => return (Vec::new(), None)
+ };
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
@@ -2667,6 +2661,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let mut outputs_to_watch = None;
+ // Previously, we would only claim HTLCs from revoked HTLC transactions if they had 1 input
+ // with a witness of 5 elements and 1 output. This wasn't enough for anchor outputs, as the
+ // counterparty can now aggregate multiple HTLCs into a single transaction thanks to
+ // `SIGHASH_SINGLE` remote signatures, leading us to not claim any HTLCs upon seeing a
+ // confirmed revoked HTLC transaction (for more details, see
+ // https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html).
+ //
+ // We make sure we're not vulnerable to this case by checking all inputs of the transaction,
+ // and claim those which spend the commitment transaction, have a witness of 5 elements, and
+ // have a corresponding output at the same index within the transaction.
for (idx, input) in tx.input.iter().enumerate() {
- // HTLC transactions always spend an output on the commitment transaction with a witness
- // of 5 elements. The HTLC input will always have a corresponding output at the same
- // index within the transaction.
if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() {

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from f62e96f to e5a61f2CompareNovember 28, 2022 16:38
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

@TheBlueMatt I addressed your remaining comments. Will open a new PR to address the serialization changes discussed in #1825 (comment).

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

I left a handful of comments, ranging from mildly annoyed at what we have to trivial nits, but only one (the question about the 0th output) actually needs a response for this to land, I think.

Comment threadlightning/src/chain/keysinterface.rs Outdated
/// Note that this should only be used to sign HTLC transactions from channels supporting anchor
/// outputs after all additional inputs/outputs have been added to the transaction.
fn sign_holder_htlc_transaction(
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,

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.

For both this and sign_holder_anchor_input I'm really not a fan of passing in a full tx-to-sign. I'm not sure what to do about it, though, we don't really want to constrain the tx contents really. Luckily segwit commits to the amount, but I could see someone forgetting to check the fee, for example. I guess we're really relying on the other input being signed as "the" security control here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm not following -- we need to pass in the full transaction since it includes the additional fee inputs/outputs and our local signature is SIGHASH_ALL.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess I was thinking more that we could pass the set of non-HTLC inputs, the output script and fee and then have the siger recreate the transaction. This avoids having to calculate the fee, check the locktime, etc. In general the principle for the signing interfaces should be (but isn't always) that we pass the user the things they need to construct the object to sign (and a utility method to do so) and make them do the construction. That way everything they need to validate is shoved in their face and they can't avoid looking at it (and hopefully remember to check it). Given our other HTLC-sign transactions already have this mistake it's fine for now, but in the future we should refactor all those methods to do the HTLC tx construction on the signer's end.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/chan_utils.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from b9a485f to f26f235CompareDecember 1, 2022 00:32
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Updated docs and rebased on top of #1887 since it gets rid of some Results on HTLCDescriptor methods.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See last comment.

ClaimEvent::BumpHTLC {
target_feerate_sat_per_1000_weight, htlcs,
} => {
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Here I think we can debug_assert!(seff.onchain_tx_handler.opt_anchors()).

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.

We can probably replace most #[cfg(anchors)] with this assertion so will leave it for a follow-up.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_anchor_input`]: crate::chain::keysinterface::BaseSign::sign_holder_anchor_input
/// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness
ChannelClose {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note if you would like to add a commit to recall this enum field FundingUnilateralResolution. A ChannelClose in Lightning parlance is really ambiguous as it designates also the cooperative case. This cooperative case itself could be a target for fee-bumping in the future (e.g splicing support). A small bikeshedding as we're around.

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.

If we want to rename it, let's do it in a followup. This PR has been delayed enough and isn't even touching this code.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_htlc_transaction`]: crate::chain::keysinterface::BaseSign::sign_holder_htlc_transaction
/// [`HTLCDescriptor::tx_input_witness`]: HTLCDescriptor::tx_input_witness
HTLCResolution {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There is no current indication on the level of external fee-bumping reserves a user is supposed to keep. For HTLC resolution, we would have to encompass the worst-case of holder's max_accepted_htlcs + counterparty's max_accepted_htlcs * 706 WU (for post-anchor HTLC-Success) * worst-historical mempool feerate. And this is only per-channel. Anyway, I don't know where we would like to start document those requirements here, in #1860 or even after ?

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.

Let's do it later.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase, think we can land this today or tomorrow?

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from 148c04c to 575b2afCompareDecember 6, 2022 20:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like CI is sad? Feel free to squash the fixups.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 575b2af to 12e0310CompareDecember 6, 2022 22:16
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

There was a stray else causing the failures. Test suite passes locally now so we should be good.

@ariard

Copy link
Copy Markdown

On my side, I'll try to review back tomorrow. Checking the full transaction processing flow from ChainMonitor.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 12e0310 to 6a99960CompareDecember 7, 2022 00:14
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@TheBlueMattTheBlueMatt mentioned this pull request Dec 7, 2022
Previously, this method assumed that all HTLC transactions have 1 input
and 1 output, with the sole input having a witness of 5 elements. This
will no longer be the case for HTLC transactions on channels with
anchors outputs since additional inputs and outputs can be attached to
them to allow fee bumping.
This is only a name change, there is no change in behavior.
Now that our txids will no longer be stable for package claims that
require external funds to be allocated, we transition to a 32-byte array
identifier to remain compatible with them.
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 6a99960 to ec1f334CompareDecember 7, 2022 00:48

@arik-soarik-so left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I really like the comments explaining the potential vulnerability that might arise with anchor HTLC transactions.

Comment threadlightning/src/chain/onchaintx.rs
@TheBlueMatt
TheBlueMatt merged commit eea56e9 into lightningdevkit:mainDec 7, 2022
@tnulltnull mentioned this pull request Dec 7, 2022

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See second-last comment.

Comment threadlightning/src/chain/channelmonitor.rs
// Since there may be multiple HTLCs (all from the same commitment) being
// claimed by the counterparty within the same transaction, and
// `check_spend_counterparty_htlc` already checks for all of them, we can
// safely break from our loop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is assumption is broken, and per se our code will still miss the detection and claim of revoked second-stage HTLC transactions.

With SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, the second-stage HTLC transaction claims can be aggregated across commitment transactions, as there is no commitment to the spend txid in the shared transaction fields (e.g the parent txid could be in the nLocktime field). Our logic iterate on all the inputs, and at the first one with a confirmed commitment txid in counterparty_commitment_txn_on_chain, we'll call in check_spend_counterparty_htlc() L3028. There, we iterate against all inputs and if the input outpoint txid is different from the passed commitment_txid, we skip it further processing. Once we're out of check_spend_counterparty_htlc(), we end up here and break from the for control flow, am I correct ? At the very least I don't think we have a test coverage for this case.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

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.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

Note, here we're checking the second-stage HTLC transactions, and we're filtering out the input/output pair to be claimed based on the spent commitment transaction id. Post-anchor output, second-stage HTLC transactions are signed with SIGHASH_SINGLE enabling aggregation of HTLC claims from the same commitment transaction, but there is no constraint restraining the aggregation of HTLC claims across commitment transactions. Even if for one ChannelMonitor there is a single commitment_txid that can confirms.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

This is a correct, the claim of HTLCs is a per-ChannelMonitor responsibility, and I don't think there is a way open by malleability where the aggregated HTLC claims crafted by our counterparty could blind our parsing logic. Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

Since the ChannelMonitor is already on a per-channel basis, the comment really means "all from the same commitment for this channel". I agree it could definitely be re-worded better though.

Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Doesn't seem like there's much we can do other than document it? Even then, I would imagine all LDK users want to use our ChainMonitor as is. Rather than re-implementing it for their own needs, they just need to implement the trait dependencies on ChainMonitor.

@ariardariardDec 8, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Doesn't seem like there's much we can do other than document it?

More documentation of our assumptions is always better. I think with the extended flexibility offered by our interfaces we have far less visibility on how it could be re-implemented by users rather than a monolithic Lightning node directly consuming from standard Core interfaces. This is raising the bar for funds safety, as implicit assumptions of our low-level code could be silently broken. Beyond, we might have in the future to re-implement a ChainMonitor for "monitor-replica" support, a bit more sophisticated than the standard one.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulino deleted the anchors-bump-htlc-resolution-event branch December 7, 2022 18:33
TheBlueMatt added a commit that referenced this pull request Dec 12, 2022
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.

5 participants

@wpaulino@codecov-commenter@TheBlueMatt@ariard@arik-so
, '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" + ' Introduce new BumpTransactionEvent variant HTLCResolution by wpaulino · Pull Request #1825 · lightningdevkit/rust-lightning · GitHub
Skip to content

Introduce new BumpTransactionEvent variant HTLCResolution - #1825

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event
Dec 7, 2022
Merged

Introduce new BumpTransactionEvent variant HTLCResolution#1825
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

This PR continues the work laid out in #1689 and introduces a new BumpTransactionEvent variant: HTLCResolution. Similarly, this event is to be consumed by users and indicates that a channel's commitment transaction with unresolved HTLCs has confirmed onchain, requiring HTLC transactions to be broadcast with additional inputs and/or outputs attached to satisfy feerate demands at the time of broadcast.

Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 20fff12 to 51418abCompareNovember 4, 2022 19:37
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 51418ab to 9fbd465CompareNovember 16, 2022 00:47
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 9fbd465 to 5a257d0CompareNovember 18, 2022 01:01
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 5a257d0 to 88c0356CompareNovember 18, 2022 01:36
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Rebased on latest to address an import conflict.

@codecov-commenter

codecov-commenter commented Nov 18, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.57% // Head: 90.50% // Decreases project coverage by -0.07%⚠️

Coverage data is based on head (6a99960) compared to base (36e6023).
Patch coverage: 86.98% of modified lines in pull request are covered.

❗ Current head 6a99960 differs from pull request most recent head ec1f334. Consider uploading reports for the commit ec1f334 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1825 +/- ##
==========================================
- Coverage 90.57% 90.50% -0.08% 
==========================================
Files 91 91 Lines 48556 48731 +175 Branches 48556 48731 +175 ==========================================
+ Hits 43982 44102 +120 - Misses 4574 4629 +55 
Impacted FilesCoverage Δ
lightning/src/chain/keysinterface.rs83.14% <ø> (ø)
lightning/src/util/enforcing_trait_impls.rs82.92% <ø> (ø)
lightning/src/util/events.rs25.45% <ø> (ø)
lightning/src/chain/onchaintx.rs92.73% <66.66%> (-2.62%)⬇️
lightning/src/chain/package.rs91.76% <68.57%> (-1.09%)⬇️
lightning/src/ln/chan_utils.rs93.61% <94.59%> (-0.02%)⬇️
lightning/src/chain/channelmonitor.rs91.04% <98.63%> (+0.11%)⬆️
lightning/src/ln/onion_utils.rs93.56% <0.00%> (-1.37%)⬇️
lightning/src/ln/functional_tests.rs96.65% <0.00%> (-0.35%)⬇️
... and 5 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 88c0356 to c8be80fCompareNovember 22, 2022 01:27
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Pushed a new update addressing some remaining comments and a missing change noted by @ariard in #1860.

arik-so
arik-so previously approved these changes Nov 22, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from dd94ec2 to a6eff8aCompareNovember 22, 2022 20:34
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Squashed and rebased on latest to resolve a conflict.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from a6eff8a to f62e96fCompareNovember 23, 2022 19:00
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

> git diff -U1 a6eff8a f62e96f

diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index b70121ba..cb31783d 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -2651,13 +2651,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
- macro_rules! ignore_error {
- ( $thing : expr ) => {
- match $thing {
- Ok(a) => a,
- Err(_) => return (Vec::new(), None)
- }
- };
- }
-
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); };
- let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
+ let per_commitment_key = match SecretKey::from_slice(&secret) {
+ Ok(key) => key,
+ Err(_) => return (Vec::new(), None)
+ };
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
@@ -2667,6 +2661,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let mut outputs_to_watch = None;
+ // Previously, we would only claim HTLCs from revoked HTLC transactions if they had 1 input
+ // with a witness of 5 elements and 1 output. This wasn't enough for anchor outputs, as the
+ // counterparty can now aggregate multiple HTLCs into a single transaction thanks to
+ // `SIGHASH_SINGLE` remote signatures, leading us to not claim any HTLCs upon seeing a
+ // confirmed revoked HTLC transaction (for more details, see
+ // https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html).
+ //
+ // We make sure we're not vulnerable to this case by checking all inputs of the transaction,
+ // and claim those which spend the commitment transaction, have a witness of 5 elements, and
+ // have a corresponding output at the same index within the transaction.
for (idx, input) in tx.input.iter().enumerate() {
- // HTLC transactions always spend an output on the commitment transaction with a witness
- // of 5 elements. The HTLC input will always have a corresponding output at the same
- // index within the transaction.
if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() {

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from f62e96f to e5a61f2CompareNovember 28, 2022 16:38
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

@TheBlueMatt I addressed your remaining comments. Will open a new PR to address the serialization changes discussed in #1825 (comment).

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

I left a handful of comments, ranging from mildly annoyed at what we have to trivial nits, but only one (the question about the 0th output) actually needs a response for this to land, I think.

Comment threadlightning/src/chain/keysinterface.rs Outdated
/// Note that this should only be used to sign HTLC transactions from channels supporting anchor
/// outputs after all additional inputs/outputs have been added to the transaction.
fn sign_holder_htlc_transaction(
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,

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.

For both this and sign_holder_anchor_input I'm really not a fan of passing in a full tx-to-sign. I'm not sure what to do about it, though, we don't really want to constrain the tx contents really. Luckily segwit commits to the amount, but I could see someone forgetting to check the fee, for example. I guess we're really relying on the other input being signed as "the" security control here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm not following -- we need to pass in the full transaction since it includes the additional fee inputs/outputs and our local signature is SIGHASH_ALL.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess I was thinking more that we could pass the set of non-HTLC inputs, the output script and fee and then have the siger recreate the transaction. This avoids having to calculate the fee, check the locktime, etc. In general the principle for the signing interfaces should be (but isn't always) that we pass the user the things they need to construct the object to sign (and a utility method to do so) and make them do the construction. That way everything they need to validate is shoved in their face and they can't avoid looking at it (and hopefully remember to check it). Given our other HTLC-sign transactions already have this mistake it's fine for now, but in the future we should refactor all those methods to do the HTLC tx construction on the signer's end.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/chan_utils.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from b9a485f to f26f235CompareDecember 1, 2022 00:32
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Updated docs and rebased on top of #1887 since it gets rid of some Results on HTLCDescriptor methods.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See last comment.

ClaimEvent::BumpHTLC {
target_feerate_sat_per_1000_weight, htlcs,
} => {
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Here I think we can debug_assert!(seff.onchain_tx_handler.opt_anchors()).

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.

We can probably replace most #[cfg(anchors)] with this assertion so will leave it for a follow-up.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_anchor_input`]: crate::chain::keysinterface::BaseSign::sign_holder_anchor_input
/// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness
ChannelClose {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note if you would like to add a commit to recall this enum field FundingUnilateralResolution. A ChannelClose in Lightning parlance is really ambiguous as it designates also the cooperative case. This cooperative case itself could be a target for fee-bumping in the future (e.g splicing support). A small bikeshedding as we're around.

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.

If we want to rename it, let's do it in a followup. This PR has been delayed enough and isn't even touching this code.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_htlc_transaction`]: crate::chain::keysinterface::BaseSign::sign_holder_htlc_transaction
/// [`HTLCDescriptor::tx_input_witness`]: HTLCDescriptor::tx_input_witness
HTLCResolution {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There is no current indication on the level of external fee-bumping reserves a user is supposed to keep. For HTLC resolution, we would have to encompass the worst-case of holder's max_accepted_htlcs + counterparty's max_accepted_htlcs * 706 WU (for post-anchor HTLC-Success) * worst-historical mempool feerate. And this is only per-channel. Anyway, I don't know where we would like to start document those requirements here, in #1860 or even after ?

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.

Let's do it later.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase, think we can land this today or tomorrow?

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from 148c04c to 575b2afCompareDecember 6, 2022 20:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like CI is sad? Feel free to squash the fixups.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 575b2af to 12e0310CompareDecember 6, 2022 22:16
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

There was a stray else causing the failures. Test suite passes locally now so we should be good.

@ariard

Copy link
Copy Markdown

On my side, I'll try to review back tomorrow. Checking the full transaction processing flow from ChainMonitor.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 12e0310 to 6a99960CompareDecember 7, 2022 00:14
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@TheBlueMattTheBlueMatt mentioned this pull request Dec 7, 2022
Previously, this method assumed that all HTLC transactions have 1 input
and 1 output, with the sole input having a witness of 5 elements. This
will no longer be the case for HTLC transactions on channels with
anchors outputs since additional inputs and outputs can be attached to
them to allow fee bumping.
This is only a name change, there is no change in behavior.
Now that our txids will no longer be stable for package claims that
require external funds to be allocated, we transition to a 32-byte array
identifier to remain compatible with them.
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 6a99960 to ec1f334CompareDecember 7, 2022 00:48

@arik-soarik-so left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I really like the comments explaining the potential vulnerability that might arise with anchor HTLC transactions.

Comment threadlightning/src/chain/onchaintx.rs
@TheBlueMatt
TheBlueMatt merged commit eea56e9 into lightningdevkit:mainDec 7, 2022
@tnulltnull mentioned this pull request Dec 7, 2022

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See second-last comment.

Comment threadlightning/src/chain/channelmonitor.rs
// Since there may be multiple HTLCs (all from the same commitment) being
// claimed by the counterparty within the same transaction, and
// `check_spend_counterparty_htlc` already checks for all of them, we can
// safely break from our loop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is assumption is broken, and per se our code will still miss the detection and claim of revoked second-stage HTLC transactions.

With SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, the second-stage HTLC transaction claims can be aggregated across commitment transactions, as there is no commitment to the spend txid in the shared transaction fields (e.g the parent txid could be in the nLocktime field). Our logic iterate on all the inputs, and at the first one with a confirmed commitment txid in counterparty_commitment_txn_on_chain, we'll call in check_spend_counterparty_htlc() L3028. There, we iterate against all inputs and if the input outpoint txid is different from the passed commitment_txid, we skip it further processing. Once we're out of check_spend_counterparty_htlc(), we end up here and break from the for control flow, am I correct ? At the very least I don't think we have a test coverage for this case.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

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.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

Note, here we're checking the second-stage HTLC transactions, and we're filtering out the input/output pair to be claimed based on the spent commitment transaction id. Post-anchor output, second-stage HTLC transactions are signed with SIGHASH_SINGLE enabling aggregation of HTLC claims from the same commitment transaction, but there is no constraint restraining the aggregation of HTLC claims across commitment transactions. Even if for one ChannelMonitor there is a single commitment_txid that can confirms.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

This is a correct, the claim of HTLCs is a per-ChannelMonitor responsibility, and I don't think there is a way open by malleability where the aggregated HTLC claims crafted by our counterparty could blind our parsing logic. Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

Since the ChannelMonitor is already on a per-channel basis, the comment really means "all from the same commitment for this channel". I agree it could definitely be re-worded better though.

Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Doesn't seem like there's much we can do other than document it? Even then, I would imagine all LDK users want to use our ChainMonitor as is. Rather than re-implementing it for their own needs, they just need to implement the trait dependencies on ChainMonitor.

@ariardariardDec 8, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Doesn't seem like there's much we can do other than document it?

More documentation of our assumptions is always better. I think with the extended flexibility offered by our interfaces we have far less visibility on how it could be re-implemented by users rather than a monolithic Lightning node directly consuming from standard Core interfaces. This is raising the bar for funds safety, as implicit assumptions of our low-level code could be silently broken. Beyond, we might have in the future to re-implement a ChainMonitor for "monitor-replica" support, a bit more sophisticated than the standard one.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulino deleted the anchors-bump-htlc-resolution-event branch December 7, 2022 18:33
TheBlueMatt added a commit that referenced this pull request Dec 12, 2022
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.

5 participants

@wpaulino@codecov-commenter@TheBlueMatt@ariard@arik-so
, '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('^' + ".*" + ' Introduce new BumpTransactionEvent variant HTLCResolution by wpaulino · Pull Request #1825 · lightningdevkit/rust-lightning · GitHub
Skip to content

Introduce new BumpTransactionEvent variant HTLCResolution - #1825

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event
Dec 7, 2022
Merged

Introduce new BumpTransactionEvent variant HTLCResolution#1825
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

This PR continues the work laid out in #1689 and introduces a new BumpTransactionEvent variant: HTLCResolution. Similarly, this event is to be consumed by users and indicates that a channel's commitment transaction with unresolved HTLCs has confirmed onchain, requiring HTLC transactions to be broadcast with additional inputs and/or outputs attached to satisfy feerate demands at the time of broadcast.

Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 20fff12 to 51418abCompareNovember 4, 2022 19:37
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 51418ab to 9fbd465CompareNovember 16, 2022 00:47
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 9fbd465 to 5a257d0CompareNovember 18, 2022 01:01
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 5a257d0 to 88c0356CompareNovember 18, 2022 01:36
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Rebased on latest to address an import conflict.

@codecov-commenter

codecov-commenter commented Nov 18, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.57% // Head: 90.50% // Decreases project coverage by -0.07%⚠️

Coverage data is based on head (6a99960) compared to base (36e6023).
Patch coverage: 86.98% of modified lines in pull request are covered.

❗ Current head 6a99960 differs from pull request most recent head ec1f334. Consider uploading reports for the commit ec1f334 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1825 +/- ##
==========================================
- Coverage 90.57% 90.50% -0.08% 
==========================================
Files 91 91 Lines 48556 48731 +175 Branches 48556 48731 +175 ==========================================
+ Hits 43982 44102 +120 - Misses 4574 4629 +55 
Impacted FilesCoverage Δ
lightning/src/chain/keysinterface.rs83.14% <ø> (ø)
lightning/src/util/enforcing_trait_impls.rs82.92% <ø> (ø)
lightning/src/util/events.rs25.45% <ø> (ø)
lightning/src/chain/onchaintx.rs92.73% <66.66%> (-2.62%)⬇️
lightning/src/chain/package.rs91.76% <68.57%> (-1.09%)⬇️
lightning/src/ln/chan_utils.rs93.61% <94.59%> (-0.02%)⬇️
lightning/src/chain/channelmonitor.rs91.04% <98.63%> (+0.11%)⬆️
lightning/src/ln/onion_utils.rs93.56% <0.00%> (-1.37%)⬇️
lightning/src/ln/functional_tests.rs96.65% <0.00%> (-0.35%)⬇️
... and 5 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 88c0356 to c8be80fCompareNovember 22, 2022 01:27
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Pushed a new update addressing some remaining comments and a missing change noted by @ariard in #1860.

arik-so
arik-so previously approved these changes Nov 22, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from dd94ec2 to a6eff8aCompareNovember 22, 2022 20:34
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Squashed and rebased on latest to resolve a conflict.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from a6eff8a to f62e96fCompareNovember 23, 2022 19:00
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

> git diff -U1 a6eff8a f62e96f

diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index b70121ba..cb31783d 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -2651,13 +2651,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
- macro_rules! ignore_error {
- ( $thing : expr ) => {
- match $thing {
- Ok(a) => a,
- Err(_) => return (Vec::new(), None)
- }
- };
- }
-
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); };
- let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
+ let per_commitment_key = match SecretKey::from_slice(&secret) {
+ Ok(key) => key,
+ Err(_) => return (Vec::new(), None)
+ };
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
@@ -2667,6 +2661,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let mut outputs_to_watch = None;
+ // Previously, we would only claim HTLCs from revoked HTLC transactions if they had 1 input
+ // with a witness of 5 elements and 1 output. This wasn't enough for anchor outputs, as the
+ // counterparty can now aggregate multiple HTLCs into a single transaction thanks to
+ // `SIGHASH_SINGLE` remote signatures, leading us to not claim any HTLCs upon seeing a
+ // confirmed revoked HTLC transaction (for more details, see
+ // https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html).
+ //
+ // We make sure we're not vulnerable to this case by checking all inputs of the transaction,
+ // and claim those which spend the commitment transaction, have a witness of 5 elements, and
+ // have a corresponding output at the same index within the transaction.
for (idx, input) in tx.input.iter().enumerate() {
- // HTLC transactions always spend an output on the commitment transaction with a witness
- // of 5 elements. The HTLC input will always have a corresponding output at the same
- // index within the transaction.
if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() {

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from f62e96f to e5a61f2CompareNovember 28, 2022 16:38
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

@TheBlueMatt I addressed your remaining comments. Will open a new PR to address the serialization changes discussed in #1825 (comment).

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

I left a handful of comments, ranging from mildly annoyed at what we have to trivial nits, but only one (the question about the 0th output) actually needs a response for this to land, I think.

Comment threadlightning/src/chain/keysinterface.rs Outdated
/// Note that this should only be used to sign HTLC transactions from channels supporting anchor
/// outputs after all additional inputs/outputs have been added to the transaction.
fn sign_holder_htlc_transaction(
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,

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.

For both this and sign_holder_anchor_input I'm really not a fan of passing in a full tx-to-sign. I'm not sure what to do about it, though, we don't really want to constrain the tx contents really. Luckily segwit commits to the amount, but I could see someone forgetting to check the fee, for example. I guess we're really relying on the other input being signed as "the" security control here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm not following -- we need to pass in the full transaction since it includes the additional fee inputs/outputs and our local signature is SIGHASH_ALL.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess I was thinking more that we could pass the set of non-HTLC inputs, the output script and fee and then have the siger recreate the transaction. This avoids having to calculate the fee, check the locktime, etc. In general the principle for the signing interfaces should be (but isn't always) that we pass the user the things they need to construct the object to sign (and a utility method to do so) and make them do the construction. That way everything they need to validate is shoved in their face and they can't avoid looking at it (and hopefully remember to check it). Given our other HTLC-sign transactions already have this mistake it's fine for now, but in the future we should refactor all those methods to do the HTLC tx construction on the signer's end.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/chan_utils.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from b9a485f to f26f235CompareDecember 1, 2022 00:32
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Updated docs and rebased on top of #1887 since it gets rid of some Results on HTLCDescriptor methods.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See last comment.

ClaimEvent::BumpHTLC {
target_feerate_sat_per_1000_weight, htlcs,
} => {
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Here I think we can debug_assert!(seff.onchain_tx_handler.opt_anchors()).

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.

We can probably replace most #[cfg(anchors)] with this assertion so will leave it for a follow-up.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_anchor_input`]: crate::chain::keysinterface::BaseSign::sign_holder_anchor_input
/// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness
ChannelClose {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note if you would like to add a commit to recall this enum field FundingUnilateralResolution. A ChannelClose in Lightning parlance is really ambiguous as it designates also the cooperative case. This cooperative case itself could be a target for fee-bumping in the future (e.g splicing support). A small bikeshedding as we're around.

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.

If we want to rename it, let's do it in a followup. This PR has been delayed enough and isn't even touching this code.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_htlc_transaction`]: crate::chain::keysinterface::BaseSign::sign_holder_htlc_transaction
/// [`HTLCDescriptor::tx_input_witness`]: HTLCDescriptor::tx_input_witness
HTLCResolution {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There is no current indication on the level of external fee-bumping reserves a user is supposed to keep. For HTLC resolution, we would have to encompass the worst-case of holder's max_accepted_htlcs + counterparty's max_accepted_htlcs * 706 WU (for post-anchor HTLC-Success) * worst-historical mempool feerate. And this is only per-channel. Anyway, I don't know where we would like to start document those requirements here, in #1860 or even after ?

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.

Let's do it later.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase, think we can land this today or tomorrow?

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from 148c04c to 575b2afCompareDecember 6, 2022 20:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like CI is sad? Feel free to squash the fixups.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 575b2af to 12e0310CompareDecember 6, 2022 22:16
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

There was a stray else causing the failures. Test suite passes locally now so we should be good.

@ariard

Copy link
Copy Markdown

On my side, I'll try to review back tomorrow. Checking the full transaction processing flow from ChainMonitor.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 12e0310 to 6a99960CompareDecember 7, 2022 00:14
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@TheBlueMattTheBlueMatt mentioned this pull request Dec 7, 2022
Previously, this method assumed that all HTLC transactions have 1 input
and 1 output, with the sole input having a witness of 5 elements. This
will no longer be the case for HTLC transactions on channels with
anchors outputs since additional inputs and outputs can be attached to
them to allow fee bumping.
This is only a name change, there is no change in behavior.
Now that our txids will no longer be stable for package claims that
require external funds to be allocated, we transition to a 32-byte array
identifier to remain compatible with them.
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 6a99960 to ec1f334CompareDecember 7, 2022 00:48

@arik-soarik-so left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I really like the comments explaining the potential vulnerability that might arise with anchor HTLC transactions.

Comment threadlightning/src/chain/onchaintx.rs
@TheBlueMatt
TheBlueMatt merged commit eea56e9 into lightningdevkit:mainDec 7, 2022
@tnulltnull mentioned this pull request Dec 7, 2022

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See second-last comment.

Comment threadlightning/src/chain/channelmonitor.rs
// Since there may be multiple HTLCs (all from the same commitment) being
// claimed by the counterparty within the same transaction, and
// `check_spend_counterparty_htlc` already checks for all of them, we can
// safely break from our loop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is assumption is broken, and per se our code will still miss the detection and claim of revoked second-stage HTLC transactions.

With SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, the second-stage HTLC transaction claims can be aggregated across commitment transactions, as there is no commitment to the spend txid in the shared transaction fields (e.g the parent txid could be in the nLocktime field). Our logic iterate on all the inputs, and at the first one with a confirmed commitment txid in counterparty_commitment_txn_on_chain, we'll call in check_spend_counterparty_htlc() L3028. There, we iterate against all inputs and if the input outpoint txid is different from the passed commitment_txid, we skip it further processing. Once we're out of check_spend_counterparty_htlc(), we end up here and break from the for control flow, am I correct ? At the very least I don't think we have a test coverage for this case.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

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.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

Note, here we're checking the second-stage HTLC transactions, and we're filtering out the input/output pair to be claimed based on the spent commitment transaction id. Post-anchor output, second-stage HTLC transactions are signed with SIGHASH_SINGLE enabling aggregation of HTLC claims from the same commitment transaction, but there is no constraint restraining the aggregation of HTLC claims across commitment transactions. Even if for one ChannelMonitor there is a single commitment_txid that can confirms.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

This is a correct, the claim of HTLCs is a per-ChannelMonitor responsibility, and I don't think there is a way open by malleability where the aggregated HTLC claims crafted by our counterparty could blind our parsing logic. Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

Since the ChannelMonitor is already on a per-channel basis, the comment really means "all from the same commitment for this channel". I agree it could definitely be re-worded better though.

Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Doesn't seem like there's much we can do other than document it? Even then, I would imagine all LDK users want to use our ChainMonitor as is. Rather than re-implementing it for their own needs, they just need to implement the trait dependencies on ChainMonitor.

@ariardariardDec 8, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Doesn't seem like there's much we can do other than document it?

More documentation of our assumptions is always better. I think with the extended flexibility offered by our interfaces we have far less visibility on how it could be re-implemented by users rather than a monolithic Lightning node directly consuming from standard Core interfaces. This is raising the bar for funds safety, as implicit assumptions of our low-level code could be silently broken. Beyond, we might have in the future to re-implement a ChainMonitor for "monitor-replica" support, a bit more sophisticated than the standard one.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulino deleted the anchors-bump-htlc-resolution-event branch December 7, 2022 18:33
TheBlueMatt added a commit that referenced this pull request Dec 12, 2022
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.

5 participants

@wpaulino@codecov-commenter@TheBlueMatt@ariard@arik-so
, '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('^' + ".*" + ' Introduce new BumpTransactionEvent variant HTLCResolution by wpaulino · Pull Request #1825 · lightningdevkit/rust-lightning · GitHub
Skip to content

Introduce new BumpTransactionEvent variant HTLCResolution - #1825

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event
Dec 7, 2022
Merged

Introduce new BumpTransactionEvent variant HTLCResolution#1825
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

This PR continues the work laid out in #1689 and introduces a new BumpTransactionEvent variant: HTLCResolution. Similarly, this event is to be consumed by users and indicates that a channel's commitment transaction with unresolved HTLCs has confirmed onchain, requiring HTLC transactions to be broadcast with additional inputs and/or outputs attached to satisfy feerate demands at the time of broadcast.

Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 20fff12 to 51418abCompareNovember 4, 2022 19:37
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 51418ab to 9fbd465CompareNovember 16, 2022 00:47
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 9fbd465 to 5a257d0CompareNovember 18, 2022 01:01
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 5a257d0 to 88c0356CompareNovember 18, 2022 01:36
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Rebased on latest to address an import conflict.

@codecov-commenter

codecov-commenter commented Nov 18, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.57% // Head: 90.50% // Decreases project coverage by -0.07%⚠️

Coverage data is based on head (6a99960) compared to base (36e6023).
Patch coverage: 86.98% of modified lines in pull request are covered.

❗ Current head 6a99960 differs from pull request most recent head ec1f334. Consider uploading reports for the commit ec1f334 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1825 +/- ##
==========================================
- Coverage 90.57% 90.50% -0.08% 
==========================================
Files 91 91 Lines 48556 48731 +175 Branches 48556 48731 +175 ==========================================
+ Hits 43982 44102 +120 - Misses 4574 4629 +55 
Impacted FilesCoverage Δ
lightning/src/chain/keysinterface.rs83.14% <ø> (ø)
lightning/src/util/enforcing_trait_impls.rs82.92% <ø> (ø)
lightning/src/util/events.rs25.45% <ø> (ø)
lightning/src/chain/onchaintx.rs92.73% <66.66%> (-2.62%)⬇️
lightning/src/chain/package.rs91.76% <68.57%> (-1.09%)⬇️
lightning/src/ln/chan_utils.rs93.61% <94.59%> (-0.02%)⬇️
lightning/src/chain/channelmonitor.rs91.04% <98.63%> (+0.11%)⬆️
lightning/src/ln/onion_utils.rs93.56% <0.00%> (-1.37%)⬇️
lightning/src/ln/functional_tests.rs96.65% <0.00%> (-0.35%)⬇️
... and 5 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 88c0356 to c8be80fCompareNovember 22, 2022 01:27
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Pushed a new update addressing some remaining comments and a missing change noted by @ariard in #1860.

arik-so
arik-so previously approved these changes Nov 22, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from dd94ec2 to a6eff8aCompareNovember 22, 2022 20:34
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Squashed and rebased on latest to resolve a conflict.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from a6eff8a to f62e96fCompareNovember 23, 2022 19:00
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

> git diff -U1 a6eff8a f62e96f

diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index b70121ba..cb31783d 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -2651,13 +2651,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
- macro_rules! ignore_error {
- ( $thing : expr ) => {
- match $thing {
- Ok(a) => a,
- Err(_) => return (Vec::new(), None)
- }
- };
- }
-
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); };
- let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
+ let per_commitment_key = match SecretKey::from_slice(&secret) {
+ Ok(key) => key,
+ Err(_) => return (Vec::new(), None)
+ };
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
@@ -2667,6 +2661,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let mut outputs_to_watch = None;
+ // Previously, we would only claim HTLCs from revoked HTLC transactions if they had 1 input
+ // with a witness of 5 elements and 1 output. This wasn't enough for anchor outputs, as the
+ // counterparty can now aggregate multiple HTLCs into a single transaction thanks to
+ // `SIGHASH_SINGLE` remote signatures, leading us to not claim any HTLCs upon seeing a
+ // confirmed revoked HTLC transaction (for more details, see
+ // https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html).
+ //
+ // We make sure we're not vulnerable to this case by checking all inputs of the transaction,
+ // and claim those which spend the commitment transaction, have a witness of 5 elements, and
+ // have a corresponding output at the same index within the transaction.
for (idx, input) in tx.input.iter().enumerate() {
- // HTLC transactions always spend an output on the commitment transaction with a witness
- // of 5 elements. The HTLC input will always have a corresponding output at the same
- // index within the transaction.
if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() {

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from f62e96f to e5a61f2CompareNovember 28, 2022 16:38
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

@TheBlueMatt I addressed your remaining comments. Will open a new PR to address the serialization changes discussed in #1825 (comment).

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

I left a handful of comments, ranging from mildly annoyed at what we have to trivial nits, but only one (the question about the 0th output) actually needs a response for this to land, I think.

Comment threadlightning/src/chain/keysinterface.rs Outdated
/// Note that this should only be used to sign HTLC transactions from channels supporting anchor
/// outputs after all additional inputs/outputs have been added to the transaction.
fn sign_holder_htlc_transaction(
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,

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.

For both this and sign_holder_anchor_input I'm really not a fan of passing in a full tx-to-sign. I'm not sure what to do about it, though, we don't really want to constrain the tx contents really. Luckily segwit commits to the amount, but I could see someone forgetting to check the fee, for example. I guess we're really relying on the other input being signed as "the" security control here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm not following -- we need to pass in the full transaction since it includes the additional fee inputs/outputs and our local signature is SIGHASH_ALL.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess I was thinking more that we could pass the set of non-HTLC inputs, the output script and fee and then have the siger recreate the transaction. This avoids having to calculate the fee, check the locktime, etc. In general the principle for the signing interfaces should be (but isn't always) that we pass the user the things they need to construct the object to sign (and a utility method to do so) and make them do the construction. That way everything they need to validate is shoved in their face and they can't avoid looking at it (and hopefully remember to check it). Given our other HTLC-sign transactions already have this mistake it's fine for now, but in the future we should refactor all those methods to do the HTLC tx construction on the signer's end.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/chan_utils.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from b9a485f to f26f235CompareDecember 1, 2022 00:32
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Updated docs and rebased on top of #1887 since it gets rid of some Results on HTLCDescriptor methods.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See last comment.

ClaimEvent::BumpHTLC {
target_feerate_sat_per_1000_weight, htlcs,
} => {
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Here I think we can debug_assert!(seff.onchain_tx_handler.opt_anchors()).

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.

We can probably replace most #[cfg(anchors)] with this assertion so will leave it for a follow-up.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_anchor_input`]: crate::chain::keysinterface::BaseSign::sign_holder_anchor_input
/// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness
ChannelClose {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note if you would like to add a commit to recall this enum field FundingUnilateralResolution. A ChannelClose in Lightning parlance is really ambiguous as it designates also the cooperative case. This cooperative case itself could be a target for fee-bumping in the future (e.g splicing support). A small bikeshedding as we're around.

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.

If we want to rename it, let's do it in a followup. This PR has been delayed enough and isn't even touching this code.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_htlc_transaction`]: crate::chain::keysinterface::BaseSign::sign_holder_htlc_transaction
/// [`HTLCDescriptor::tx_input_witness`]: HTLCDescriptor::tx_input_witness
HTLCResolution {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There is no current indication on the level of external fee-bumping reserves a user is supposed to keep. For HTLC resolution, we would have to encompass the worst-case of holder's max_accepted_htlcs + counterparty's max_accepted_htlcs * 706 WU (for post-anchor HTLC-Success) * worst-historical mempool feerate. And this is only per-channel. Anyway, I don't know where we would like to start document those requirements here, in #1860 or even after ?

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.

Let's do it later.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase, think we can land this today or tomorrow?

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from 148c04c to 575b2afCompareDecember 6, 2022 20:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like CI is sad? Feel free to squash the fixups.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 575b2af to 12e0310CompareDecember 6, 2022 22:16
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

There was a stray else causing the failures. Test suite passes locally now so we should be good.

@ariard

Copy link
Copy Markdown

On my side, I'll try to review back tomorrow. Checking the full transaction processing flow from ChainMonitor.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 12e0310 to 6a99960CompareDecember 7, 2022 00:14
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@TheBlueMattTheBlueMatt mentioned this pull request Dec 7, 2022
Previously, this method assumed that all HTLC transactions have 1 input
and 1 output, with the sole input having a witness of 5 elements. This
will no longer be the case for HTLC transactions on channels with
anchors outputs since additional inputs and outputs can be attached to
them to allow fee bumping.
This is only a name change, there is no change in behavior.
Now that our txids will no longer be stable for package claims that
require external funds to be allocated, we transition to a 32-byte array
identifier to remain compatible with them.
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 6a99960 to ec1f334CompareDecember 7, 2022 00:48

@arik-soarik-so left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I really like the comments explaining the potential vulnerability that might arise with anchor HTLC transactions.

Comment threadlightning/src/chain/onchaintx.rs
@TheBlueMatt
TheBlueMatt merged commit eea56e9 into lightningdevkit:mainDec 7, 2022
@tnulltnull mentioned this pull request Dec 7, 2022

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See second-last comment.

Comment threadlightning/src/chain/channelmonitor.rs
// Since there may be multiple HTLCs (all from the same commitment) being
// claimed by the counterparty within the same transaction, and
// `check_spend_counterparty_htlc` already checks for all of them, we can
// safely break from our loop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is assumption is broken, and per se our code will still miss the detection and claim of revoked second-stage HTLC transactions.

With SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, the second-stage HTLC transaction claims can be aggregated across commitment transactions, as there is no commitment to the spend txid in the shared transaction fields (e.g the parent txid could be in the nLocktime field). Our logic iterate on all the inputs, and at the first one with a confirmed commitment txid in counterparty_commitment_txn_on_chain, we'll call in check_spend_counterparty_htlc() L3028. There, we iterate against all inputs and if the input outpoint txid is different from the passed commitment_txid, we skip it further processing. Once we're out of check_spend_counterparty_htlc(), we end up here and break from the for control flow, am I correct ? At the very least I don't think we have a test coverage for this case.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

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.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

Note, here we're checking the second-stage HTLC transactions, and we're filtering out the input/output pair to be claimed based on the spent commitment transaction id. Post-anchor output, second-stage HTLC transactions are signed with SIGHASH_SINGLE enabling aggregation of HTLC claims from the same commitment transaction, but there is no constraint restraining the aggregation of HTLC claims across commitment transactions. Even if for one ChannelMonitor there is a single commitment_txid that can confirms.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

This is a correct, the claim of HTLCs is a per-ChannelMonitor responsibility, and I don't think there is a way open by malleability where the aggregated HTLC claims crafted by our counterparty could blind our parsing logic. Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

Since the ChannelMonitor is already on a per-channel basis, the comment really means "all from the same commitment for this channel". I agree it could definitely be re-worded better though.

Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Doesn't seem like there's much we can do other than document it? Even then, I would imagine all LDK users want to use our ChainMonitor as is. Rather than re-implementing it for their own needs, they just need to implement the trait dependencies on ChainMonitor.

@ariardariardDec 8, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Doesn't seem like there's much we can do other than document it?

More documentation of our assumptions is always better. I think with the extended flexibility offered by our interfaces we have far less visibility on how it could be re-implemented by users rather than a monolithic Lightning node directly consuming from standard Core interfaces. This is raising the bar for funds safety, as implicit assumptions of our low-level code could be silently broken. Beyond, we might have in the future to re-implement a ChainMonitor for "monitor-replica" support, a bit more sophisticated than the standard one.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulino deleted the anchors-bump-htlc-resolution-event branch December 7, 2022 18:33
TheBlueMatt added a commit that referenced this pull request Dec 12, 2022
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.

5 participants

@wpaulino@codecov-commenter@TheBlueMatt@ariard@arik-so
, '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); } })(); })(); Introduce new BumpTransactionEvent variant HTLCResolution by wpaulino · Pull Request #1825 · lightningdevkit/rust-lightning · GitHub
Skip to content

Introduce new BumpTransactionEvent variant HTLCResolution - #1825

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event
Dec 7, 2022
Merged

Introduce new BumpTransactionEvent variant HTLCResolution#1825
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
wpaulino:anchors-bump-htlc-resolution-event

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

This PR continues the work laid out in #1689 and introduces a new BumpTransactionEvent variant: HTLCResolution. Similarly, this event is to be consumed by users and indicates that a channel's commitment transaction with unresolved HTLCs has confirmed onchain, requiring HTLC transactions to be broadcast with additional inputs and/or outputs attached to satisfy feerate demands at the time of broadcast.

Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 20fff12 to 51418abCompareNovember 4, 2022 19:37
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/ln/chan_utils.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 51418ab to 9fbd465CompareNovember 16, 2022 00:47
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/keysinterface.rs Outdated
Comment threadlightning/src/chain/package.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 9fbd465 to 5a257d0CompareNovember 18, 2022 01:01
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 5a257d0 to 88c0356CompareNovember 18, 2022 01:36
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Rebased on latest to address an import conflict.

@codecov-commenter

codecov-commenter commented Nov 18, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.57% // Head: 90.50% // Decreases project coverage by -0.07%⚠️

Coverage data is based on head (6a99960) compared to base (36e6023).
Patch coverage: 86.98% of modified lines in pull request are covered.

❗ Current head 6a99960 differs from pull request most recent head ec1f334. Consider uploading reports for the commit ec1f334 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1825 +/- ##
==========================================
- Coverage 90.57% 90.50% -0.08% 
==========================================
Files 91 91 Lines 48556 48731 +175 Branches 48556 48731 +175 ==========================================
+ Hits 43982 44102 +120 - Misses 4574 4629 +55 
Impacted FilesCoverage Δ
lightning/src/chain/keysinterface.rs83.14% <ø> (ø)
lightning/src/util/enforcing_trait_impls.rs82.92% <ø> (ø)
lightning/src/util/events.rs25.45% <ø> (ø)
lightning/src/chain/onchaintx.rs92.73% <66.66%> (-2.62%)⬇️
lightning/src/chain/package.rs91.76% <68.57%> (-1.09%)⬇️
lightning/src/ln/chan_utils.rs93.61% <94.59%> (-0.02%)⬇️
lightning/src/chain/channelmonitor.rs91.04% <98.63%> (+0.11%)⬆️
lightning/src/ln/onion_utils.rs93.56% <0.00%> (-1.37%)⬇️
lightning/src/ln/functional_tests.rs96.65% <0.00%> (-0.35%)⬇️
... and 5 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/onchaintx.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 88c0356 to c8be80fCompareNovember 22, 2022 01:27
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Pushed a new update addressing some remaining comments and a missing change noted by @ariard in #1860.

arik-so
arik-so previously approved these changes Nov 22, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from dd94ec2 to a6eff8aCompareNovember 22, 2022 20:34
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Squashed and rebased on latest to resolve a conflict.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from a6eff8a to f62e96fCompareNovember 23, 2022 19:00
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

> git diff -U1 a6eff8a f62e96f

diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index b70121ba..cb31783d 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -2651,13 +2651,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
- macro_rules! ignore_error {
- ( $thing : expr ) => {
- match $thing {
- Ok(a) => a,
- Err(_) => return (Vec::new(), None)
- }
- };
- }
-
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); };
- let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
+ let per_commitment_key = match SecretKey::from_slice(&secret) {
+ Ok(key) => key,
+ Err(_) => return (Vec::new(), None)
+ };
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
@@ -2667,6 +2661,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let mut outputs_to_watch = None;
+ // Previously, we would only claim HTLCs from revoked HTLC transactions if they had 1 input
+ // with a witness of 5 elements and 1 output. This wasn't enough for anchor outputs, as the
+ // counterparty can now aggregate multiple HTLCs into a single transaction thanks to
+ // `SIGHASH_SINGLE` remote signatures, leading us to not claim any HTLCs upon seeing a
+ // confirmed revoked HTLC transaction (for more details, see
+ // https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html).
+ //
+ // We make sure we're not vulnerable to this case by checking all inputs of the transaction,
+ // and claim those which spend the commitment transaction, have a witness of 5 elements, and
+ // have a corresponding output at the same index within the transaction.
for (idx, input) in tx.input.iter().enumerate() {
- // HTLC transactions always spend an output on the commitment transaction with a witness
- // of 5 elements. The HTLC input will always have a corresponding output at the same
- // index within the transaction.
if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() {

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from f62e96f to e5a61f2CompareNovember 28, 2022 16:38
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

@TheBlueMatt I addressed your remaining comments. Will open a new PR to address the serialization changes discussed in #1825 (comment).

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

I left a handful of comments, ranging from mildly annoyed at what we have to trivial nits, but only one (the question about the 0th output) actually needs a response for this to land, I think.

Comment threadlightning/src/chain/keysinterface.rs Outdated
/// Note that this should only be used to sign HTLC transactions from channels supporting anchor
/// outputs after all additional inputs/outputs have been added to the transaction.
fn sign_holder_htlc_transaction(
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,

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.

For both this and sign_holder_anchor_input I'm really not a fan of passing in a full tx-to-sign. I'm not sure what to do about it, though, we don't really want to constrain the tx contents really. Luckily segwit commits to the amount, but I could see someone forgetting to check the fee, for example. I guess we're really relying on the other input being signed as "the" security control here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm not following -- we need to pass in the full transaction since it includes the additional fee inputs/outputs and our local signature is SIGHASH_ALL.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess I was thinking more that we could pass the set of non-HTLC inputs, the output script and fee and then have the siger recreate the transaction. This avoids having to calculate the fee, check the locktime, etc. In general the principle for the signing interfaces should be (but isn't always) that we pass the user the things they need to construct the object to sign (and a utility method to do so) and make them do the construction. That way everything they need to validate is shoved in their face and they can't avoid looking at it (and hopefully remember to check it). Given our other HTLC-sign transactions already have this mistake it's fine for now, but in the future we should refactor all those methods to do the HTLC tx construction on the signer's end.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/chan_utils.rs Outdated
Comment threadlightning/src/chain/onchaintx.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from b9a485f to f26f235CompareDecember 1, 2022 00:32
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Updated docs and rebased on top of #1887 since it gets rid of some Results on HTLCDescriptor methods.

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See last comment.

ClaimEvent::BumpHTLC {
target_feerate_sat_per_1000_weight, htlcs,
} => {
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Here I think we can debug_assert!(seff.onchain_tx_handler.opt_anchors()).

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.

We can probably replace most #[cfg(anchors)] with this assertion so will leave it for a follow-up.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_anchor_input`]: crate::chain::keysinterface::BaseSign::sign_holder_anchor_input
/// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness
ChannelClose {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note if you would like to add a commit to recall this enum field FundingUnilateralResolution. A ChannelClose in Lightning parlance is really ambiguous as it designates also the cooperative case. This cooperative case itself could be a target for fee-bumping in the future (e.g splicing support). A small bikeshedding as we're around.

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.

If we want to rename it, let's do it in a followup. This PR has been delayed enough and isn't even touching this code.

/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
/// [`BaseSign::sign_holder_htlc_transaction`]: crate::chain::keysinterface::BaseSign::sign_holder_htlc_transaction
/// [`HTLCDescriptor::tx_input_witness`]: HTLCDescriptor::tx_input_witness
HTLCResolution {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There is no current indication on the level of external fee-bumping reserves a user is supposed to keep. For HTLC resolution, we would have to encompass the worst-case of holder's max_accepted_htlcs + counterparty's max_accepted_htlcs * 706 WU (for post-anchor HTLC-Success) * worst-historical mempool feerate. And this is only per-channel. Anyway, I don't know where we would like to start document those requirements here, in #1860 or even after ?

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.

Let's do it later.

Comment threadlightning/src/util/events.rs Outdated
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/chain/channelmonitor.rs
Comment threadlightning/src/chain/channelmonitor.rs
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase, think we can land this today or tomorrow?

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch 2 times, most recently from 148c04c to 575b2afCompareDecember 6, 2022 20:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like CI is sad? Feel free to squash the fixups.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 575b2af to 12e0310CompareDecember 6, 2022 22:16
@wpaulino

Copy link
Copy Markdown
ContributorAuthor

There was a stray else causing the failures. Test suite passes locally now so we should be good.

@ariard

Copy link
Copy Markdown

On my side, I'll try to review back tomorrow. Checking the full transaction processing flow from ChainMonitor.

@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 12e0310 to 6a99960CompareDecember 7, 2022 00:14
Comment threadlightning/src/chain/channelmonitor.rs Outdated
@TheBlueMattTheBlueMatt mentioned this pull request Dec 7, 2022
Previously, this method assumed that all HTLC transactions have 1 input
and 1 output, with the sole input having a witness of 5 elements. This
will no longer be the case for HTLC transactions on channels with
anchors outputs since additional inputs and outputs can be attached to
them to allow fee bumping.
This is only a name change, there is no change in behavior.
Now that our txids will no longer be stable for package claims that
require external funds to be allocated, we transition to a 32-byte array
identifier to remain compatible with them.
@wpaulino
wpaulinoforce-pushed the anchors-bump-htlc-resolution-event branch from 6a99960 to ec1f334CompareDecember 7, 2022 00:48

@arik-soarik-so left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I really like the comments explaining the potential vulnerability that might arise with anchor HTLC transactions.

Comment threadlightning/src/chain/onchaintx.rs
@TheBlueMatt
TheBlueMatt merged commit eea56e9 into lightningdevkit:mainDec 7, 2022
@tnulltnull mentioned this pull request Dec 7, 2022

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See second-last comment.

Comment threadlightning/src/chain/channelmonitor.rs
// Since there may be multiple HTLCs (all from the same commitment) being
// claimed by the counterparty within the same transaction, and
// `check_spend_counterparty_htlc` already checks for all of them, we can
// safely break from our loop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is assumption is broken, and per se our code will still miss the detection and claim of revoked second-stage HTLC transactions.

With SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, the second-stage HTLC transaction claims can be aggregated across commitment transactions, as there is no commitment to the spend txid in the shared transaction fields (e.g the parent txid could be in the nLocktime field). Our logic iterate on all the inputs, and at the first one with a confirmed commitment txid in counterparty_commitment_txn_on_chain, we'll call in check_spend_counterparty_htlc() L3028. There, we iterate against all inputs and if the input outpoint txid is different from the passed commitment_txid, we skip it further processing. Once we're out of check_spend_counterparty_htlc(), we end up here and break from the for control flow, am I correct ? At the very least I don't think we have a test coverage for this case.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

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.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

I'm not sure I 100% got your issue here, but I don't think this is broken - we can't have two transactions confirmed spending different commitment transactions, thus, if commitment_txid is set based on counterparty_commitment_txn_on_chain its definitely the one and only remote commitment transaction that is spendable. Thus, the loop in check_spend_counterparty_htlc looks correct to me.

Note, here we're checking the second-stage HTLC transactions, and we're filtering out the input/output pair to be claimed based on the spent commitment transaction id. Post-anchor output, second-stage HTLC transactions are signed with SIGHASH_SINGLE enabling aggregation of HTLC claims from the same commitment transaction, but there is no constraint restraining the aggregation of HTLC claims across commitment transactions. Even if for one ChannelMonitor there is a single commitment_txid that can confirms.

Only one commitment transaction can ultimately confirm per channel though, which is why we break. If a second-stage HTLC transaction claims HTLCs across different channels, then it's up to each ChannelMonitor to claim the HTLCs only for their channel.

This is a correct, the claim of HTLCs is a per-ChannelMonitor responsibility, and I don't think there is a way open by malleability where the aggregated HTLC claims crafted by our counterparty could blind our parsing logic. Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay after looking more, the code is correct, even if the comment is wrong or confusing on the (all from the same commitment) as you can have HTLCs claims in the same transaction spending multiple counterparty commitment transactions.

Since the ChannelMonitor is already on a per-channel basis, the comment really means "all from the same commitment for this channel". I agree it could definitely be re-worded better though.

Note, it makes the logic dependent on the implementation of Chain::Confirm::transaction_confirmed. Our default implementation is correct as we're iterating over all the monitor states, though a more naive logic which would dedup transaction confirmation, or if it'll stop transaction processing after first monitor state match would be concerned by the security issue I'm raising.

Doesn't seem like there's much we can do other than document it? Even then, I would imagine all LDK users want to use our ChainMonitor as is. Rather than re-implementing it for their own needs, they just need to implement the trait dependencies on ChainMonitor.

@ariardariardDec 8, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Doesn't seem like there's much we can do other than document it?

More documentation of our assumptions is always better. I think with the extended flexibility offered by our interfaces we have far less visibility on how it could be re-implemented by users rather than a monolithic Lightning node directly consuming from standard Core interfaces. This is raising the bar for funds safety, as implicit assumptions of our low-level code could be silently broken. Beyond, we might have in the future to re-implement a ChainMonitor for "monitor-replica" support, a bit more sophisticated than the standard one.

Comment threadlightning/src/chain/channelmonitor.rs
@wpaulino
wpaulino deleted the anchors-bump-htlc-resolution-event branch December 7, 2022 18:33
TheBlueMatt added a commit that referenced this pull request Dec 12, 2022
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.

5 participants

@wpaulino@codecov-commenter@TheBlueMatt@ariard@arik-so