mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-24 15:52:22 +02: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",
|
Name: "invoice routing hints",
|
||||||
TestFunc: testInvoiceRoutingHints,
|
TestFunc: testInvoiceRoutingHints,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "scid alias routing hints",
|
||||||
|
TestFunc: testScidAliasRoutingHints,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "multi-hop payments over private channels",
|
Name: "multi-hop payments over private channels",
|
||||||
TestFunc: testMultiHopOverPrivateChannels,
|
TestFunc: testMultiHopOverPrivateChannels,
|
||||||
|
@ -3,6 +3,7 @@ package itest
|
|||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -746,6 +747,194 @@ func testInvoiceRoutingHints(ht *lntest.HarnessTest) {
|
|||||||
ht.ForceCloseChannel(alice, chanPointEve)
|
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
|
// testMultiHopOverPrivateChannels tests that private channels can be used as
|
||||||
// intermediate hops in a route for payments.
|
// intermediate hops in a route for payments.
|
||||||
func testMultiHopOverPrivateChannels(ht *lntest.HarnessTest) {
|
func testMultiHopOverPrivateChannels(ht *lntest.HarnessTest) {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/chainrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/chainrpc"
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc/devrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/neutrinorpc"
|
"github.com/lightningnetwork/lnd/lnrpc/neutrinorpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/peersrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/peersrpc"
|
||||||
@ -42,6 +43,7 @@ type HarnessRPC struct {
|
|||||||
ChainKit chainrpc.ChainKitClient
|
ChainKit chainrpc.ChainKitClient
|
||||||
NeutrinoKit neutrinorpc.NeutrinoKitClient
|
NeutrinoKit neutrinorpc.NeutrinoKitClient
|
||||||
Peer peersrpc.PeersClient
|
Peer peersrpc.PeersClient
|
||||||
|
DevRPC devrpc.DevClient
|
||||||
|
|
||||||
// Name is the HarnessNode's name.
|
// Name is the HarnessNode's name.
|
||||||
Name string
|
Name string
|
||||||
@ -73,6 +75,7 @@ func NewHarnessRPC(ctxt context.Context, t *testing.T, c *grpc.ClientConn,
|
|||||||
ChainKit: chainrpc.NewChainKitClient(c),
|
ChainKit: chainrpc.NewChainKitClient(c),
|
||||||
NeutrinoKit: neutrinorpc.NewNeutrinoKitClient(c),
|
NeutrinoKit: neutrinorpc.NewNeutrinoKitClient(c),
|
||||||
Peer: peersrpc.NewPeersClient(c),
|
Peer: peersrpc.NewPeersClient(c),
|
||||||
|
DevRPC: devrpc.NewDevClient(c),
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +208,54 @@ func (h *HarnessRPC) HtlcInterceptor() (InterceptorClient, context.CancelFunc) {
|
|||||||
return resp, cancel
|
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
|
type TrackPaymentsClient routerrpc.Router_TrackPaymentsClient
|
||||||
|
|
||||||
// TrackPayments makes a RPC call to the node's RouterClient and asserts.
|
// TrackPayments makes a RPC call to the node's RouterClient and asserts.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user