mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
lntest: dispatch and intercept payment to blinded route
We don't support receiving blinded in this PR - just intercept and settle instead. The HTLC's arrival on the interceptor indicates that it was successfully forwarded on a blinded hop.
This commit is contained in:
parent
69e1162dd1
commit
0d9a184df8
@ -1,9 +1,11 @@
|
||||
package itest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
@ -13,6 +15,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
"github.com/lightningnetwork/lnd/lntest/node"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/record"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
@ -323,6 +326,10 @@ type blindedForwardTest struct {
|
||||
dave *node.HarnessNode
|
||||
channels []*lnrpc.ChannelPoint
|
||||
|
||||
carolInterceptor routerrpc.Router_HtlcInterceptorClient
|
||||
|
||||
preimage [32]byte
|
||||
|
||||
// cancel will cancel the test's top level context.
|
||||
cancel func()
|
||||
}
|
||||
@ -333,16 +340,28 @@ func newBlindedForwardTest(ht *lntest.HarnessTest) (context.Context,
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
return ctx, &blindedForwardTest{
|
||||
ht: ht,
|
||||
cancel: cancel,
|
||||
ht: ht,
|
||||
cancel: cancel,
|
||||
preimage: [32]byte{1, 2, 3},
|
||||
}
|
||||
}
|
||||
|
||||
// setup spins up additional nodes needed for our test and creates a four hop
|
||||
// network for testing blinded forwarding and returns a blinded route from
|
||||
// Bob -> Carol -> Dave, with Bob acting as the introduction point.
|
||||
func (b *blindedForwardTest) setup() *routing.BlindedPayment {
|
||||
b.carol = b.ht.NewNode("Carol", nil)
|
||||
// Bob -> Carol -> Dave, with Bob acting as the introduction point and an
|
||||
// interceptor on Carol's node to manage HTLCs (as Dave does not yet support
|
||||
// receiving).
|
||||
func (b *blindedForwardTest) setup(
|
||||
ctx context.Context) *routing.BlindedPayment {
|
||||
|
||||
b.carol = b.ht.NewNode("Carol", []string{
|
||||
"requireinterceptor",
|
||||
})
|
||||
|
||||
var err error
|
||||
b.carolInterceptor, err = b.carol.RPC.Router.HtlcInterceptor(ctx)
|
||||
require.NoError(b.ht, err, "interceptor")
|
||||
|
||||
b.dave = b.ht.NewNode("Dave", nil)
|
||||
|
||||
b.channels = setupFourHopNetwork(b.ht, b.carol, b.dave)
|
||||
@ -429,6 +448,82 @@ func (b *blindedForwardTest) createRouteToBlinded(paymentAmt int64,
|
||||
return resp.Routes[0]
|
||||
}
|
||||
|
||||
// sendBlindedPayment dispatches a payment to the route provided.
|
||||
func (b *blindedForwardTest) sendBlindedPayment(ctx context.Context,
|
||||
route *lnrpc.Route) {
|
||||
|
||||
hash := sha256.Sum256(b.preimage[:])
|
||||
sendReq := &routerrpc.SendToRouteRequest{
|
||||
PaymentHash: hash[:],
|
||||
Route: route,
|
||||
}
|
||||
|
||||
// Dispatch in a goroutine because this call is blocking - we assume
|
||||
// that we'll have assertions that this payment is sent by the caller.
|
||||
go func() {
|
||||
b.ht.Alice.RPC.SendToRouteV2(sendReq)
|
||||
}()
|
||||
}
|
||||
|
||||
// interceptFinalHop launches a goroutine to intercept Carol's htlcs and
|
||||
// returns a closure that can be used to resolve intercepted htlcs.
|
||||
//
|
||||
//nolint:lll
|
||||
func (b *blindedForwardTest) interceptFinalHop() func(routerrpc.ResolveHoldForwardAction) {
|
||||
hash := sha256.Sum256(b.preimage[:])
|
||||
htlcReceived := make(chan *routerrpc.ForwardHtlcInterceptRequest)
|
||||
|
||||
// Launch a goroutine which will receive from the interceptor and pipe
|
||||
// it into our request channel.
|
||||
go func() {
|
||||
forward, err := b.carolInterceptor.Recv()
|
||||
if err != nil {
|
||||
b.ht.Fatalf("intercept receive failed: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(forward.PaymentHash, hash[:]) {
|
||||
b.ht.Fatalf("unexpected payment hash: %v", hash)
|
||||
}
|
||||
|
||||
select {
|
||||
case htlcReceived <- forward:
|
||||
|
||||
case <-time.After(lntest.DefaultTimeout):
|
||||
b.ht.Fatal("timeout waiting to send intercepted htlc")
|
||||
}
|
||||
}()
|
||||
|
||||
// Create a closure that will wait for the intercept request and
|
||||
// resolve the HTLC with the appropriate action.
|
||||
resolve := func(action routerrpc.ResolveHoldForwardAction) {
|
||||
select {
|
||||
case forward := <-htlcReceived:
|
||||
resp := &routerrpc.ForwardHtlcInterceptResponse{
|
||||
IncomingCircuitKey: forward.IncomingCircuitKey,
|
||||
}
|
||||
|
||||
switch action {
|
||||
case routerrpc.ResolveHoldForwardAction_FAIL:
|
||||
resp.Action = routerrpc.ResolveHoldForwardAction_FAIL
|
||||
|
||||
case routerrpc.ResolveHoldForwardAction_SETTLE:
|
||||
resp.Action = routerrpc.ResolveHoldForwardAction_SETTLE
|
||||
resp.Preimage = b.preimage[:]
|
||||
|
||||
case routerrpc.ResolveHoldForwardAction_RESUME:
|
||||
resp.Action = routerrpc.ResolveHoldForwardAction_RESUME
|
||||
}
|
||||
|
||||
require.NoError(b.ht, b.carolInterceptor.Send(resp))
|
||||
|
||||
case <-time.After(lntest.DefaultTimeout):
|
||||
b.ht.Fatal("timeout waiting for htlc intercept")
|
||||
}
|
||||
}
|
||||
|
||||
return resolve
|
||||
}
|
||||
|
||||
// setupFourHopNetwork creates a network with the following topology and
|
||||
// liquidity:
|
||||
// Alice (100k)----- Bob (100k) ----- Carol (100k) ----- Dave
|
||||
@ -654,9 +749,42 @@ func getForwardingEdge(ht *lntest.HarnessTest,
|
||||
// testForwardBlindedRoute tests lnd's ability to forward payments in a blinded
|
||||
// route.
|
||||
func testForwardBlindedRoute(ht *lntest.HarnessTest) {
|
||||
_, testCase := newBlindedForwardTest(ht)
|
||||
ctx, testCase := newBlindedForwardTest(ht)
|
||||
defer testCase.cleanup()
|
||||
|
||||
route := testCase.setup()
|
||||
testCase.createRouteToBlinded(100_000, route)
|
||||
route := testCase.setup(ctx)
|
||||
blindedRoute := testCase.createRouteToBlinded(10_000_000, route)
|
||||
|
||||
// Receiving via blinded routes is not yet supported, so Dave won't be
|
||||
// able to process the payment.
|
||||
//
|
||||
// We have an interceptor at our disposal that will catch htlcs as they
|
||||
// are forwarded (ie, it won't intercept a HTLC that dave is receiving,
|
||||
// since no forwarding occurs). We initiate this interceptor with
|
||||
// Carol, so that we can catch it and settle on the outgoing link to
|
||||
// Dave. Once we hit the outgoing link, we know that we successfully
|
||||
// parsed the htlc, so this is an acceptable compromise.
|
||||
// Assert that our interceptor has exited without an error.
|
||||
resolveHTLC := testCase.interceptFinalHop()
|
||||
|
||||
// Once our interceptor is set up, we can send the blinded payment.
|
||||
testCase.sendBlindedPayment(ctx, blindedRoute)
|
||||
|
||||
// Wait for the HTLC to be active on Alice's channel.
|
||||
hash := sha256.Sum256(testCase.preimage[:])
|
||||
ht.AssertOutgoingHTLCActive(ht.Alice, testCase.channels[0], hash[:])
|
||||
ht.AssertOutgoingHTLCActive(ht.Bob, testCase.channels[1], hash[:])
|
||||
|
||||
// Intercept and settle the HTLC.
|
||||
resolveHTLC(routerrpc.ResolveHoldForwardAction_SETTLE)
|
||||
|
||||
// Wait for the HTLC to reflect as settled for Alice.
|
||||
preimage, err := lntypes.MakePreimage(testCase.preimage[:])
|
||||
require.NoError(ht, err)
|
||||
ht.AssertPaymentStatus(ht.Alice, preimage, lnrpc.Payment_SUCCEEDED)
|
||||
|
||||
// Assert that the HTLC has settled before test cleanup runs so that
|
||||
// we can cooperatively close all channels.
|
||||
ht.AssertHLTCNotActive(ht.Bob, testCase.channels[1], hash[:])
|
||||
ht.AssertHLTCNotActive(ht.Alice, testCase.channels[0], hash[:])
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user