-
Notifications
You must be signed in to change notification settings - Fork 407
Introduce new BumpTransactionEvent variant HTLCResolution #1825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce new BumpTransactionEvent variant HTLCResolution #1825
Conversation
20fff12
to
51418ab
Compare
51418ab
to
9fbd465
Compare
9fbd465
to
5a257d0
Compare
5a257d0
to
88c0356
Compare
Rebased on latest to address an import conflict. |
Codecov ReportBase: 90.57% // Head: 90.50% // Decreases project coverage by
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
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. |
88c0356
to
c8be80f
Compare
Feel free to squash. |
dd94ec2
to
a6eff8a
Compare
Squashed and rebased on latest to resolve a conflict. |
a6eff8a
to
f62e96f
Compare
|
f62e96f
to
e5a61f2
Compare
@TheBlueMatt I addressed your remaining comments. Will open a new PR to address the serialization changes discussed in #1825 (comment). |
There was a problem hiding this comment.
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.
lightning/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, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
b9a485f
to
f26f235
Compare
Updated docs and rebased on top of #1887 since it gets rid of some |
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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())
.
There was a problem hiding this comment.
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.
@@ -277,6 +377,8 @@ pub enum BumpTransactionEvent { | |||
/// | |||
/// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner | |||
/// [`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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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.
Needs rebase, think we can land this today or tomorrow? |
148c04c
to
575b2af
Compare
Looks like CI is sad? Feel free to squash the fixups. |
575b2af
to
12e0310
Compare
There was a stray |
On my side, I'll try to review back tomorrow. Checking the full transaction processing flow from |
12e0310
to
6a99960
Compare
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.
6a99960
to
ec1f334
Compare
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See second-last comment.
// 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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
Trivial Followups to #1825
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.