@@ -74,6 +74,11 @@ type RegistryConfig struct {
7474 // KeysendHoldTime indicates for how long we want to accept and hold
7575 // spontaneous keysend payments.
7676 KeysendHoldTime time.Duration
77+
78+ // HtlcModifier is a service that intercepts invoice HTLCs during the
79+ // settlement phase, enabling a subscribed client to modify certain
80+ // aspects of those HTLCs.
81+ HtlcModifier HtlcModifier
7782}
7883
7984// htlcReleaseEvent describes an htlc auto-release event. It is used to release
@@ -998,6 +1003,53 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
9981003 )
9991004
10001005 callback := func (inv * Invoice ) (* InvoiceUpdateDesc , error ) {
1006+ // Provide the invoice to the settlement interceptor to allow
1007+ // the interceptor's client an opportunity to manipulate the
1008+ // settlement process.
1009+ clientReq := HtlcModifyRequest {
1010+ ExitHtlcCircuitKey : ctx .circuitKey ,
1011+ ExitHtlcAmt : ctx .amtPaid ,
1012+ ExitHtlcExpiry : ctx .expiry ,
1013+ CurrentHeight : uint32 (ctx .currentHeight ),
1014+ Invoice : * inv ,
1015+ }
1016+ interceptSession := i .cfg .HtlcModifier .Intercept (
1017+ clientReq ,
1018+ )
1019+
1020+ // If the interceptor service has provided a response, we'll
1021+ // use the interceptor session to wait for the client to respond
1022+ // with a settlement resolution.
1023+ var interceptErr error
1024+ interceptSession .WhenSome (func (session InterceptSession ) {
1025+ log .Debug ("Waiting for client response from invoice " +
1026+ "HTLC interceptor session" )
1027+
1028+ select {
1029+ case resp := <- session .ClientResponseChannel :
1030+ log .Debugf ("Received invoice HTLC interceptor " +
1031+ "response: %v" , resp )
1032+
1033+ if resp .AmountPaid != 0 {
1034+ ctx .amtPaid = resp .AmountPaid
1035+ }
1036+
1037+ case err := <- session .ClientErrChannel :
1038+ log .Errorf ("Error from invoice HTLC " +
1039+ "interceptor session: %v" , err )
1040+
1041+ interceptErr = err
1042+
1043+ case <- session .Quit :
1044+ // At this point, the interceptor session has
1045+ // quit.
1046+ }
1047+ })
1048+ if interceptErr != nil {
1049+ return nil , fmt .Errorf ("error during invoice HTLC " +
1050+ "interception: %w" , interceptErr )
1051+ }
1052+
10011053 updateDesc , res , err := updateInvoice (ctx , inv )
10021054 if err != nil {
10031055 return nil , err
@@ -1051,6 +1103,8 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
10511103
10521104 var invoiceToExpire invoiceExpiry
10531105
1106+ log .Tracef ("Settlement resolution: %T %v" , resolution , resolution )
1107+
10541108 switch res := resolution .(type ) {
10551109 case * HtlcFailResolution :
10561110 // Inspect latest htlc state on the invoice. If it is found,
@@ -1183,7 +1237,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
11831237 }
11841238
11851239 // Now that the links have been notified of any state changes to their
1186- // HTLCs, we'll go ahead and notify any clients wiaiting on the invoice
1240+ // HTLCs, we'll go ahead and notify any clients waiting on the invoice
11871241 // state changes.
11881242 if updateSubscribers {
11891243 // We'll add a setID onto the notification, but only if this is
0 commit comments