-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
[htlcnotifier 3/4]: Add HTLC Notifier #3781
[htlcnotifier 3/4]: Add HTLC Notifier #3781
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.
First pass done, I like the direction this is going. My two main thoughts:
- Push the refactor of
ForwardingError
further. Get rid ofExtraMsg
completely and make settingFailureDetail
mandatory when instantiating theForwardingError
, possibly by creating a constructor function that takes bothFailureMessage
andFailureDetail
. Otherwise it remains a bit loose and bugs may sneak in. - Check carefully whether the notification interface is consistent. I am a little worried about mixing up fail+settle (link level) with forwarding (two links involved), but maybe there is no reason to be worried. I think that going through the various scenarios and validating that the produced event(s) are logical will help (if you haven't already done so)
Running through some different scenarios, as suggested by @joostjager, to make sure we can distinguish between different types of events. ✅ = Value set ; ❌ = Value not set Forward Events: Ok
Forward Failure: Ok
Settle Event: Potential overlap
In the ideal case, we anything with a zero outgoing key belonging to us then check for matching forward events to determine whether the settle is for a send or receive. However, we will not have a forward event for sends which were forwarded before restart and settled afterwards. Link Failures: Overlap
For this set of events, there is an overlap issue between forwarding failures which occur on the incoming link and failures paying our own invoices. Receives have a specific set of failure details, which we could use to distinguish, but that is not forward thinking because we may have a failure which is common for receives and forwards. Solution: |
With regards to the event sequences, would it make sense to have only a single event per htlc as it is handled by our system? So a forward will generate just a single even, even though there are an incoming and an outgoing link involved. Or is that what you suggest with the send/receive/fwd event type? |
As is we have a single event for a forward, which has the information for the incoming and outgoing links in Incoming exit hop htlc rejected Incoming exit hop htlc settled Incoming exit hop htlc accepted but not yet resolved (hodl invoice), then settled Incoming exit hop htlc accepted but not yet resolved (hodl invoice), then rejected Outgoing origin htlc (our payment) not handed out (didn't even leave the link) Outgoing origin htlc (our payment) failed back by peer Outgoing origin htlc (our payment) settled Incoming htlc to forward not accepted (assuming this is on incoming link?): Incoming htlc to forward accepted, but rejected by outgoing link Htlc forwarded, but failed back by downstream peer Htlc forwarded and settled
A type enum would go in the settle events to distinguish our own sends being settled from our receives (since they have the same data right now). Unrelated to the four main event types. I will update the PR with this today, which will hopefully clarify what I mean by it. |
Ok, nice overview. I see that there are multiple events fired for a single forward, one for the forward-going path and one for the backward path. This is also the case for an outgoing origin htlc. For incoming there is only one event. Maybe it is more consistent to always do two events and cover the hodl invoice accept too? So one event that describes what is requested from us by the htlc metadata and the second event what happened to the request. Btw, don't forget the on-chain resolution flow where htlc decision are made too. |
PR needs some work adding on chain events, not ready for review right now. |
0f81309
to
49a2f90
Compare
as specified here: #3420 |
49a2f90
to
daf27ce
Compare
This is a little tricky because the point of One approach for this would be to implement an internal error interface
Updated in this version. There is some ambiguity for certain events (eg an incoming link failure for a forward and an invoice look the same), so I have gone with a event type enum which indicates whether an event is linked to a send/receive/forward. This makes it clear to the user, and is easily implemented because forwards and our own sends/receives are mostly handled by different code. |
Requesting review on this, although I suspect there's more discussion to be had around |
htlcswitch/htlcnotifier.go
Outdated
HtlcKey: newHtlcKey(pkt), | ||
HtlcInfo: newHtlcInfo(pkt, hash, getEventType(pkt)), | ||
Timestamp: time.Now(), | ||
} |
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 it is much cleaner now that you keep the packet in the link/switch.
htlcswitch/switch_test.go
Outdated
} | ||
|
||
assertCircuit(t, zeroCircuit, settle.IncomingCircuit) | ||
assertCircuit(t, aliceToBob, settle.OutgoingCircuit) |
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.
Some method extraction is possible to reduce duplication
htlcswitch/switch_test.go
Outdated
// checkSuccessful is a function that checks that Alice, Bob and Carol | ||
// have the correct set of event for a payment that is successfully | ||
// routed over Alice -> Bob -> Carol. | ||
func checkSuccessful(t *testing.T, alice, bob, carol <-chan interface{}, |
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 quite a bit of assertion code here. Can't this test be written by just having three (one for every node) lists of fully-specified events that are expected? The actual events are then just read from the channels and deep-equaled with the expectations.
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.
Switched over to a set of results, still quite a lot of setup code, but I think it's better. Less duplication at least.
dc28cdf
to
5bd23a2
Compare
// SettleEvent represents a htlc that was settled. | ||
type SettleEvent struct { | ||
// HtlcKey uniquely identifies the htlc. | ||
HtlcKey |
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 HtlcInfo
here?
// because errors returned down the route are encrypted. | ||
type ForwardingFailEvent struct { | ||
// HtlcKey uniquely identifies the htlc. | ||
HtlcKey |
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 HtlcInfo
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.
Wasnt't it also suggested earlier to merge HlcJey
and HtlcInfo
?
htlcswitch/switch.go
Outdated
case *lnwire.UpdateFailHTLC: | ||
s.cfg.HtlcNotifier.NotifyForwardingFailEvent(key, eventType) | ||
} | ||
|
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.
newline
HtlcEventType: HtlcEventTypeReceive, | ||
Timestamp: ts, | ||
}, | ||
} |
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 set of expectations
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.
echoing @joostjager's question about HtlcKey/HtlcInfo, otherwise this looks good to me now :=
// because errors returned down the route are encrypted. | ||
type ForwardingFailEvent struct { | ||
// HtlcKey uniquely identifies the htlc. | ||
HtlcKey |
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.
Wasnt't it also suggested earlier to merge HlcJey
and HtlcInfo
?
incomingHTLCID: packet.incomingHTLCID, | ||
circuit: packet.circuit, | ||
linkFailure: failure, | ||
sourceRef: packet.sourceRef, |
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: this change could be made its own commit with an explaining commit message. Reason being this is touching existing code, so it would be nice (IMO) to see it in isolation to deem whether it is safe.
@@ -811,6 +782,47 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error { | |||
return nil | |||
} | |||
|
|||
// handleLocalAddHTLC handles the addition of a htlc for a send 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.
nice to do code moves in separate commits 👍
carol *HtlcNotifier) serverOption { | ||
|
||
return func(aliceServer, bobServer, carolServer *mockServer) { | ||
aliceServer.htlcSwitch.cfg.HtlcNotifier = alice |
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 we add the htlc notifier by default when creating the mockServers? It should not interfere with existing test cases,
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 gets a bit more involved, because we then need to pass mocked time into newThreeHopNetwork
(which would probably need to be with functional options like this anyway, so all the other calls don't need to be updated), thread the notifier down to initSwithcWithDB
(more functional opts so we don't need to update every caller) and either expand the htlcNotifier
interface to include Start
/Stop
/SubscribeEvents
or do a type assertion in the test to get the event subscription. If it's not a blocker, I think I'd rather leave it like this?
Some clarification on |
This commit adds a HTLCNotifier to htlcswitch which HTLC events will be piped through to provide clients with subscriptions to HTLC level events. The event types added are forward events (which occur for sends from and forwards through our node), forward failues (when a send or forward fails down the line), settles for forwards or receives to our node and link failures which occur when a htlc is failed at our node (which may occur for a send, receive or foreward).
In this commit, a htlcNotifier interface is added to allow for easy unit testing. Instances of the HtlcNotifier are added to the server, switch and link.
This commit sets more fields on the htlcPacket created to fail adding a htlc packet to the switch for notification purposes. This new data is copied by value from the original packet. The packet is then failed back to the peer that forwarded us the packet, which is handled by handledownstream packet. The values added to the packet are not used in the handling of a failed packet.
This commit adds notifications for htlcs which are forwarded through our node. Forwards are notified when the htlc is added on our ougoing link, settles when we send a settle message to the downstream peer. If a failure occurs, we check whether it occurred at our node, then notify a link or forwarding failure accordingly. Note that this change also adds forward event notifications for sends which are initiated by our node because the handling code for adding a htlc which originates from our node is the same as that for handling forwards. Htlcs for our locally initiated sends have our internal pid set in the incoming htlcs id field, so we extract this value and notify with a zero htlc id to be consistent with receives (which have zero outgoing circuits). Subsequent settles or failures are not noitfied for local sends in this commit, and will be handled in a follow up.
Split handleLocalDispatch into an extra handleLocalAddHTLC function so we can easily notify an error should one occur adding the htlc.
Notify link failures for our own payments. Separate handling code is required for local payment link failures because we do not pass these failures back through the switch (like we do for link failures for forwards), but rather send them straight back to the router. Our own sends have the payment ID saved in the incoming htlc ID of the packet's incoming circuit. This change replaces that value with for the sake of consistent notifying of sends and receives from our node.
Add notifications for local initiated sends settles and forwarding failures. As with link failures, local send settles and forwarding failures are reported directly to the router so must have their own notification handling.
This commit adds link failure notifications for failures which occur on our incoming link. These failures may be receives which we failed or forwards which we could not parse.
5bd23a2
to
c0a4923
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.
It has been quite an evolution to design the event structure and get access to all the required data in a clean way, but I think it was worth it. Not only are those events exposed now, but also technical debt was cleaned up along the way. That will benefit further development in this area.
Final question: did you try running a three hop regtest network and sending some payments back and forth manually with log on trace? As a rough sanity check to see that the log messages make sense.
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.
Very happy about the final outcome of this PR, I agree that the extra effort put into it was worth it in the end. LGTM ✅
Thank you for all the review @halseth and @joostjager! The saga is almost complete 🎢
Indeed, I have a very rich local forwarding node after all the iterations of this PR. Send from Alice -> Bob -> Carol looks like this in logs: Alice:
Bob:
Carol:
|
This PR is change 3 of 4 proposed to add a htlc event notifier to lnd.
1: #3843
2: #3844
3: This PR
4: #3848
Since the PR is dependent on the preceding PRs, it has been rebased on #3844.
Only the last 9 commits are eligible for review.
This PR adds a
htlcnotifier
to thehtlcswitch
which provides a stream of notifications with the following types of events: