07076cce21
Originally we adding the edge without proof in order to able to use it for payments path constrcution. This method will allow us to populate the announcement proof after the exchange of the half proofs and constrcutrion of full proof finished.
257 lines
7.4 KiB
Go
257 lines
7.4 KiB
Go
package routing
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
"github.com/roasbeef/btcd/wire"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/roasbeef/btcd/btcec"
|
|
"github.com/roasbeef/btcutil"
|
|
)
|
|
|
|
type testCtx struct {
|
|
router *ChannelRouter
|
|
|
|
graph *channeldb.ChannelGraph
|
|
|
|
aliases map[string]*btcec.PublicKey
|
|
|
|
chain *mockChain
|
|
|
|
notifier *mockNotifier
|
|
}
|
|
|
|
func createTestCtx(startingHeight uint32, testGraph ...string) (*testCtx, func(), error) {
|
|
var (
|
|
graph *channeldb.ChannelGraph
|
|
sourceNode *channeldb.LightningNode
|
|
cleanup func()
|
|
err error
|
|
)
|
|
|
|
aliasMap := make(map[string]*btcec.PublicKey)
|
|
|
|
// If the testGraph isn't set, then we'll create an empty graph to
|
|
// start out with. Our usage of a variadic parameter allows caller to
|
|
// omit the testGraph argument all together if they wish to start with
|
|
// a blank graph.
|
|
if testGraph == nil {
|
|
// First we'll set up a test graph for usage within the test.
|
|
graph, cleanup, err = makeTestGraph()
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to create test graph: %v", err)
|
|
}
|
|
|
|
sourceNode, err = createTestNode()
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to create source node: %v", err)
|
|
}
|
|
if err = graph.SetSourceNode(sourceNode); err != nil {
|
|
return nil, nil, fmt.Errorf("unable to set source node: %v", err)
|
|
}
|
|
} else {
|
|
// Otherwise, we'll attempt to locate and parse out the file
|
|
// that encodes the graph that our tests should be run against.
|
|
graph, cleanup, aliasMap, err = parseTestGraph(testGraph[0])
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to create test graph: %v", err)
|
|
}
|
|
|
|
sourceNode, err = graph.SourceNode()
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to fetch source node: %v", err)
|
|
}
|
|
}
|
|
|
|
// Next we'll initialize an instance of the channel router with mock
|
|
// versions of the chain and channel notifier. As we don't need to test
|
|
// any p2p functionality, the peer send and switch send messages won't
|
|
// be populated.
|
|
chain := newMockChain(startingHeight)
|
|
notifier := newMockNotifier()
|
|
router, err := New(Config{
|
|
Graph: graph,
|
|
Chain: chain,
|
|
Notifier: notifier,
|
|
SendToSwitch: func(_ *btcec.PublicKey,
|
|
_ *lnwire.UpdateAddHTLC) ([32]byte, error) {
|
|
return [32]byte{}, nil
|
|
},
|
|
})
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to create router %v", err)
|
|
}
|
|
if err := router.Start(); err != nil {
|
|
return nil, nil, fmt.Errorf("unable to start router: %v", err)
|
|
}
|
|
|
|
cleanUp := func() {
|
|
router.Stop()
|
|
cleanup()
|
|
}
|
|
|
|
return &testCtx{
|
|
router: router,
|
|
graph: graph,
|
|
aliases: aliasMap,
|
|
chain: chain,
|
|
notifier: notifier,
|
|
}, cleanUp, nil
|
|
}
|
|
|
|
// TestFindRoutesFeeSorting asserts that routes found by the FindRoutes method
|
|
// within the channel router are properly returned in a sorted order, with the
|
|
// lowest fee route coming first.
|
|
func TestFindRoutesFeeSorting(t *testing.T) {
|
|
const startingBlockHeight = 101
|
|
ctx, cleanUp, err := createTestCtx(startingBlockHeight, basicGraphFilePath)
|
|
defer cleanUp()
|
|
if err != nil {
|
|
t.Fatalf("unable to create router: %v", err)
|
|
}
|
|
|
|
// In this test we'd like to ensure proper integration of the various
|
|
// functions that are involved in path finding, and also route
|
|
// selection.
|
|
|
|
// Execute a query for all possible routes between roasbeef and luo ji.
|
|
const paymentAmt = btcutil.Amount(100)
|
|
target := ctx.aliases["luoji"]
|
|
routes, err := ctx.router.FindRoutes(target, paymentAmt)
|
|
if err != nil {
|
|
t.Fatalf("unable to find any routes: %v", err)
|
|
}
|
|
|
|
// Exactly, two such paths should be found.
|
|
if len(routes) != 2 {
|
|
t.Fatalf("2 routes shouldn't been selected, instead %v were: ",
|
|
len(routes))
|
|
}
|
|
|
|
// The paths should properly be ranked according to their total fee
|
|
// rate.
|
|
if routes[0].TotalFees > routes[1].TotalFees {
|
|
t.Fatalf("routes not ranked by total fee: %v",
|
|
spew.Sdump(routes))
|
|
}
|
|
}
|
|
|
|
// TestSendPaymentRouteFailureFallback tests that when sending a payment, if
|
|
// one of the target routes is seen as unavailable, then the next route in the
|
|
// queue is used instead. This process should continue until either a payment
|
|
// succeeds, or all routes have been exhausted.
|
|
func TestSendPaymentRouteFailureFallback(t *testing.T) {
|
|
const startingBlockHeight = 101
|
|
ctx, cleanUp, err := createTestCtx(startingBlockHeight, basicGraphFilePath)
|
|
defer cleanUp()
|
|
if err != nil {
|
|
t.Fatalf("unable to create router: %v", err)
|
|
}
|
|
|
|
// Craft a LightningPayment struct that'll send a payment from roasbeef
|
|
// to luo ji for 100 satoshis.
|
|
var payHash [32]byte
|
|
payment := LightningPayment{
|
|
Target: ctx.aliases["luoji"],
|
|
Amount: btcutil.Amount(1000),
|
|
PaymentHash: payHash,
|
|
}
|
|
|
|
var preImage [32]byte
|
|
copy(preImage[:], bytes.Repeat([]byte{9}, 32))
|
|
|
|
// We'll modify the SendToSwitch method that's been set within the
|
|
// router's configuration to ignore the path that has luo ji as the
|
|
// first hop. This should force the router to instead take the
|
|
// available two hop path (through satoshi).
|
|
ctx.router.cfg.SendToSwitch = func(n *btcec.PublicKey,
|
|
_ *lnwire.UpdateAddHTLC) ([32]byte, error) {
|
|
|
|
if ctx.aliases["luoji"].IsEqual(n) {
|
|
return [32]byte{}, errors.New("send error")
|
|
}
|
|
|
|
return preImage, nil
|
|
}
|
|
|
|
// Send off the payment request to the router, route through satoshi
|
|
// should've been selected as a fall back and succeeded correctly.
|
|
paymentPreImage, route, err := ctx.router.SendPayment(&payment)
|
|
if err != nil {
|
|
t.Fatalf("unable to send payment: %v", err)
|
|
}
|
|
|
|
// The route selected should have two hops
|
|
if len(route.Hops) != 2 {
|
|
t.Fatalf("incorrect route length: expected %v got %v", 2,
|
|
len(route.Hops))
|
|
}
|
|
|
|
// The preimage should match up with the once created above.
|
|
if !bytes.Equal(paymentPreImage[:], preImage[:]) {
|
|
t.Fatalf("incorrect preimage used: expected %x got %x",
|
|
preImage[:], paymentPreImage[:])
|
|
}
|
|
|
|
// The route should have satoshi as the first hop.
|
|
if route.Hops[0].Channel.Node.Alias != "satoshi" {
|
|
t.Fatalf("route should go through satoshi as first hop, "+
|
|
"instead passes through: %v",
|
|
route.Hops[0].Channel.Node.Alias)
|
|
}
|
|
}
|
|
|
|
// TestAddProof checks that we can update the channel proof after channel
|
|
// info was added to the database.
|
|
func TestAddProof(t *testing.T) {
|
|
ctx, cleanup, err := createTestCtx(0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer cleanup()
|
|
|
|
// In order to be able to add the edge we should have the valud
|
|
// funding UTXO within the blockchain.
|
|
fundingTx, _, chanID, err := createChannelEdge(ctx,
|
|
bitcoinKey1.SerializeCompressed(), bitcoinKey2.SerializeCompressed(),
|
|
100, 0)
|
|
if err != nil {
|
|
t.Fatalf("unable create channel edge: %v", err)
|
|
}
|
|
|
|
fundingBlock := &wire.MsgBlock{
|
|
Transactions: []*wire.MsgTx{fundingTx},
|
|
}
|
|
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight)
|
|
|
|
// After utxo was recreated adding the edge without the proof.
|
|
edge := &channeldb.ChannelEdgeInfo{
|
|
ChannelID: chanID.ToUint64(),
|
|
NodeKey1: priv1.PubKey(),
|
|
NodeKey2: priv1.PubKey(),
|
|
BitcoinKey1: bitcoinKey1,
|
|
BitcoinKey2: bitcoinKey2,
|
|
AuthProof: nil,
|
|
}
|
|
|
|
if err := ctx.router.AddEdge(edge); err != nil {
|
|
t.Fatalf("unable to add edge: %v", err)
|
|
}
|
|
|
|
// No trying to update the proof and checking that it have been updated.
|
|
if err := ctx.router.AddProof(*chanID, &testAuthProof); err != nil {
|
|
t.Fatalf("unable to add proof: %v", err)
|
|
}
|
|
|
|
info, _, _, err := ctx.router.GetChannelByID(*chanID)
|
|
if info.AuthProof == nil {
|
|
t.Fatal("proof have been updated")
|
|
}
|
|
}
|