Skip to content

Commit 12ad0cb

Browse files
ffranrguggero
authored andcommitted
invoices: integrate settlement interceptor with invoice registry
This commit updates the invoice registry to utilize the settlement interceptor during the invoice settlement routine. It allows the interceptor to capture the invoice, providing interception clients an opportunity to determine the settlement outcome.
1 parent 72abc94 commit 12ad0cb

File tree

4 files changed

+68
-1
lines changed

4 files changed

+68
-1
lines changed

htlcswitch/mock.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,7 @@ func newMockRegistry(minDelta uint32) *mockInvoiceRegistry {
10111011
panic(err)
10121012
}
10131013

1014+
modifierMock := &invoices.MockHtlcModifier{}
10141015
registry := invoices.NewRegistry(
10151016
cdb,
10161017
invoices.NewInvoiceExpiryWatcher(
@@ -1019,6 +1020,7 @@ func newMockRegistry(minDelta uint32) *mockInvoiceRegistry {
10191020
),
10201021
&invoices.RegistryConfig{
10211022
FinalCltvRejectDelta: 5,
1023+
HtlcModifier: modifierMock,
10221024
},
10231025
)
10241026
registry.Start()

invoices/invoiceregistry.go

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

invoices/invoiceregistry_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ import (
2323
"github.com/stretchr/testify/require"
2424
)
2525

26+
var (
27+
// htlcModifierMock is a mock implementation of the invoice HtlcModifier
28+
// interface.
29+
htlcModifierMock = &invpkg.MockHtlcModifier{}
30+
)
31+
2632
// TestInvoiceRegistry is a master test which encompasses all tests using an
2733
// InvoiceDB instance. The purpose of this test is to be able to run all tests
2834
// with a custom DB instance, so that we can test the same logic with different
@@ -517,6 +523,7 @@ func testSettleHoldInvoice(t *testing.T,
517523
cfg := invpkg.RegistryConfig{
518524
FinalCltvRejectDelta: testFinalCltvRejectDelta,
519525
Clock: clock,
526+
HtlcModifier: htlcModifierMock,
520527
}
521528

522529
expiryWatcher := invpkg.NewInvoiceExpiryWatcher(
@@ -683,6 +690,7 @@ func testCancelHoldInvoice(t *testing.T,
683690
cfg := invpkg.RegistryConfig{
684691
FinalCltvRejectDelta: testFinalCltvRejectDelta,
685692
Clock: testClock,
693+
HtlcModifier: htlcModifierMock,
686694
}
687695
expiryWatcher := invpkg.NewInvoiceExpiryWatcher(
688696
cfg.Clock, 0, uint32(testCurrentHeight), nil, newMockNotifier(),
@@ -1200,6 +1208,7 @@ func testInvoiceExpiryWithRegistry(t *testing.T,
12001208
cfg := invpkg.RegistryConfig{
12011209
FinalCltvRejectDelta: testFinalCltvRejectDelta,
12021210
Clock: testClock,
1211+
HtlcModifier: htlcModifierMock,
12031212
}
12041213

12051214
expiryWatcher := invpkg.NewInvoiceExpiryWatcher(
@@ -1310,6 +1319,7 @@ func testOldInvoiceRemovalOnStart(t *testing.T,
13101319
FinalCltvRejectDelta: testFinalCltvRejectDelta,
13111320
Clock: testClock,
13121321
GcCanceledInvoicesOnStartup: true,
1322+
HtlcModifier: htlcModifierMock,
13131323
}
13141324

13151325
expiryWatcher := invpkg.NewInvoiceExpiryWatcher(

invoices/test_utils_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ func defaultRegistryConfig() invpkg.RegistryConfig {
143143
return invpkg.RegistryConfig{
144144
FinalCltvRejectDelta: testFinalCltvRejectDelta,
145145
HtlcHoldDuration: 30 * time.Second,
146+
HtlcModifier: &invpkg.MockHtlcModifier{},
146147
}
147148
}
148149

0 commit comments

Comments
 (0)