itest: add dynamic scid alias routing test

This commit is contained in:
George Tsagkarelis 2024-03-08 13:21:22 +01:00 committed by Oliver Gugger
parent 70f82bc4ff
commit 705e567357
No known key found for this signature in database
GPG Key ID: 8E4256593F177720
4 changed files with 244 additions and 0 deletions

View File

@ -358,6 +358,10 @@ var allTestCases = []*lntest.TestCase{
Name: "invoice routing hints",
TestFunc: testInvoiceRoutingHints,
},
{
Name: "scid alias routing hints",
TestFunc: testScidAliasRoutingHints,
},
{
Name: "multi-hop payments over private channels",
TestFunc: testMultiHopOverPrivateChannels,

View File

@ -3,6 +3,7 @@ package itest
import (
"encoding/hex"
"fmt"
"math"
"testing"
"time"
@ -746,6 +747,194 @@ func testInvoiceRoutingHints(ht *lntest.HarnessTest) {
ht.ForceCloseChannel(alice, chanPointEve)
}
// testScidAliasRoutingHints tests that dynamically created aliases via the RPC
// are properly used when routing.
func testScidAliasRoutingHints(ht *lntest.HarnessTest) {
const chanAmt = btcutil.Amount(800000)
// Option-scid-alias is opt-in, as is anchors.
scidAliasArgs := []string{
"--protocol.option-scid-alias",
"--protocol.anchors",
}
// We'll have a network Bob -> Carol -> Dave in the end, with both Carol
// and Dave having an alias for the channel between them.
carol := ht.NewNode("Carol", scidAliasArgs)
dave := ht.NewNode("Dave", scidAliasArgs)
ht.FundCoins(btcutil.SatoshiPerBitcoin, carol)
ht.FundCoins(btcutil.SatoshiPerBitcoin, dave)
ht.ConnectNodes(carol, dave)
// Create a channel between Carol and Dave, which uses the scid alias
// feature.
chanPointCD := ht.OpenChannel(carol, dave, lntest.OpenChannelParams{
Amt: chanAmt,
PushAmt: chanAmt / 2,
ScidAlias: true,
Private: true,
})
// Find the channel ID of the channel between Carol and Dave.
carolDaveChan := ht.QueryChannelByChanPoint(carol, chanPointCD)
// Make sure we can't add an alias that's not actually in the alias
// range (which is the case with the base SCID).
err := carol.RPC.XAddLocalChanAliasesErr(&routerrpc.AddAliasesRequest{
AliasMaps: []*lnrpc.AliasMap{
{
BaseScid: carolDaveChan.ChanId,
Aliases: []uint64{
carolDaveChan.ChanId,
},
},
},
})
require.ErrorContains(ht, err, routerrpc.ErrNoValidAlias.Error())
// Create an ephemeral alias that will be used as a routing hint.
ephemeralChanPoint := lnwire.ShortChannelID{
BlockHeight: 16_100_000,
TxIndex: 1,
TxPosition: 1,
}
ephemeralAlias := ephemeralChanPoint.ToUint64()
ephemeralAliasMap := []*lnrpc.AliasMap{
{
BaseScid: carolDaveChan.ChanId,
Aliases: []uint64{
ephemeralAlias,
},
},
}
// Add the alias to Carol.
carol.RPC.XAddLocalChanAliases(&routerrpc.AddAliasesRequest{
AliasMaps: ephemeralAliasMap,
})
// We shouldn't be able to add the same alias again.
err = carol.RPC.XAddLocalChanAliasesErr(&routerrpc.AddAliasesRequest{
AliasMaps: ephemeralAliasMap,
})
require.ErrorContains(ht, err, routerrpc.ErrAliasAlreadyExists.Error())
// Add the alias to Dave. This isn't strictly needed for the test, as
// the payment will go from Bob -> Carol -> Dave, so only Carol needs
// to know about the alias. So we'll later on remove it again to
// demonstrate that.
dave.RPC.XAddLocalChanAliases(&routerrpc.AddAliasesRequest{
AliasMaps: ephemeralAliasMap,
})
carolChans := carol.RPC.ListChannels(&lnrpc.ListChannelsRequest{})
require.Len(ht, carolChans.Channels, 1, "expected one channel")
// Get the alias scids for Carol's channel.
aliases := carolChans.Channels[0].AliasScids
// There should be two aliases.
require.Len(ht, aliases, 2, "expected two aliases")
// The ephemeral alias should be included.
require.Contains(
ht, aliases, ephemeralAlias, "expected ephemeral alias",
)
// List Dave's Channels.
daveChans := dave.RPC.ListChannels(&lnrpc.ListChannelsRequest{})
require.Len(ht, daveChans.Channels, 1, "expected one channel")
// Get the alias scids for his channel.
aliases = daveChans.Channels[0].AliasScids
// There should be two aliases.
require.Len(ht, aliases, 2, "expected two aliases")
// The ephemeral alias should be included.
require.Contains(
ht, aliases, ephemeralAlias, "expected ephemeral alias",
)
// Now that we've asserted that the alias is properly set up, we'll
// delete the one for Dave again. The payment should still succeed.
dave.RPC.XDeleteLocalChanAliases(&routerrpc.DeleteAliasesRequest{
AliasMaps: ephemeralAliasMap,
})
// Connect the existing Bob node with Carol via a public channel.
ht.ConnectNodes(ht.Bob, carol)
chanPointBC := ht.OpenChannel(ht.Bob, carol, lntest.OpenChannelParams{
Amt: chanAmt,
PushAmt: chanAmt / 2,
})
// Create the hop hint that Dave will use to craft his invoice. The
// goal here is to define only the ephemeral alias as a hop hint.
hopHint := &lnrpc.HopHint{
NodeId: carol.PubKeyStr,
ChanId: ephemeralAlias,
FeeBaseMsat: uint32(
chainreg.DefaultBitcoinBaseFeeMSat,
),
FeeProportionalMillionths: uint32(
chainreg.DefaultBitcoinFeeRate,
),
CltvExpiryDelta: chainreg.DefaultBitcoinTimeLockDelta,
}
// Define the invoice that Dave will add to his node.
invoice := &lnrpc.Invoice{
Memo: "dynamic alias",
Value: int64(chanAmt / 4),
RouteHints: []*lnrpc.RouteHint{
{
HopHints: []*lnrpc.HopHint{hopHint},
},
},
}
// Add the invoice and retrieve the payment request.
payReq := dave.RPC.AddInvoice(invoice).PaymentRequest
// Now Alice will try to pay to that payment request.
timeout := time.Second * 15
stream := ht.Bob.RPC.SendPayment(&routerrpc.SendPaymentRequest{
PaymentRequest: payReq,
TimeoutSeconds: int32(timeout.Seconds()),
FeeLimitSat: math.MaxInt64,
})
// Payment should eventually succeed.
ht.AssertPaymentSucceedWithTimeout(stream, timeout)
// Check that Dave's invoice appears as settled.
invoices := dave.RPC.ListInvoices(&lnrpc.ListInvoiceRequest{})
require.Len(ht, invoices.Invoices, 1, "expected one invoice")
require.Equal(ht, invoices.Invoices[0].State, lnrpc.Invoice_SETTLED,
"expected settled invoice")
// We'll now delete the alias again, but only on Carol's end. That
// should be enough to make the payment fail, since she doesn't know
// about the alias in the hop hint anymore.
carol.RPC.XDeleteLocalChanAliases(&routerrpc.DeleteAliasesRequest{
AliasMaps: ephemeralAliasMap,
})
payReq2 := dave.RPC.AddInvoice(invoice).PaymentRequest
stream2 := ht.Bob.RPC.SendPayment(&routerrpc.SendPaymentRequest{
PaymentRequest: payReq2,
TimeoutSeconds: int32(timeout.Seconds()),
FeeLimitSat: math.MaxInt64,
})
ht.AssertPaymentStatusFromStream(stream2, lnrpc.Payment_FAILED)
ht.CloseChannel(carol, chanPointCD)
ht.CloseChannel(ht.Bob, chanPointBC)
}
// testMultiHopOverPrivateChannels tests that private channels can be used as
// intermediate hops in a route for payments.
func testMultiHopOverPrivateChannels(ht *lntest.HarnessTest) {

View File

@ -7,6 +7,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/chainrpc"
"github.com/lightningnetwork/lnd/lnrpc/devrpc"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
"github.com/lightningnetwork/lnd/lnrpc/neutrinorpc"
"github.com/lightningnetwork/lnd/lnrpc/peersrpc"
@ -42,6 +43,7 @@ type HarnessRPC struct {
ChainKit chainrpc.ChainKitClient
NeutrinoKit neutrinorpc.NeutrinoKitClient
Peer peersrpc.PeersClient
DevRPC devrpc.DevClient
// Name is the HarnessNode's name.
Name string
@ -73,6 +75,7 @@ func NewHarnessRPC(ctxt context.Context, t *testing.T, c *grpc.ClientConn,
ChainKit: chainrpc.NewChainKitClient(c),
NeutrinoKit: neutrinorpc.NewNeutrinoKitClient(c),
Peer: peersrpc.NewPeersClient(c),
DevRPC: devrpc.NewDevClient(c),
Name: name,
}

View File

@ -208,6 +208,54 @@ func (h *HarnessRPC) HtlcInterceptor() (InterceptorClient, context.CancelFunc) {
return resp, cancel
}
// XAddLocalChanAliases adds a list of aliases to the node's alias map.
func (h *HarnessRPC) XAddLocalChanAliases(req *routerrpc.AddAliasesRequest) {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.Router.XAddLocalChanAliases(ctxt, req)
h.NoError(err, "XAddLocalChanAliases")
}
// XAddLocalChanAliasesErr adds a list of aliases to the node's alias map and
// expects an error.
func (h *HarnessRPC) XAddLocalChanAliasesErr(
req *routerrpc.AddAliasesRequest) error {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.Router.XAddLocalChanAliases(ctxt, req)
require.Error(h, err)
return err
}
// XDeleteLocalChanAliases deleted a set of alias mappings.
func (h *HarnessRPC) XDeleteLocalChanAliases(
req *routerrpc.DeleteAliasesRequest) {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.Router.XDeleteLocalChanAliases(ctxt, req)
h.NoError(err, "XDeleteLocalChanAliases")
}
// XDeleteLocalChanAliasesErr deleted a set of alias mappings and expects an
// error.
func (h *HarnessRPC) XDeleteLocalChanAliasesErr(
req *routerrpc.DeleteAliasesRequest) error {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.Router.XDeleteLocalChanAliases(ctxt, req)
require.Error(h, err)
return err
}
type TrackPaymentsClient routerrpc.Router_TrackPaymentsClient
// TrackPayments makes a RPC call to the node's RouterClient and asserts.