-
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
intercept htlcs #1601
intercept htlcs #1601
Conversation
b3d8288
to
dfa45d6
Compare
lightning/src/ln/channelmanager.rs
Outdated
new_events.push(events::Event::PaymentIntercepted { | ||
short_channel_id: short_chan_id, | ||
payment_hash, | ||
inbound_amount_msats: 0, |
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 best way to get this or if it's really that important to surface it? Does it need to be piped through from earlier steps?
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, hmmm, yea, that's annoying. We'll have to add it to HTLCForwardInfo::AddHTLC
, I guess. I think we can still cheat and make the Event
non-Option
al - in general if a user is using a new feature its okay if it doesnt work if they downgrade to an old LDK. If an AddHTLC
is missing the previous_htlc_amount
we can just fail the HTLC.
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 that makes sense. we can fail it safely because if it's missing that attribute it means it was created on a version that didn't support interception, right?
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.
Yep!
if let Some(_payment) = pending_intercept { | ||
// TODO: what's best way to fail this? in `process_pending_htlc_forwards` it uses that `fail_forward` macro | ||
// awkward way could be to just use the forward_intercepted_payment code and pick a random scid | ||
} |
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.
best way to handle this? see 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.
You'll have to move the process_pending_htlc_forwards
fail_forward
macro out into a util method, constructing the HTLCSource
object and call fail_htlc_backwards_internal
.
lightning/src/ln/channelmanager.rs
Outdated
|
||
Ok(()) | ||
}, | ||
_ => Err(APIError::APIMisuseError { err: "impossible".to_string() }) |
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.
Not sure how to handle this exactly. It should be impossible to get into this state.
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.
Two options: (a) move the contents of HTLCForwardInfo::AddHTLC
into a struct and just store that struct here, or (b) just unreachable
it. (b) kinda sucks but its less of a pain touching a bunch of code.
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.
with a) i assume when you say "store that struct here" you mean in pending_intercepted_payments?
b) does kind of suck but I guess would you accept a PR that just did that?
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'd survive (b), but you can also do (a) in a separate PR. Indeed, I meant in the new field for tracking intercepted payments.
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.
went for a
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.
Wait, did you? I intepreted (a) as also implying that we should make HTLCForwardInfo::AddHTLC
be AddHTLC(NewStruct)
.
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 whoops, i misunderstood. i thought you meant create a new separate struct that has all the info, not literally store the struct in HTLCForwardInfo::AddHTLC.
ok I can try to refactor it to what you meant.
lightning/src/ln/channelmanager.rs
Outdated
}); | ||
}, | ||
hash_map::Entry::Occupied(_) => { | ||
fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new(), 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.
do we want to fail with this same error message in 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.
The error code itself should remain, but the text isn't sent to peers, its just printed locally, so we can add more detail there.
823f566
to
2557243
Compare
Codecov ReportBase: 90.73% // Head: 90.62% // Decreases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## main #1601 +/- ##
==========================================
- Coverage 90.73% 90.62% -0.11%
==========================================
Files 87 87
Lines 46713 46791 +78
Branches 46713 46791 +78
==========================================
+ Hits 42383 42403 +20
- Misses 4330 4388 +58
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. |
b2b0f6f
to
312b7cb
Compare
lightning/src/ln/channelmanager.rs
Outdated
@@ -1432,7 +1463,7 @@ macro_rules! handle_chan_restoration_locked { | |||
let chanmon_update_is_none = chanmon_update.is_none(); | |||
let counterparty_node_id = $channel_entry.get().get_counterparty_node_id(); | |||
let res = loop { | |||
let forwards: Vec<(PendingHTLCInfo, u64)> = $pending_forwards; // Force type-checking to resolve | |||
let forwards: Vec<(PendingHTLCInfo, u64, Option<u64>)> = $pending_forwards; // Force type-checking to resolve |
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.
Now that you've threaded it through everywhere - does it not make more sense to add this as a field in PendingHTLCInfo
? It looks like that's constructed in the receive pipeline so we still have the amount field, then you shouldn't need to touch channel.rs
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 i add it to PendingHTLCInfo then I wouldn't also have to thread through this extra Option in the tuple, right?
It does seem to make sense to just put it in there. it will still need to be an Option because they're serialized as part of pending forwards, right?
There's sort of another weirdness:
seems like in the Receive case, we are setting amt_to_forward to the 'prev/incoming amt' which ends up being fine because the logic differs for receives even though it's the same object.
i guess if i add this new field, would we want to update the receive logic as well to use it? though old PendingHtlcInfo's will still have the forward field set. so we'd probably just want to store the incoming amount in both fields.. which is going to look pretty confusing.
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 i add it to PendingHTLCInfo then I wouldn't also have to thread through this extra Option in the tuple, right?
Yep.
it will still need to be an Option because they're serialized as part of pending forwards, right?
Yep.
i guess if i add this new field, would we want to update the receive logic as well to use it? though old PendingHtlcInfo's will still have the forward field set. so we'd probably just want to store the incoming amount in both fields.. which is going to look pretty confusing.
Probably just use the existing field (and maybe add a debug_assert that they're equivalent, if the new one is Some
) - dealing with backwards compat to switch to a new field just sounds annoying. A debug_assert is self-documenting.
lightning/src/ln/channelmanager.rs
Outdated
@@ -6527,6 +6654,7 @@ impl_writeable_tlv_based_enum!(HTLCForwardInfo, | |||
(2, prev_short_channel_id, required), | |||
(4, prev_htlc_id, required), | |||
(6, prev_funding_outpoint, required), | |||
(8, prev_htlc_amount, option) |
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.
New fields generally need to be odd - this allows older versions of LDK to simply ignore the field when reading instead of it causing a read error.
d9e0c45
to
c027a24
Compare
intercept_id.write(writer)?; | ||
pending_intercepted_payment.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.
not sure where exactly this should be written. seemed like last was a good idea.
lightning/src/ln/channelmanager.rs
Outdated
return Err(DecodeError::InvalidValue); | ||
} | ||
} | ||
|
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.
tried to read it last as well but the reader var gets moved into the read_tlv_fields! for outbound_payments below. just put it here for now so it compiles but I'm pretty sure this won't work.
f98ae49
to
28870ff
Compare
ok moved the incoming amount to PendingHTLCInfo and now channel.rs is untouched. I also attempted to update the ChannelManager read/write but I'm not entirely confident there. I basically just added it to the end 🤷♂️ let me know what else might be missing when you get a chance. |
@@ -3055,6 +3088,63 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana | |||
Ok(()) | |||
} | |||
|
|||
/// Fails the intercepted payment indicated by intercept_id. This should really only be called in response | |||
/// to a PaymentIntercepted event |
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.
Can you link the event with [` and `]
match pending_intercept { | ||
None => Err(APIError::APIMisuseError { err: "Payment with that InterceptId not found".to_string() }), | ||
Some(payment) => { | ||
let routing = match payment.forward_info.routing { |
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: spurious indentation here.
lightning/src/ln/channelmanager.rs
Outdated
|
||
Ok(()) | ||
}, | ||
_ => Err(APIError::APIMisuseError { err: "impossible".to_string() }) |
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.
Wait, did you? I intepreted (a) as also implying that we should make HTLCForwardInfo::AddHTLC
be AddHTLC(NewStruct)
.
lightning/src/ln/channelmanager.rs
Outdated
@@ -131,6 +132,13 @@ pub(super) enum PendingHTLCStatus { | |||
Fail(HTLCFailureMsg), | |||
} | |||
|
|||
pub(super) struct PendingInterceptedHTLC { |
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.
Are we gonna meke HTLCForwardInfo::AddHTLC
just hold this (and maybe rename it).
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, my bad. agreed we should rename it if so.
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.
Looking at this now I'm a little confused. Does HTLCForwardInfo::AddHTLC need to "hold" this struct? They are literally exactly the same already. Was this not the case back in July when this was first written?
Unless I'm misremembering something couldn't I just get rid of this PendingInterceptedHTLC struct entirely and just store (or clone) the entire HTLCForwardInfo as the intercepted htlc instead of essentially copying it into this new struct?
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 it would mean matching on HTLCForwardInfo anywhere I need to use it and ignoring/erroring if we came across a ::FailHTLC when we expected an ::AddHTLC.
Do you know what you meant by hold it when it's already the same data?
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 whoops. There's a separate comment thread in here about this. You meant change it to HTLCForwardInfo::AddHTLC(RenamedPendingInterceptedHTLC).
Is the reason to update the AddHTLC variant to hold a struct instead of using named fields so that when I need to store the "pending intercepted htlcs" I can take (or clone) this inner struct instead of having to store the entire HTLCForwardInfo? Thus also eliminating the need to match against it.
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.
So I starting working on this and it's a bit of a pain so I wanted to make sure it still makes sense to do this before I continue.
A couple things I wanted to highlight or questions I have about it if we do want to move forward with that idea:
- There's a lot of places where you match on HTLCForwardInfo::AddHTLC { fields...} and do a nested match on the PendingRoutingHTLC::Forward variant at the same time. I was doing this instead in the new version:
// destructuring to avoid having to write `pending_add_htlc_info.<field_name>` everywhere
let PendingAddHTLCInfo {
prev_short_channel_id,
prev_htlc_id,
forward_info: PendingHTLCInfo {
routing,
incoming_shared_secret,
payment_hash,
amt_to_forward,
outgoing_cltv_value,
..
},
prev_funding_outpoint
} = pending_add_htlc_info;
// instead of the nested match, putting the whole arm inside this if let instead :/
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
...
}
Is there a cleaner way to do this?
-
How can I insert the PendingAddHTLCInfo (what I renamed it for now) into my map without cloning it? Is it possible? Is it okay if I have to clone it to insert it? Otherwise it's fields get used / thus moved before I need to insert.
-
Is there an issue with serialization / backwards compat? The old enums will be serialized with the ::AddHTLC struct variant and the new version will want to deserialize with this tuple / nested struct version. Is this okay?
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.
Is the reason to update the AddHTLC variant to hold a struct instead of using named fields so that when I need to store the "pending intercepted htlcs" I can take (or clone) this inner struct instead of having to store the entire HTLCForwardInfo? Thus also eliminating the need to match against it.
Yea, basically.
Is there a cleaner way to do this?
Yes, you can directly match on the whole thing. This should work fine:
match enum_a {
EnumA::VariantA(VariantAStruct { inner_enum: EnumB::VariantA { ... }, ... } => {}
}
How can I insert the PendingAddHTLCInfo (what I renamed it for now) into my map without cloning it? Is it possible?
I'll have to look at the final code, but for a first draft just clone it.
Is there an issue with serialization / backwards compat?
Yes, we're going to have to look at this closely. But having to hand-write some serialization glue isn't a good reason to avoid cleaning up the logic to be what we want IMO. Happy to help write that glue.
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.
Ok sounds good, just wanted to make sure it made sense before continuing down this path. Thanks!
lightning/src/ln/channelmanager.rs
Outdated
phantom_shared_secret: None, | ||
}); | ||
|
||
let failure_reason = HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: 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.
Actually, on second thought, lets use temporary_channel_failure
here - x4000|10 means "the channel you tried to send to is bogus and will never exist", which is probably true in most cases, but the user may just have failed to talk to the client to open the JIT channel or whatever, so we may not want to do that.
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.
Actually, we might extend fail_intercepted_payment
to return a custom data message from the client ?
PendingHTLCRouting::Forward { onion_packet, .. } => { | ||
PendingHTLCRouting::Forward { onion_packet, short_channel_id: scid } | ||
}, | ||
_ => payment.forward_info.routing |
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 need some clarity from the LSP spec folks on if this suffices - can we forward the existing onion or do they want to demand that we support unwrapping the onion differently. Id strongly prefer we keep this, but it may not suffice depending on what users want to do. At a minimum this needs to be documented.
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, my gut is that we might as well support it. I assume this means surfacing the original onion in the event in addition to being able to pass a new one here, right?
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 there's any value in surfaacing the existing onion - its encrypted for the final recipient so its not like we can do anything with it. I suppose we can add a "create new onion" option as a followup, but I'd kinda like to understand where the LSP folks are gonna go before we get that far - the other thing is the LSP folks may find mpp to be important, in which case this whole PR is probably not useful - the LSP will be the one to create the invoice, will receive the payment as a "normal" payment, then the LSP dev can open a channel, call send_payment
with the same payment hash and call it a day, no need for the complexity here [1].
[1] Well, okay, we still need to probably actually support that, cause we have to bubble payment preimages through even if both channels go offline, but it will merit an entirely different approach than this PR.
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 right, that makes sense.
weren't there other non-lsp use-cases mentioned in here ( #680 ) for a feature like this?
also it seems like this can still be useful for non-mpp supporting lsp. i guess it really comes down to what trade-offs are being made with the mpp supporting lsp and if there's a strong case to support both flows.
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.
Hmm, not sure what you mean by the 680 reference here - I think that's ultimately related to this but only in a "code cleanup" kinda way, not in any actual behavior changes.
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 i guess i misunderstood 680 as well. i thought it alluded to being able surface htlcs to end-user similarly to this 'intercept' pr to enable them to add additional relay logic at the user-level.
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 mean, yea, it did talk about that, but its not ultimately required here.
..payment.forward_info | ||
}; | ||
|
||
let mut per_source_pending_forward = vec![(payment.prev_short_channel_id, payment.prev_funding_outpoint, vec![(pending_htlc_info, payment.prev_htlc_id)])]; |
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.
The outer vec can just be a []
, doesn't need to be a vec.
_ => payment.forward_info.routing | ||
}; | ||
|
||
let pending_htlc_info = PendingHTLCInfo { |
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.
Once you move ::AddHTLC
to use the same struct it'd be nice to DRY this up with other forwarding.
@@ -6696,6 +6833,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f | |||
(11, self.probing_cookie_secret, required), | |||
}); | |||
|
|||
(pending_intercepted_payments.len() as u64).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.
We can't add new fields at the end, they have to go in the TLVs.
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 that makes sense, whoops.
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.
Could be nice to have a look on the pending or future spec objects which would require or be easier to implement with such HTLC intercept interface (e.g trampoline or lightning/bolts#1008). To check if we fit them well.
lightning/src/ln/channelmanager.rs
Outdated
loop { | ||
let scid_candidate = fake_scid::Namespace::Intercept.get_fake_scid(best_block.height(), &self.genesis_hash, &self.fake_scid_rand_bytes, &self.keys_manager); | ||
// Ensure the generated scid doesn't conflict with a real channel. | ||
match channel_state.short_to_id.entry(scid_candidate) { |
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.
Btw, I wonder if this reorg-robust. At block N, we have real channel funding transaction X confirmed at index 2. We draw out the scid N:3:4, we're good. Block N gets reorged-out and real channel funding transaction X get confirmed anew at index 3. Now I think we have a conflict ?
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 only generate fake scids up to 144 blocks before our highest known height IIUC
prev_funding_outpoint, | ||
}; | ||
entry.insert(pending_intercepted_payment); | ||
new_events.push(events::Event::PaymentIntercepted { |
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 I think we might have an issue here with the way we're implementing BOLT4 routing policy checks. Currently, we implement those checks even before to accept the inbound HTLC in internal_update_add_htlc()
. From my memory, the spec does not mandate the order of the checks and it's not problematic as long as we don't provide an internal interface to manipulate relayed HTLCs. As we aim to do so with current PR, it might a source of confusion for the consumer, where additional routing policy checks or LSP callback actions are offered through the new PaymentIntercepted
. I'm thinking of the "additional" routing policy checks could be even to turn off some of the default check (e.g bypassing CLTV_FAR_FAR_AWAY
in the context of channel jamming mitigations experimentations or whatever).
So I don't know if the approach as suggested #680 could make more sense.I don't hold specifically to this approach, I just care it covers all the near-term use-cases we have mind.
lightning/src/ln/channelmanager.rs
Outdated
phantom_shared_secret: None, | ||
}); | ||
|
||
let failure_reason = HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: 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.
Actually, we might extend fail_intercepted_payment
to return a custom data message from the client ?
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.
Nice, seems like a very useful feature to have indeed! Left some suggestions on a few things I noticed when looking through this, other than whats been raised by the other reviewers already.
}; | ||
|
||
let mut per_source_pending_forward = vec![(payment.prev_short_channel_id, payment.prev_funding_outpoint, vec![(pending_htlc_info, payment.prev_htlc_id)])]; | ||
self.forward_htlcs(&mut per_source_pending_forward); |
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.
Hmmm wouldn't we want to check that there's actually been a channel opened for the scid before we try forwarding over it by checking that there now exists an entry for the scid in the short_to_id
map (now short_to_chan_info
), and return an error if there's no such channel?
Else we will just end up generating a new PaymentIntercepted
event for the same fake intercept scid over and over again (for every forward_intercepted_payment
try) once we try to forward the htlc, which I think will be confusing for the user as no error will occur (given that I've followed the code correctly).
Edit: Ah I realized now that the scid
in the forward_intercepted_payment
parameters is of course ment to be another than the short_channel_id
in the PaymentIntercepted
event. Perhaps this should be clarified in the docs to avoid confusion :). I still think it would make sense to include a short_to_chan_info
key check in this function though, to give a clear message to the user rather than failing the forward.
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.
Added this in #1835, good catch IMO!
@@ -3055,6 +3088,63 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana | |||
Ok(()) | |||
} | |||
|
|||
/// Fails the intercepted payment indicated by intercept_id. This should really only be called in response | |||
/// to a PaymentIntercepted event | |||
pub fn fail_intercepted_payment(&self, intercept_id: InterceptId) { |
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 we'd like to return a Result<(), APIError>
for this API function, similar to our other API functions (such as for cases when the pending_intercept
didn't exists etc) :)?
Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, vec![(info, prev_htlc_id)])), | ||
Err(ReceiveError { err_code, err_data, msg }) => fail_forward!(msg, err_code, err_data, Some(phantom_shared_secret)) | ||
} | ||
}, | ||
_ => panic!(), | ||
} | ||
} else if forward_info.amt_incoming.is_some() && fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, short_chan_id) { |
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 thought of a potential attack vector here that I wanted to raise in the review club earlier today, but unfortunately we didn't have time to go over the question about such vectors:
Shouldn't we check here that the fake_scid
is a scid
that we have actually generated, and not only that it's within the Intercept
namespace?
Else, if another peer is able to generate a fake_scid
that we determine to be in the Intercept namespace but that we didn't generate ourselves, we face 2 issues:
-
Since the
PaymentIntercepted
event is serialized for peers we have no active channel to (and isn't dropped on disconnect like other events for that type of peers), as well asPendingInterceptedHTLC
that aren't handled, random peers we have no channels to could potentially be able cause extensive serialization of huge amount of events/structs. -
Probably even worse, just one successful
fake_scid
attack by a random peer that passes theis_valid_intercept
, will serialize thePaymentIntercepted
event (which according to comments above may be a required field) +PendingInterceptedHTLC
(which has only required fields) for any LDK user, even users that didn't explicitly choose to use the new Intercept HTLC feature. That will break the backwards compatibility for such users, hence opening up for a backwards compatibility attack.
Looking at the implemention for fake_scids
+ namespaces
, it shouldn't be too expensive for random peers to brute force a fake_scids
that we will accept as a valid Intercept
fake_scid
?
I think we therefore also need to add a way to track which fake_scids
we have actually generated to not face this issue? I guess one alternative solution to just solve issue (2) would also be to add a config flag for enabling Intercept HTLC
support, which defaults to false
.
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.
Good points, thanks!! I started a new discussion on the superceding PR here: #1835 (comment).
Re: (1) I'm not sure they could force us to store a ton of stuff since the payments would presumably be failed back as they come in by users that don't support intercept scid forwards (maybe the docs on the event should clarify that you MUST handle it)?
28870ff
to
c42f450
Compare
Let me know when you want additional rounds of review here! |
Thanks, I just squashed all the old commits to ease the rebase. I'm going to review what I was doing and the last round of comments here. I think there's a few things that were never addressed. |
This PR is kind of a mess with (outdated) comments already but would love feedback on these questions: https://github.com/lightningdevkit/rust-lightning/pull/1601/files#r987995094 |
c42f450
to
e421b59
Compare
Are we waiting on other contributors to respond to @johncantrell97's questions? |
Not at the moment. Think I could use a hint from matt about what existing routing code can be dry'd up now that I moved the new struct into the existing enum. |
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 LGTM, could use some commit separation and a test, though. I don't remember exactly what this code looked like before, but I find it quite nice that we're not adding a new type.
AddHTLC { | ||
forward_info: PendingHTLCInfo, | ||
#[derive(Clone)] | ||
pub(super) struct PendingAddHTLCInfo { |
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.
Can we make this refactor in a separate commit?
}; | ||
|
||
let mut per_source_pending_forward = vec![(payment.prev_short_channel_id, payment.prev_funding_outpoint, vec![(pending_htlc_info, payment.prev_htlc_id)])]; | ||
self.forward_htlcs(&mut per_source_pending_forward); |
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.
Ideally we'd actually just do the forward right away rather than waiting a full forwarding-timer-tick. Its not really a huge deal, but waiting the full timer tick kinda sucks for timing analysis reasons.
@@ -91,6 +92,7 @@ pub(crate) mod fake_scid { | |||
pub(crate) enum Namespace { | |||
Phantom, | |||
OutboundAlias, | |||
Intercept |
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.
Can we add this variant in a separate commit.
What's the next step here? |
I still think we should check if this proposed intercept HTLCs interface answers well the use-cases we have in mind such as trampoline/offline receive/jamming mitigation strategies and others. I still think there are previous review conceptual comments not addressed. |
I think the one done here implements it mostly. There are some cases where we want the user to be in-path for all htlc forwards, but I think we can reuse most of the infrastructure here for that. The nice thing about this PR is it gives user control of how much they want to see/control and how much they want LDK to "do for them". A similar concern applies to trampoline - we really want LDK to just "do it for users", its dumb to make users write a lot of boilerplate just to implement trampoline when there's an exact way they have to do it. So, I think we should move this PR forward if possible (@johncantrell97 ?), and we can go from there and add additional extensibility in HTLC forwarding. |
FYI @TheBlueMatt, as discussed offline with @johncantrell97, I'm almost done with a PR that supercedes this one (containing all of this PR's changes + tests) |
Superceded by #1835. I think all the feedback here has been addressed but lmk if something's missing |
This PR aims to add a new fake_scid Namespace (Namespace::Intercept) that allows htlcs forwarded with scids from this namespace to be surfaced to end-users via a new event (PaymentIntercepted). The end-user must then either fail using
fail_intercepted_payment
or forward it usingforward_intercepted_payment
methods on ChannelManager.The main use-case in mind is for enabling on-demand channel creation though there are likely other interesting use-cases that are also enabled by it.
I need help with updating serialization (read/write) for ChannelManager because we will need to add a new HashMap for storing the intercepted payments that we are waiting on the end-user to tell us what to do with them.
I'm also not sure the best way to fail these. Please see the note in
fail_intercepted_payment
about one thought I had.I'm also not exactly sure what other functionality might be missing. I added ability to generate a fake intercepter scid on the channel manager but there's nothing with route hints yet. I'm not sure if it makes sense to add that to LDK or if it should be the end-user's responsibility because we can't be sure what they're doing with them? I guess they'll always at least want to include the fake_scid as the last hop but they'll likely want to specify a hop before that as well -- which in the on-demand channel / lsp use-case ldk won't know about this node.
Lastly, I wrote about this on discord but I'm not sure what is needed in terms of signaling CM persistence when htlcs are drained from forward_htlcs => pending_intermittent_payments and then again moved back into foward_htlcs once signaled from user. Feels like it's possible we lose an htlc in one of these transitions if the node is killed.