mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
itest: add dynamic scid alias routing test
This commit is contained in:
parent
70f82bc4ff
commit
705e567357
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user