@@ -74,6 +74,10 @@ 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+ // HtlcInterceptor is an interface that allows the invoice registry to
79+ // let clients intercept invoices before they are settled.
80+ HtlcInterceptor HtlcInterceptor
7781}
7882
7983// htlcReleaseEvent describes an htlc auto-release event. It is used to release
@@ -914,6 +918,7 @@ func (i *InvoiceRegistry) processAMP(ctx invoiceUpdateCtx) error {
914918func (i * InvoiceRegistry ) NotifyExitHopHtlc (rHash lntypes.Hash ,
915919 amtPaid lnwire.MilliSatoshi , expiry uint32 , currentHeight int32 ,
916920 circuitKey CircuitKey , hodlChan chan <- interface {},
921+ wireCustomRecords lnwire.CustomRecords ,
917922 payload Payload ) (HtlcResolution , error ) {
918923
919924 // Create the update context containing the relevant details of the
@@ -925,6 +930,7 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
925930 expiry : expiry ,
926931 currentHeight : currentHeight ,
927932 finalCltvRejectDelta : i .cfg .FinalCltvRejectDelta ,
933+ wireCustomRecords : wireCustomRecords ,
928934 customRecords : payload .CustomRecords (),
929935 mpp : payload .MultiPath (),
930936 amp : payload .AMPRecord (),
@@ -1019,13 +1025,62 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
10191025 ctx * invoiceUpdateCtx , hodlChan chan <- interface {}) (
10201026 HtlcResolution , invoiceExpiry , error ) {
10211027
1028+ invoiceRef := ctx .invoiceRef ()
1029+ setID := (* SetID )(ctx .setID ())
1030+
1031+ // We need to look up the current state of the invoice in order to send
1032+ // the previously accepted/settled HTLCs to the interceptor.
1033+ existingInvoice , err := i .idb .LookupInvoice (
1034+ context .Background (), invoiceRef ,
1035+ )
1036+ switch {
1037+ case errors .Is (err , ErrInvoiceNotFound ) ||
1038+ errors .Is (err , ErrNoInvoicesCreated ):
1039+
1040+ // If the invoice was not found, return a failure resolution
1041+ // with an invoice not found result.
1042+ return NewFailResolution (
1043+ ctx .circuitKey , ctx .currentHeight ,
1044+ ResultInvoiceNotFound ,
1045+ ), nil , nil
1046+
1047+ case err != nil :
1048+ ctx .log (err .Error ())
1049+ return nil , nil , err
1050+ }
1051+
1052+ // Provide the invoice to the settlement interceptor to allow
1053+ // the interceptor's client an opportunity to manipulate the
1054+ // settlement process.
1055+ err = i .cfg .HtlcInterceptor .Intercept (HtlcModifyRequest {
1056+ WireCustomRecords : ctx .wireCustomRecords ,
1057+ ExitHtlcCircuitKey : ctx .circuitKey ,
1058+ ExitHtlcAmt : ctx .amtPaid ,
1059+ ExitHtlcExpiry : ctx .expiry ,
1060+ CurrentHeight : uint32 (ctx .currentHeight ),
1061+ Invoice : existingInvoice ,
1062+ }, func (resp HtlcModifyResponse ) {
1063+ log .Debugf ("Received invoice HTLC interceptor response: %v" ,
1064+ resp )
1065+
1066+ if resp .AmountPaid != 0 {
1067+ ctx .amtPaid = resp .AmountPaid
1068+ }
1069+ })
1070+ if err != nil {
1071+ err := fmt .Errorf ("error during invoice HTLC interception: %w" ,
1072+ err )
1073+ ctx .log (err .Error ())
1074+
1075+ return nil , nil , err
1076+ }
1077+
10221078 // We'll attempt to settle an invoice matching this rHash on disk (if
10231079 // one exists). The callback will update the invoice state and/or htlcs.
10241080 var (
10251081 resolution HtlcResolution
10261082 updateSubscribers bool
10271083 )
1028-
10291084 callback := func (inv * Invoice ) (* InvoiceUpdateDesc , error ) {
10301085 updateDesc , res , err := updateInvoice (ctx , inv )
10311086 if err != nil {
@@ -1042,8 +1097,6 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
10421097 return updateDesc , nil
10431098 }
10441099
1045- invoiceRef := ctx .invoiceRef ()
1046- setID := (* SetID )(ctx .setID ())
10471100 invoice , err := i .idb .UpdateInvoice (
10481101 context .Background (), invoiceRef , setID , callback ,
10491102 )
@@ -1080,6 +1133,8 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
10801133
10811134 var invoiceToExpire invoiceExpiry
10821135
1136+ log .Tracef ("Settlement resolution: %T %v" , resolution , resolution )
1137+
10831138 switch res := resolution .(type ) {
10841139 case * HtlcFailResolution :
10851140 // Inspect latest htlc state on the invoice. If it is found,
@@ -1212,7 +1267,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
12121267 }
12131268
12141269 // Now that the links have been notified of any state changes to their
1215- // HTLCs, we'll go ahead and notify any clients wiaiting on the invoice
1270+ // HTLCs, we'll go ahead and notify any clients waiting on the invoice
12161271 // state changes.
12171272 if updateSubscribers {
12181273 // We'll add a setID onto the notification, but only if this is
0 commit comments