-
Notifications
You must be signed in to change notification settings - Fork 385
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
Add support for custom HTLC TLVs #2308
Add support for custom HTLC TLVs #2308
Conversation
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.
A couple questions for reviewers:
87ec6b0
to
4cb0a4b
Compare
4cb0a4b
to
07a9910
Compare
Codecov ReportPatch coverage:
❗ Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please install the Github App Integration for your organization. Read more. Additional details and impacted files@@ Coverage Diff @@
## main #2308 +/- ##
==========================================
+ Coverage 90.24% 90.58% +0.34%
==========================================
Files 106 106
Lines 55817 59617 +3800
Branches 55817 59617 +3800
==========================================
+ Hits 50370 54007 +3637
- Misses 5447 5610 +163
☔ View full report in Codecov by Sentry. |
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.
Sorry for the delay here, just getting caught up post-travels.
07a9910
to
3d848a6
Compare
3483289
to
59f3fb6
Compare
a7a2162
to
ad4376e
Compare
Rebased due to conflicts. |
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.
This basically looks good, I think, needs another reviewer.
751c245
to
a0d10a3
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.
Looks good! Since a user can now include arbitrary data, do we know if the router can handle restricting the number of hops based on the existing recipient payload?
It currently cannot, though this is an issue that was really introduced in payment metadata, this just makes it worse. Tracked at #2201. |
Feel free to squash fixups when you next push, I think. |
a0d10a3
to
fdbcbff
Compare
93a8fb6
to
e8eedd3
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.
LGTM
e8eedd3
to
9a77843
Compare
Fixed and squashed immediately since they were just small nits |
yes, though i’m not sure no returning error is workable on the long-term due to onion bandwidth cost and payment reliability (at least end-to-end), though here more conversation at the spec-level. answered on the two comments:
though overall i won’t insist and if / when we have users privacy screwed up on those vectors, i’ll just pointed it back to you guys :) |
here the code is good in fact after looking (though doc confusing), just good if we don’t change it in the future to match BOLT4 bad suggestion imho |
9a77843
to
1db481f
Compare
Squashed immediately for minor doc change: diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 968b0765..daa0b34f 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -371,7 +371,7 @@ pub enum FailureCode {
/// We failed to process the payload after the onion was decrypted. You may wish to
/// use this when receiving custom HTLC TLVs with even type numbers that you don't recognize.
///
- /// If available, the tuple data should include the type number and byte offset in the
+ /// If available, the tuple data may include the type number and byte offset in the
/// decrypted byte stream where the failure occurred.
InvalidOnionPayload(Option<(u64, u16)>),
} |
Happy after following diff applied,
|
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.
Code Review ACK 1db481f
Nevermind the above comment effectively multi-payer scheme would need additional code changes in our MPP validation and as such wider custom HTLC support (though they’re the ones more exposed in term of potential deanonymization attacks)
FailureCode::InvalidOnionPayload(data) => { | ||
let fail_data = match data { | ||
Some((typ, offset)) => [BigSize(typ).encode(), offset.encode()].concat(), | ||
None => Vec::new(), |
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.
nit: I think the debug_assert(data.is_none())
could be added due to its only usage in claim_payment_internal
with a None
value given to InvalidOnionPayload
.
Grrr, needs rebase, sorry about that. |
Custom TLVs allow users to send extra application-specific data with a payment. These have the additional flexibility compared to `payment_metadata` that they don't have to reflect recipient generated data provided in an invoice, in which `payment_metadata` could be reused. We ensure provided type numbers are unique, increasing, and within the experimental range with the `RecipientOnionFields::with_custom_tlvs` method. This begins sender-side support for custom TLVs.
When serialized, the TLVs in `OutboundOnionPayload`, unlike a normal TLV stream, are prefixed with the length of the stream. To allow a user to add arbitrary custom TLVs, we aren't able to communicate to our serialization macros exactly which fields to expect, so this commit adds new macro variants to allow appending an extra set of bytes (and modifying the prefixed length accordingly). Because the keysend preimage TLV has a type number in the custom type range, and a user's TLVs may have type numbers above and/or below keysend's type number, and because TLV streams must be serialized in increasing order by type number, this commit also ensures the keysend TLV is properly sorted/serialized amongst the custom TLVs.
This completes basic receiver-side support for custom TLVs and adds functional testing for sending and receiving.
Upon receiving multiple payment parts with custom TLVs, we fail payments if they have any non-matching or missing even TLVs, and otherwise just drop non-matching TLVs if they're odd.
When a user decodes custom TLVs, if they fail to recognize even type numbers they should fail back with the correct failure code and fail data. This new variant adds the proper failure variant for the user to pass into `ChannelManager::fail_htlc_backwards_with_reason`. Note that the enum discriminants were removed because when adding a struct variant we can no longer make use of the discriminant through casting like we previously did, and instead have to manually define the associated failure code anyway.
Because we don't know which custom TLV type numbers the user is expecting (and it would be cumbersome for them to tell us), instead of failing unknown even custom TLVs on deserialization, we accept all custom TLVs, and pass them to the user to check whether they recognize them and choose to fail back if they don't. However, a user may not check for custom TLVs, in which case we should reject any even custom TLVs as unknown. This commit makes sure a user must explicitly accept a payment with even custom TLVs, by (1) making the default `ChannelManager::claim_funds` fail if the payment had even custom TLVs and (2) adding a new function `ChannelManager::claim_funds_with_known_custom_tlvs` that accepts them. This commit also refactors our custom TLVs test and updates various documentation to account for this.
1db481f
to
dec3fb3
Compare
Rebased |
} | ||
|
||
#[cfg(debug_assertions)] { | ||
let mut last_seen: Option<u64> = None; |
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.
it'd be nice to have this in one macro body with the above non-custom TLVs so that we can debug_assert!()
that the ordering including custom TLVs is correct.
let tlvs = &mut self.custom_tlvs; | ||
let further_tlvs = &mut further_htlc_fields.custom_tlvs; | ||
|
||
let even_tlvs: Vec<&(u64, Vec<u8>)> = tlvs.iter().filter(|(typ, _)| *typ % 2 == 0).collect(); |
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 should be able to do the comparison here without collect
ing into a Vec
.
let preimage = if let Some(ref preimage) = keysend_preimage { | ||
Some((5482373484, preimage.encode())) | ||
} else { None }; | ||
let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter().chain(preimage.iter()).collect(); |
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 are trying to build an interator, we shouldn't have to collect
here (though the macro may want it twice, so we may have to do the iter().chain()
in the macro argument rather than 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.
Though AFAIK it wouldn't be possible to sort the tlvs as an iterator?
match &purpose { | ||
PaymentPurpose::InvoicePayment { payment_secret, .. } => { | ||
assert_eq!(our_payment_secret, *payment_secret); | ||
assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret); |
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.
nit: just for your reference, if we're copying test util bits into a test, we can usually skip a bunch of the assertions - the test utility methods tend to be a bit verbose and check everything, which is great in the general code, but in an individual test we don't need to test too much more than we care about for the purpose of the specific test.
if let Some(expected_tlvs) = expected_receive_tlvs { | ||
// Claim and match expected | ||
let events = nodes[3].node.get_and_clear_pending_events(); | ||
println!("events: {:?}", events); |
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.
Oops, I missed this before hitting merge, let's do a followup and remove this (and maybe hit a few other nits, though no real pressure on those).
Thanks for all the review everybody, will get a follow up up soon :) |
Closes #1298. This PR adds support for sending and receiving custom HTLC TLVs.
Custom TLVs allow users to send extra application-specific data with a payment. These have the additional flexibility compared to
payment_metadata
that they don't have to reflect recipient generated data provided in an invoice, in whichpayment_metadata
could (probably) be reused.On the send side, a user can provide their serialized TLVs as a
Vec<(u64, Vec<u8>)>
toRecipientOnionFields::with_custom_tlvs
which checks whether the type numbers are unique, increasing, and in the range reserved for custom values. They'll then pass this into whichever send payment function they use, it'll be serialized in the onion payload, and sent with the payment.On the receive side, when deserializing the onion payload, we just add the bytes back into this type, then pipe it from the onion through to
Event::PaymentClaimable
, dropping non-matching TLVs between payment parts.