-
Notifications
You must be signed in to change notification settings - Fork 366
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
Support receiving to multi-hop blinded paths #2688
Support receiving to multi-hop blinded paths #2688
Conversation
Codecov ReportAttention:
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## main #2688 +/- ##
==========================================
+ Coverage 88.53% 89.31% +0.77%
==========================================
Files 115 115
Lines 91011 96503 +5492
Branches 91011 96503 +5492
==========================================
+ Hits 80580 86194 +5614
+ Misses 8004 7947 -57
+ Partials 2427 2362 -65 ☔ View full report in Codecov by Sentry. |
cfec513
to
bf84b67
Compare
Still ironing out some improvements to the code post-rebase of #2540, will get an update out soon. |
bf84b67
to
69fc9bb
Compare
c6d549e
to
9b89b7e
Compare
Addressed feedback. Planning to add some test coverage for the new malformed HTLC ser changes, added a TODO. |
9b89b7e
to
d5dd5ea
Compare
Will be used to read encrypted_tlvs on non-intro-node onion receipt.
Support for receiving to multi-hop blinded payment paths will be completed in the next commit, sans error handling.
The only remaining step is to use the update_add blinding point in decoding inbound onion payloads. Error handling will be completed in upcoming commits.
For use in supporting receiving to multi-hop blinded paths.
d5dd5ea
to
91f7361
Compare
Rebased. |
lightning/src/ln/channel.rs
Outdated
&HTLCUpdateAwaitingACK::FailMalformedHTLC { | ||
htlc_id, failure_code, sha256_of_onion | ||
} => { | ||
// We don't want to break downgrading by adding a new variant, so write a dummy | ||
// `::FailHTLC` variant and write the real malformed error as an optional TLV. | ||
malformed_htlcs.push((htlc_id, failure_code, sha256_of_onion)); | ||
|
||
let dummy_err_packet = msgs::OnionErrorPacket { data: Vec::new() }; | ||
2u8.write(writer)?; | ||
htlc_id.write(writer)?; | ||
dummy_err_packet.write(writer)?; | ||
} |
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.
When downgrading, would these essentially be left in the holding cell permanently? Or are they dropped as a no-op when freeing the holding cell?
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.
No, downgraded nodes will use this dummy htlc data to fail this htlc backwards. This prevents them from dropping the malformed HTLCs (which are written as optional TLVs) on read
post-downgrade, which would cause them to not fail backwards and eventually be FC'd on.
lightning/src/ln/channel.rs
Outdated
/// A wire message that fails an HTLC backwards sometime after the update_add_htlc's corresponding | ||
/// RAA has been processed, as opposed to failing on initial update_add receipt. Useful for | ||
/// [`Channel::fail_htlc`] to fail with either [`msgs::UpdateFailMalformedHTLC`] or | ||
/// [`msgs::UpdateFailHTLC`] as needed. | ||
trait PostRAAFailHTLCMessage { | ||
type FailureReason: Clone; | ||
fn new(htlc_id: u64, channel_id: ChannelId, reason: Self::FailureReason) -> Self; | ||
fn message_name() -> &'static str; | ||
fn inbound_htlc_state(reason: Self::FailureReason) -> InboundHTLCState; | ||
fn htlc_update_awaiting_ack(htlc_id: u64, reason: Self::FailureReason) -> HTLCUpdateAwaitingACK; | ||
} |
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.
Given that the err_packet
parameter to fail_htlc
is what is being generalized, it seems more natural to have those types implement the trait. That way most of the methods would be associated functions and have a &self
parameter rather than having them all be named parameters (i.e., reason
as phrased here).
Also, since you could parameterize other functions with the error type, it means you don't need to specify a type parameter at the call site. Later this avoid duplicating queue_fail_htlc
as you'd be able to generalize its parameter instead.
lightning/src/ln/channel.rs
Outdated
/// Used for failing back with [`msgs::UpdateFailMalformedHTLC`]. For now, this is used when we | ||
/// want to fail blinded HTLCs where we are not the intro node. | ||
/// | ||
/// See [`Self::queue_fail_htlc`] for more info. | ||
pub fn queue_fail_malformed_htlc<L: Deref>( | ||
&mut self, htlc_id_arg: u64, failure_code: u16, sha256_of_onion: [u8; 32], logger: &L | ||
) -> Result<(), ChannelError> where L::Target: Logger { | ||
self.fail_htlc::<L, msgs::UpdateFailMalformedHTLC>( | ||
htlc_id_arg, (failure_code, sha256_of_onion), true, logger | ||
).map(|msg_opt| assert!(msg_opt.is_none(), "We forced holding cell?")) | ||
} | ||
|
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.
You shouldn't need to duplicate this. See earlier comment on the aded trait.
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.
Yeah, although see #2688 (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.
Re-duplicated after offline discussion since it's more readable in ChannelManager
this way and helps avoid increasing the visibility of some internal channel structs.
HTLCForwardInfo::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => { | ||
log_trace!(self.logger, "Failing malformed HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id); | ||
if let Err(e) = chan.queue_fail_malformed_htlc(htlc_id, failure_code, sha256_of_onion, &self.logger) { | ||
if let ChannelError::Ignore(msg) = e { | ||
log_trace!(self.logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg); | ||
} else { | ||
panic!("Stated return value requirements in queue_fail_malformed_htlc() were not met"); | ||
} | ||
// fail-backs are best-effort, we probably already have one | ||
// pending, and if not that's OK, if not, the channel is on | ||
// the chain and sending the HTLC-Timeout is their problem. | ||
continue; | ||
} |
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.
Might be able to DRY this up, too?
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 don't think so, because queue_fail_htlc
will have a statically chosen concrete underlying type of FailHTLCContents
at compile time, IIUC.
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.
Sure, but the error type and everything downstream of it is identical.
91f7361
to
d7e40a4
Compare
lightning/src/ln/channel.rs
Outdated
} | ||
fn message_name() -> &'static str { "update_fail_malformed_htlc" } | ||
fn inbound_htlc_state(self) -> InboundHTLCState { | ||
let (failure_code, sha256_of_onion) = self; |
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.
Should we normalize the order so this doesn't need to be destructured?
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.
Got rid of the destructuring. Not sure I got what you meant by "normalize."
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.
By that I meant to keep the order of (failure_code, sha256_of_onion)
the same between here and in InboundHTLCRemovalReason::FailMalformed
. That way you just need to wrap self
here rather than accessing each item in the tuple.
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.
Oh, okay. I'll pass on this for now but I'll do it if there ends up being a follow-up to this PR.
Squashed and addressed feedback with 1 new fixup. |
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.
LGTM, feel free to squash. A few nits but they're not blocking.
HTLCForwardInfo::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => { | ||
log_trace!(self.logger, "Failing malformed HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id); | ||
if let Err(e) = chan.queue_fail_malformed_htlc(htlc_id, failure_code, sha256_of_onion, &self.logger) { | ||
if let ChannelError::Ignore(msg) = e { | ||
log_trace!(self.logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg); | ||
} else { | ||
panic!("Stated return value requirements in queue_fail_malformed_htlc() were not met"); | ||
} | ||
// fail-backs are best-effort, we probably already have one | ||
// pending, and if not that's OK, if not, the channel is on | ||
// the chain and sending the HTLC-Timeout is their problem. | ||
continue; | ||
} |
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.
Sure, but the error type and everything downstream of it is identical.
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.
LGTM
For context, blinded HTLCs where we are not the intro node must always be failed back with malformed and invalid_onion_blinding error per BOLT 4. Prior to supporting blinded payments, the only way for an update_malformed to be returned from Channel was if an onion was actually found to be malformed during initial update_add processing. This meant that any malformed HTLCs would never live in the holding cell but instead would be returned directly upon initial RAA processing. Now, we need to be able to store these HTLCs in the holding cell because the HTLC failure necessitating an update_malformed may come long after the RAA is initially processed, and we may not be a state to send the update_malformed message at that time. Therefore, add a new holding cell HTLC variant for blinded non-intro node HTLCs, which will signal to Channel to fail with malformed and the correct error code.
Currently it returns only update_fail, but we'll want it to be able to return update_malformed as well in upcoming commits. We'll use this for correctly failing blinded received HTLCs backwards with malformed and invalid_onion_blinding error per BOLT 4.
Useful for failing blinded payments back with malformed, and will also be useful in the future when we move onion decoding into process_pending_htlc_forwards, after which Channel::fail_htlc will be used for all malformed htlcs.
Necessary to tell the Channel how to fail these htlcs.
Makes the next commit adding support for failing blinded HTLCs in said method easier to read.
If an HTLC fails after its RAA is processed, it is failed back with ChannelManager::fail_htlc_backwards_internal. This method will now correctly inform the channel that this HTLC is blinded and to construct an update_malformed message accordingly.
If a blinded recipient to a multihop blinded path needs to fail back a malformed HTLC, they should use error code INVALID_ONION_BLINDING and a zeroed out onion hash per BOLT 4.
And use it in the multihop blinded path receive failure test. Will be used in the next commit to test receiving an invalid blinded final onion payload. We can't use the existing get_route test util here because blinded payments rely on the sender adding a random shadow CLTV offset to the final hop; without this the payment will be failed with cltv-expiry-too-soon.
If a recipient behind a multihop blinded path fails to decode their onion payload, they should fail backwards with error code INVALID_ONION_BLINDING and a zeroed out onion hash per BOLT 4.
If a blinded HTLC does not satisfy the receiver's requirements, e.g. bad CLTV or amount, they should malformed-fail backwards with error code INVALID_ONION_BLINDING and a zeroed out onion hash per BOLt 4.
If a blinded HTLC errors when added to a Channel, such as if the recipient has already sent a shutdown message, they should malformed-fail backwards with error code INVALID_ONION_BLINDING and a zeroed out onion hash per BOLT 4.
.. contained within their encrypted payload.
Because we now support receiving to multi-hop blinded paths.
Although this new check is unreachable right now, it helps prevent potential future errors where we incorrectly fail blinded HTLCs with an unblinded error.
in Channel and ChannelManager.
643cf93
to
ecd8238
Compare
Squashed the fixup and added another commit addressing #2688 (comment). Sorry if I'm being dense on DRYing the match arms, but happy to address that and #2688 (comment) in a follow-up. |
9856fb6
into
lightningdevkit:main
…followups Follow-ups to #2688
Title. Currently, we only support receiving to 1-hop blinded paths. Diff is largely error handling.
Based on #2540.