-
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
rpc+htlcswitch: add htlc transformation capabilities to the interceptor #8633
Changes from all commits
043f696
8b1d9c9
71c3251
795aff5
41a5b9a
2b3618c
b93d288
6fa2db0
d247cc9
5c3f7be
406fbe2
31ee274
bd5de43
8025296
03dceca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package itest | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
"time" | ||
|
@@ -344,6 +345,140 @@ func testForwardInterceptorBasic(ht *lntest.HarnessTest) { | |
ht.CloseChannel(bob, cpBC) | ||
} | ||
|
||
// testForwardInterceptorModifiedHtlc tests that the interceptor can modify the | ||
// amount and custom records of an intercepted HTLC and resume it. | ||
func testForwardInterceptorModifiedHtlc(ht *lntest.HarnessTest) { | ||
// Initialize the test context with 3 connected nodes. | ||
ts := newInterceptorTestScenario(ht) | ||
|
||
alice, bob, carol := ts.alice, ts.bob, ts.carol | ||
|
||
// Open and wait for channels. | ||
const chanAmt = btcutil.Amount(300000) | ||
p := lntest.OpenChannelParams{Amt: chanAmt} | ||
reqs := []*lntest.OpenChannelRequest{ | ||
{Local: alice, Remote: bob, Param: p}, | ||
{Local: bob, Remote: carol, Param: p}, | ||
} | ||
resp := ht.OpenMultiChannelsAsync(reqs) | ||
cpAB, cpBC := resp[0], resp[1] | ||
|
||
// Make sure Alice is aware of channel Bob=>Carol. | ||
ht.AssertTopologyChannelOpen(alice, cpBC) | ||
|
||
// Connect an interceptor to Bob's node. | ||
bobInterceptor, cancelBobInterceptor := bob.RPC.HtlcInterceptor() | ||
|
||
// Prepare the test cases. | ||
invoiceValueAmtMsat := int64(1000) | ||
req := &lnrpc.Invoice{ValueMsat: invoiceValueAmtMsat} | ||
addResponse := carol.RPC.AddInvoice(req) | ||
invoice := carol.RPC.LookupInvoice(addResponse.RHash) | ||
tc := &interceptorTestCase{ | ||
amountMsat: invoiceValueAmtMsat, | ||
invoice: invoice, | ||
payAddr: invoice.PaymentAddr, | ||
} | ||
|
||
// We initiate a payment from Alice. | ||
done := make(chan struct{}) | ||
go func() { | ||
// Signal that all the payments have been sent. | ||
defer close(done) | ||
|
||
ts.sendPaymentAndAssertAction(tc) | ||
}() | ||
|
||
// We start the htlc interceptor with a simple implementation that saves | ||
// all intercepted packets. These packets are held to simulate a | ||
// pending payment. | ||
packet := ht.ReceiveHtlcInterceptor(bobInterceptor) | ||
|
||
// Resume the intercepted HTLC with a modified amount and custom | ||
// records. | ||
if packet.CustomRecords == nil { | ||
packet.CustomRecords = make(map[uint64][]byte) | ||
} | ||
customRecords := packet.CustomRecords | ||
|
||
// Add custom records entry. | ||
crKey := uint64(65537) | ||
crValue := []byte("custom-records-test-value") | ||
customRecords[crKey] = crValue | ||
|
||
action := routerrpc.ResolveHoldForwardAction_RESUME_MODIFIED | ||
newOutgoingAmountMsat := packet.OutgoingAmountMsat + 4000 | ||
|
||
err := bobInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{ | ||
IncomingCircuitKey: packet.IncomingCircuitKey, | ||
OutgoingAmountMsat: newOutgoingAmountMsat, | ||
CustomRecords: customRecords, | ||
Action: action, | ||
}) | ||
require.NoError(ht, err, "failed to send request") | ||
|
||
// Check that the modified UpdateAddHTLC message fields were reported in | ||
// Carol's log. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we used this trick to get the itests running should we now consider reading these values the "healthy" way? i.e Carol also intercepts and acquires values from htlc message? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think ideally Carol should intercept as you suggest. But the log lookup should do for this PR, IMO. I'll see if I can put another PR so that Carol can intercept also. That way this test wont be bound to a log message. |
||
targetLogPrefixStr := "Received UpdateAddHTLC(" | ||
targetOutgoingAmountMsatStr := fmt.Sprintf( | ||
"amt=%d", newOutgoingAmountMsat, | ||
) | ||
|
||
// Formulate custom records target log string. | ||
var asciiValues []string | ||
for _, b := range crValue { | ||
asciiValues = append(asciiValues, fmt.Sprintf("%d", b)) | ||
} | ||
|
||
targetCustomRecordsStr := fmt.Sprintf( | ||
"%d:[%s]", crKey, strings.Join(asciiValues, " "), | ||
) | ||
|
||
// logEntryCheck is a helper function that checks if the log entry | ||
// contains the expected strings. | ||
logEntryCheck := func(logEntry string) bool { | ||
return strings.Contains(logEntry, targetLogPrefixStr) && | ||
strings.Contains(logEntry, targetCustomRecordsStr) && | ||
strings.Contains(logEntry, targetOutgoingAmountMsatStr) | ||
} | ||
|
||
// Wait for the log entry to appear in Carol's log. | ||
require.Eventually(ht, func() bool { | ||
ctx := context.Background() | ||
dbgInfo, err := carol.RPC.LN.GetDebugInfo( | ||
ffranr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ctx, &lnrpc.GetDebugInfoRequest{}, | ||
) | ||
require.NoError(ht, err, "failed to get Carol node debug info") | ||
|
||
for _, logEntry := range dbgInfo.Log { | ||
if logEntryCheck(logEntry) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
}, defaultTimeout, time.Second) | ||
|
||
// Cancel the context, which will disconnect Bob's interceptor. | ||
cancelBobInterceptor() | ||
|
||
// Make sure all goroutines are finished. | ||
select { | ||
case <-done: | ||
case <-time.After(defaultTimeout): | ||
require.Fail(ht, "timeout waiting for sending payment") | ||
} | ||
|
||
// Assert that the payment was successful. | ||
var preimage lntypes.Preimage | ||
copy(preimage[:], invoice.RPreimage) | ||
ht.AssertPaymentStatus(alice, preimage, lnrpc.Payment_SUCCEEDED) | ||
|
||
// Finally, close channels. | ||
ht.CloseChannel(alice, cpAB) | ||
ht.CloseChannel(bob, cpBC) | ||
} | ||
|
||
// interceptorTestScenario is a helper struct to hold the test context and | ||
// provide the needed functionality. | ||
type interceptorTestScenario 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.
maybe I'm missing something, why are we defaulting to err here? is this some old/outdated impementation?
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 code relates to the on-chain resolution flow. In the on-chain resolution flow, an intercepted forward can not be resumed. Therefore, it can't be resume modified either.