routing: Require adding edge to node before adding node.
This commit introduces the requirement specified in BOLT#7, where we ignore any node announcements for a specific node if we yet haven't seen any channel announcements where this node takes part. This is to prevent someone DoS-ing the network with cheap node announcements. In the router this is enforced by requiring a call to AddNode(node_id) to be preceded by an AddEdge(edge_id) call, where node_id is one of the nodes in edge_id.
This commit is contained in:
parent
bd0465ee1d
commit
39a59bbe6f
@ -35,7 +35,8 @@ const (
|
|||||||
ErrOutdated
|
ErrOutdated
|
||||||
|
|
||||||
// ErrIgnored is returned when the update have been ignored because
|
// ErrIgnored is returned when the update have been ignored because
|
||||||
// this update can't bring us something new.
|
// this update can't bring us something new, or because a node
|
||||||
|
// announcement was given for node not found in any channel.
|
||||||
ErrIgnored
|
ErrIgnored
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -52,13 +52,14 @@ func createTestNode() (*channeldb.LightningNode, error) {
|
|||||||
|
|
||||||
pub := priv.PubKey().SerializeCompressed()
|
pub := priv.PubKey().SerializeCompressed()
|
||||||
return &channeldb.LightningNode{
|
return &channeldb.LightningNode{
|
||||||
LastUpdate: time.Unix(updateTime, 0),
|
HaveNodeAnnouncement: true,
|
||||||
Addresses: testAddrs,
|
LastUpdate: time.Unix(updateTime, 0),
|
||||||
PubKey: priv.PubKey(),
|
Addresses: testAddrs,
|
||||||
Color: color.RGBA{1, 2, 3, 0},
|
PubKey: priv.PubKey(),
|
||||||
Alias: "kek" + string(pub[:]),
|
Color: color.RGBA{1, 2, 3, 0},
|
||||||
AuthSig: testSig,
|
Alias: "kek" + string(pub[:]),
|
||||||
Features: testFeatures,
|
AuthSig: testSig,
|
||||||
|
Features: testFeatures,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,7 +298,7 @@ func TestEdgeUpdateNotification(t *testing.T) {
|
|||||||
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight)
|
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight)
|
||||||
|
|
||||||
// Next we'll create two test nodes that the fake channel will be open
|
// Next we'll create two test nodes that the fake channel will be open
|
||||||
// between and add then as members of the channel graph.
|
// between.
|
||||||
node1, err := createTestNode()
|
node1, err := createTestNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create test node: %v", err)
|
t.Fatalf("unable to create test node: %v", err)
|
||||||
@ -307,15 +308,6 @@ func TestEdgeUpdateNotification(t *testing.T) {
|
|||||||
t.Fatalf("unable to create test node: %v", err)
|
t.Fatalf("unable to create test node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the two node topology updates to the channel router so they
|
|
||||||
// can be validated and stored within the graph database.
|
|
||||||
if err := ctx.router.AddNode(node1); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := ctx.router.AddNode(node2); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, to conclude our test set up, we'll create a channel
|
// Finally, to conclude our test set up, we'll create a channel
|
||||||
// update to announce the created channel between the two nodes.
|
// update to announce the created channel between the two nodes.
|
||||||
edge := &channeldb.ChannelEdgeInfo{
|
edge := &channeldb.ChannelEdgeInfo{
|
||||||
@ -462,20 +454,34 @@ func TestEdgeUpdateNotification(t *testing.T) {
|
|||||||
func TestNodeUpdateNotification(t *testing.T) {
|
func TestNodeUpdateNotification(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
ctx, cleanUp, err := createTestCtx(1)
|
const startingBlockHeight = 101
|
||||||
|
ctx, cleanUp, err := createTestCtx(startingBlockHeight)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new client to receive notifications.
|
// We only accept node announcements from nodes having a known channel,
|
||||||
ntfnClient, err := ctx.router.SubscribeTopology()
|
// so create one now.
|
||||||
|
const chanValue = 10000
|
||||||
|
fundingTx, _, chanID, err := createChannelEdge(ctx,
|
||||||
|
bitcoinKey1.SerializeCompressed(),
|
||||||
|
bitcoinKey2.SerializeCompressed(),
|
||||||
|
chanValue, startingBlockHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to subscribe for channel notifications: %v", err)
|
t.Fatalf("unable create channel edge: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create two random nodes to add to send as node announcement messages
|
// We'll also add a record for the block that included our funding
|
||||||
// to trigger notifications.
|
// transaction.
|
||||||
|
fundingBlock := &wire.MsgBlock{
|
||||||
|
Transactions: []*wire.MsgTx{fundingTx},
|
||||||
|
}
|
||||||
|
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight)
|
||||||
|
|
||||||
|
// Create two nodes acting as endpoints in the created channel, and use
|
||||||
|
// them to trigger notifications by sending updated node announcement
|
||||||
|
// messages.
|
||||||
node1, err := createTestNode()
|
node1, err := createTestNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create test node: %v", err)
|
t.Fatalf("unable to create test node: %v", err)
|
||||||
@ -485,7 +491,34 @@ func TestNodeUpdateNotification(t *testing.T) {
|
|||||||
t.Fatalf("unable to create test node: %v", err)
|
t.Fatalf("unable to create test node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change network topology by adding nodes to the channel router.
|
edge := &channeldb.ChannelEdgeInfo{
|
||||||
|
ChannelID: chanID.ToUint64(),
|
||||||
|
NodeKey1: node1.PubKey,
|
||||||
|
NodeKey2: node2.PubKey,
|
||||||
|
BitcoinKey1: bitcoinKey1,
|
||||||
|
BitcoinKey2: bitcoinKey2,
|
||||||
|
AuthProof: &channeldb.ChannelAuthProof{
|
||||||
|
NodeSig1: testSig,
|
||||||
|
NodeSig2: testSig,
|
||||||
|
BitcoinSig1: testSig,
|
||||||
|
BitcoinSig2: testSig,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding the edge will add the nodes to the graph, but with no info
|
||||||
|
// except the pubkey known.
|
||||||
|
if err := ctx.router.AddEdge(edge); err != nil {
|
||||||
|
t.Fatalf("unable to add edge: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new client to receive notifications.
|
||||||
|
ntfnClient, err := ctx.router.SubscribeTopology()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to subscribe for channel notifications: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change network topology by adding the updated info for the two nodes
|
||||||
|
// to the channel router.
|
||||||
if err := ctx.router.AddNode(node1); err != nil {
|
if err := ctx.router.AddNode(node1); err != nil {
|
||||||
t.Fatalf("unable to add node: %v", err)
|
t.Fatalf("unable to add node: %v", err)
|
||||||
}
|
}
|
||||||
@ -610,25 +643,67 @@ func TestNotificationCancellation(t *testing.T) {
|
|||||||
t.Fatalf("unable to subscribe for channel notifications: %v", err)
|
t.Fatalf("unable to subscribe for channel notifications: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We'll create the utxo for a new channel.
|
||||||
|
const chanValue = 10000
|
||||||
|
fundingTx, _, chanID, err := createChannelEdge(ctx,
|
||||||
|
bitcoinKey1.SerializeCompressed(),
|
||||||
|
bitcoinKey2.SerializeCompressed(),
|
||||||
|
chanValue, startingBlockHeight)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable create channel edge: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll also add a record for the block that included our funding
|
||||||
|
// transaction.
|
||||||
|
fundingBlock := &wire.MsgBlock{
|
||||||
|
Transactions: []*wire.MsgTx{fundingTx},
|
||||||
|
}
|
||||||
|
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight)
|
||||||
|
|
||||||
// We'll create a fresh new node topology update to feed to the channel
|
// We'll create a fresh new node topology update to feed to the channel
|
||||||
// router.
|
// router.
|
||||||
node, err := createTestNode()
|
node1, err := createTestNode()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create test node: %v", err)
|
||||||
|
}
|
||||||
|
node2, err := createTestNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create test node: %v", err)
|
t.Fatalf("unable to create test node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before we send the message to the channel router, we'll cancel the
|
// Before we send the message to the channel router, we'll cancel the
|
||||||
// notifications for this client. As a result, the notification
|
// notifications for this client. As a result, the notification
|
||||||
// triggered by accepting this announcement shouldn't be sent to the
|
// triggered by accepting the channel announcements shouldn't be sent
|
||||||
// client.
|
// to the client.
|
||||||
ntfnClient.Cancel()
|
ntfnClient.Cancel()
|
||||||
|
|
||||||
if err := ctx.router.AddNode(node); err != nil {
|
edge := &channeldb.ChannelEdgeInfo{
|
||||||
|
ChannelID: chanID.ToUint64(),
|
||||||
|
NodeKey1: node1.PubKey,
|
||||||
|
NodeKey2: node2.PubKey,
|
||||||
|
BitcoinKey1: bitcoinKey1,
|
||||||
|
BitcoinKey2: bitcoinKey2,
|
||||||
|
AuthProof: &channeldb.ChannelAuthProof{
|
||||||
|
NodeSig1: testSig,
|
||||||
|
NodeSig2: testSig,
|
||||||
|
BitcoinSig1: testSig,
|
||||||
|
BitcoinSig2: testSig,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := ctx.router.AddEdge(edge); err != nil {
|
||||||
|
t.Fatalf("unable to add edge: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.router.AddNode(node1); err != nil {
|
||||||
|
t.Fatalf("unable to add node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.router.AddNode(node2); err != nil {
|
||||||
t.Fatalf("unable to add node: %v", err)
|
t.Fatalf("unable to add node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
// The notification shouldn't be sent, however, the channel should be
|
// The notifications shouldn't be sent, however, the channel should be
|
||||||
// closed, causing the second read-value to be false.
|
// closed, causing the second read-value to be false.
|
||||||
case _, ok := <-ntfnClient.TopologyChanges:
|
case _, ok := <-ntfnClient.TopologyChanges:
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -671,21 +746,15 @@ func TestChannelCloseNotification(t *testing.T) {
|
|||||||
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight)
|
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight)
|
||||||
|
|
||||||
// Next we'll create two test nodes that the fake channel will be open
|
// Next we'll create two test nodes that the fake channel will be open
|
||||||
// between and add then as members of the channel graph.
|
// between.
|
||||||
node1, err := createTestNode()
|
node1, err := createTestNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create test node: %v", err)
|
t.Fatalf("unable to create test node: %v", err)
|
||||||
}
|
}
|
||||||
if err := ctx.router.AddNode(node1); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
node2, err := createTestNode()
|
node2, err := createTestNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create test node: %v", err)
|
t.Fatalf("unable to create test node: %v", err)
|
||||||
}
|
}
|
||||||
if err := ctx.router.AddNode(node2); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, to conclude our test set up, we'll create a channel
|
// Finally, to conclude our test set up, we'll create a channel
|
||||||
// announcement to announce the created channel between the two nodes.
|
// announcement to announce the created channel between the two nodes.
|
||||||
|
@ -165,12 +165,13 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbNode := &channeldb.LightningNode{
|
dbNode := &channeldb.LightningNode{
|
||||||
AuthSig: testSig,
|
HaveNodeAnnouncement: true,
|
||||||
LastUpdate: time.Now(),
|
AuthSig: testSig,
|
||||||
Addresses: testAddrs,
|
LastUpdate: time.Now(),
|
||||||
PubKey: pub,
|
Addresses: testAddrs,
|
||||||
Alias: node.Alias,
|
PubKey: pub,
|
||||||
Features: testFeatures,
|
Alias: node.Alias,
|
||||||
|
Features: testFeatures,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We require all aliases within the graph to be unique for our
|
// We require all aliases within the graph to be unique for our
|
||||||
|
@ -28,8 +28,9 @@ import (
|
|||||||
// and applying edges updates, return the current block with with out
|
// and applying edges updates, return the current block with with out
|
||||||
// topology is synchronized.
|
// topology is synchronized.
|
||||||
type ChannelGraphSource interface {
|
type ChannelGraphSource interface {
|
||||||
// AddNode is used to add node to the topology of the router, after
|
// AddNode is used to add information about a node to the router
|
||||||
// this node might be used in construction of payment path.
|
// database. If the node with this pubkey is not present in an existing
|
||||||
|
// channel, it will be ignored.
|
||||||
AddNode(node *channeldb.LightningNode) error
|
AddNode(node *channeldb.LightningNode) error
|
||||||
|
|
||||||
// AddEdge is used to add edge/channel to the topology of the router,
|
// AddEdge is used to add edge/channel to the topology of the router,
|
||||||
@ -524,17 +525,23 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
|
|||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case *channeldb.LightningNode:
|
case *channeldb.LightningNode:
|
||||||
// Before proceeding ensure that we aren't already away of this
|
// If we are not already aware of this node, it means that we
|
||||||
// node, and if we are then this is a newer update that we
|
// don't know about any channel using this node. To avoid a DoS
|
||||||
// known of.
|
// attack by node announcements, we will ignore such nodes. If
|
||||||
|
// we do know about this node, check that this update brings
|
||||||
|
// info newer than what we already have.
|
||||||
lastUpdate, exists, err := r.cfg.Graph.HasLightningNode(msg.PubKey)
|
lastUpdate, exists, err := r.cfg.Graph.HasLightningNode(msg.PubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("unable to query for the "+
|
return errors.Errorf("unable to query for the "+
|
||||||
"existence of node: %v", err)
|
"existence of node: %v", err)
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return newErrf(ErrIgnored, "Ignoring node announcement"+
|
||||||
|
" for node not found in channel graph (%x)",
|
||||||
|
msg.PubKey.SerializeCompressed())
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we've reached this pint then we're aware of th vertex
|
// If we've reached this point then we're aware of the vertex
|
||||||
// being advertised. So we now check if the new message has a
|
// being advertised. So we now check if the new message has a
|
||||||
// new time stamp, if not then we won't accept the new data as
|
// new time stamp, if not then we won't accept the new data as
|
||||||
// it would override newer data.
|
// it would override newer data.
|
||||||
@ -565,20 +572,34 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
|
|||||||
"chan_id=%v", msg.ChannelID)
|
"chan_id=%v", msg.ChannelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we don't yet know about this edge, then we'll do an
|
// Query the database for the existence of the two nodes in this
|
||||||
// additional check to ensure that we have information about
|
// channel. If not found, add a partial node to the database,
|
||||||
// the two nodes that this edge connects.
|
// containing only the node keys.
|
||||||
_, exists, _ = r.cfg.Graph.HasLightningNode(msg.NodeKey1)
|
_, exists, _ = r.cfg.Graph.HasLightningNode(msg.NodeKey1)
|
||||||
if !exists {
|
if !exists {
|
||||||
return errors.Errorf("unable to add channel edge, info "+
|
node1 := &channeldb.LightningNode{
|
||||||
"for node %x is missing",
|
PubKey: msg.NodeKey1,
|
||||||
msg.NodeKey1.SerializeCompressed())
|
HaveNodeAnnouncement: false,
|
||||||
|
}
|
||||||
|
err := r.cfg.Graph.AddLightningNode(node1)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("unable to add node %v to"+
|
||||||
|
" the graph: %v",
|
||||||
|
node1.PubKey.SerializeCompressed(), err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_, exists, _ = r.cfg.Graph.HasLightningNode(msg.NodeKey2)
|
_, exists, _ = r.cfg.Graph.HasLightningNode(msg.NodeKey2)
|
||||||
if !exists {
|
if !exists {
|
||||||
return errors.Errorf("unable to add channel edge, info "+
|
node2 := &channeldb.LightningNode{
|
||||||
"for node %x is missing",
|
PubKey: msg.NodeKey2,
|
||||||
msg.NodeKey1.SerializeCompressed())
|
HaveNodeAnnouncement: false,
|
||||||
|
}
|
||||||
|
err := r.cfg.Graph.AddLightningNode(node2)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("unable to add node %v to"+
|
||||||
|
" the graph: %v",
|
||||||
|
node2.PubKey.SerializeCompressed(), err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before we can add the channel to the channel graph, we need
|
// Before we can add the channel to the channel graph, we need
|
||||||
@ -1031,8 +1052,9 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route
|
|||||||
return [32]byte{}, nil, sendError
|
return [32]byte{}, nil, sendError
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddNode is used to add node to the topology of the router, after this node
|
// AddNode is used to add information about a node to the router database. If
|
||||||
// might be used in construction of payment path.
|
// the node with this pubkey is not present in an existing channel, it will
|
||||||
|
// be ignored.
|
||||||
//
|
//
|
||||||
// NOTE: This method is part of the ChannelGraphSource interface.
|
// NOTE: This method is part of the ChannelGraphSource interface.
|
||||||
func (r *ChannelRouter) AddNode(node *channeldb.LightningNode) error {
|
func (r *ChannelRouter) AddNode(node *channeldb.LightningNode) error {
|
||||||
|
@ -4,7 +4,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image/color"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/roasbeef/btcd/wire"
|
"github.com/roasbeef/btcd/wire"
|
||||||
@ -229,16 +231,10 @@ func TestAddProof(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := ctx.router.AddNode(node1); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
node2, err := createTestNode()
|
node2, err := createTestNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := ctx.router.AddNode(node2); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// In order to be able to add the edge we should have a valid funding
|
// In order to be able to add the edge we should have a valid funding
|
||||||
// UTXO within the blockchain.
|
// UTXO within the blockchain.
|
||||||
@ -279,34 +275,290 @@ func TestAddProof(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestIgnoreNodeAnnouncement tests that adding a node to the router that is
|
||||||
|
// not known from any channel annoucement, leads to the annoucement being
|
||||||
|
// ignored.
|
||||||
|
func TestIgnoreNodeAnnouncement(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const startingBlockHeight = 101
|
||||||
|
ctx, cleanUp, err := createTestCtx(startingBlockHeight,
|
||||||
|
basicGraphFilePath)
|
||||||
|
defer cleanUp()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create router: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
node := &channeldb.LightningNode{
|
||||||
|
HaveNodeAnnouncement: true,
|
||||||
|
LastUpdate: time.Unix(123, 0),
|
||||||
|
Addresses: testAddrs,
|
||||||
|
PubKey: priv1.PubKey(),
|
||||||
|
Color: color.RGBA{1, 2, 3, 0},
|
||||||
|
Alias: "node11",
|
||||||
|
AuthSig: testSig,
|
||||||
|
Features: testFeatures,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ctx.router.AddNode(node)
|
||||||
|
if !IsError(err, ErrIgnored) {
|
||||||
|
t.Fatalf("expected to get ErrIgnore, instead got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestAddEdgeUnknownVertexes tests that if an edge is added that contains two
|
// TestAddEdgeUnknownVertexes tests that if an edge is added that contains two
|
||||||
// vertex which we don't know of, then the edge is rejected.
|
// vertexes which we don't know of, the edge should be available for use
|
||||||
|
// regardless. This is due to the fact that we don't actually need node
|
||||||
|
// announcements for the channel vertexes to be able to use the channel.
|
||||||
func TestAddEdgeUnknownVertexes(t *testing.T) {
|
func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
ctx, cleanup, err := createTestCtx(0)
|
const startingBlockHeight = 101
|
||||||
|
ctx, cleanUp, err := createTestCtx(startingBlockHeight,
|
||||||
|
basicGraphFilePath)
|
||||||
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
}
|
}
|
||||||
defer cleanup()
|
|
||||||
|
|
||||||
_, _, chanID, err := createChannelEdge(ctx,
|
// The two nodes we are about to add should not exist yet.
|
||||||
bitcoinKey1.SerializeCompressed(), bitcoinKey2.SerializeCompressed(),
|
_, exists1, err := ctx.graph.HasLightningNode(priv1.PubKey())
|
||||||
10000, 500)
|
if err != nil {
|
||||||
|
t.Fatalf("unable to query graph: %v", err)
|
||||||
|
}
|
||||||
|
if exists1 {
|
||||||
|
t.Fatalf("node already existed")
|
||||||
|
}
|
||||||
|
_, exists2, err := ctx.graph.HasLightningNode(priv2.PubKey())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to query graph: %v", err)
|
||||||
|
}
|
||||||
|
if exists2 {
|
||||||
|
t.Fatalf("node already existed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the edge between the two unknown nodes to the graph, and check
|
||||||
|
// that the nodes are found after the fact.
|
||||||
|
fundingTx, _, chanID, err := createChannelEdge(ctx,
|
||||||
|
bitcoinKey1.SerializeCompressed(),
|
||||||
|
bitcoinKey2.SerializeCompressed(), 10000, 500)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create channel edge: %v", err)
|
t.Fatalf("unable to create channel edge: %v", err)
|
||||||
}
|
}
|
||||||
|
fundingBlock := &wire.MsgBlock{
|
||||||
|
Transactions: []*wire.MsgTx{fundingTx},
|
||||||
|
}
|
||||||
|
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight)
|
||||||
|
|
||||||
edge := &channeldb.ChannelEdgeInfo{
|
edge := &channeldb.ChannelEdgeInfo{
|
||||||
ChannelID: chanID.ToUint64(),
|
ChannelID: chanID.ToUint64(),
|
||||||
NodeKey1: priv1.PubKey(),
|
NodeKey1: priv1.PubKey(),
|
||||||
NodeKey2: priv1.PubKey(),
|
NodeKey2: priv2.PubKey(),
|
||||||
BitcoinKey1: bitcoinKey1,
|
BitcoinKey1: bitcoinKey1,
|
||||||
BitcoinKey2: bitcoinKey2,
|
BitcoinKey2: bitcoinKey2,
|
||||||
AuthProof: nil,
|
AuthProof: nil,
|
||||||
}
|
}
|
||||||
if err := ctx.router.AddEdge(edge); err == nil {
|
if err := ctx.router.AddEdge(edge); err != nil {
|
||||||
t.Fatal("edge should have been rejected due to unknown " +
|
t.Fatalf("expected to be able to add edge to the channel graph,"+
|
||||||
"vertexes, but wasn't")
|
" even though the vertexes were unknown: %v.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must add the edge policy to be able to use the edge for route
|
||||||
|
// finding.
|
||||||
|
edgePolicy := &channeldb.ChannelEdgePolicy{
|
||||||
|
Signature: testSig,
|
||||||
|
ChannelID: edge.ChannelID,
|
||||||
|
LastUpdate: time.Now(),
|
||||||
|
TimeLockDelta: 10,
|
||||||
|
MinHTLC: btcutil.Amount(1),
|
||||||
|
FeeBaseMSat: btcutil.Amount(10),
|
||||||
|
FeeProportionalMillionths: btcutil.Amount(10000),
|
||||||
|
}
|
||||||
|
edgePolicy.Flags = 0
|
||||||
|
|
||||||
|
if err := ctx.router.UpdateEdge(edgePolicy); err != nil {
|
||||||
|
t.Fatalf("unable to update edge policy: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create edge in the other direction as well.
|
||||||
|
edgePolicy = &channeldb.ChannelEdgePolicy{
|
||||||
|
Signature: testSig,
|
||||||
|
ChannelID: edge.ChannelID,
|
||||||
|
LastUpdate: time.Now(),
|
||||||
|
TimeLockDelta: 10,
|
||||||
|
MinHTLC: btcutil.Amount(1),
|
||||||
|
FeeBaseMSat: btcutil.Amount(10),
|
||||||
|
FeeProportionalMillionths: btcutil.Amount(10000),
|
||||||
|
}
|
||||||
|
edgePolicy.Flags = 1
|
||||||
|
|
||||||
|
if err := ctx.router.UpdateEdge(edgePolicy); err != nil {
|
||||||
|
t.Fatalf("unable to update edge policy: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// After adding the edge between the two previously unknown nodes, they
|
||||||
|
// should have been added to the graph.
|
||||||
|
_, exists1, err = ctx.graph.HasLightningNode(priv1.PubKey())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to query graph: %v", err)
|
||||||
|
}
|
||||||
|
if !exists1 {
|
||||||
|
t.Fatalf("node1 was not added to the graph")
|
||||||
|
}
|
||||||
|
_, exists2, err = ctx.graph.HasLightningNode(priv2.PubKey())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to query graph: %v", err)
|
||||||
|
}
|
||||||
|
if !exists2 {
|
||||||
|
t.Fatalf("node2 was not added to the graph")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will connect node1 to the rest of the test graph, and make sure
|
||||||
|
// we can find a route to node2, which will use the just added channel
|
||||||
|
// edge.
|
||||||
|
|
||||||
|
// We will connect node 1 to "sophon"
|
||||||
|
connectNode := ctx.aliases["sophon"]
|
||||||
|
if connectNode == nil {
|
||||||
|
t.Fatalf("could not find node to connect to")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
pubKey1 *btcec.PublicKey
|
||||||
|
pubKey2 *btcec.PublicKey
|
||||||
|
)
|
||||||
|
node1Bytes := priv1.PubKey().SerializeCompressed()
|
||||||
|
node2Bytes := connectNode.SerializeCompressed()
|
||||||
|
if bytes.Compare(node1Bytes, node2Bytes) == -1 {
|
||||||
|
pubKey1 = priv1.PubKey()
|
||||||
|
pubKey2 = connectNode
|
||||||
|
} else {
|
||||||
|
pubKey1 = connectNode
|
||||||
|
pubKey2 = priv1.PubKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
fundingTx, _, chanID, err = createChannelEdge(ctx,
|
||||||
|
pubKey1.SerializeCompressed(), pubKey2.SerializeCompressed(),
|
||||||
|
10000, 510)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create channel edge: %v", err)
|
||||||
|
}
|
||||||
|
fundingBlock = &wire.MsgBlock{
|
||||||
|
Transactions: []*wire.MsgTx{fundingTx},
|
||||||
|
}
|
||||||
|
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight)
|
||||||
|
|
||||||
|
edge = &channeldb.ChannelEdgeInfo{
|
||||||
|
ChannelID: chanID.ToUint64(),
|
||||||
|
NodeKey1: pubKey1,
|
||||||
|
NodeKey2: pubKey2,
|
||||||
|
BitcoinKey1: pubKey1,
|
||||||
|
BitcoinKey2: pubKey2,
|
||||||
|
AuthProof: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.router.AddEdge(edge); err != nil {
|
||||||
|
t.Fatalf("unable to add edge to the channel graph: %v.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
edgePolicy = &channeldb.ChannelEdgePolicy{
|
||||||
|
Signature: testSig,
|
||||||
|
ChannelID: edge.ChannelID,
|
||||||
|
LastUpdate: time.Now(),
|
||||||
|
TimeLockDelta: 10,
|
||||||
|
MinHTLC: btcutil.Amount(1),
|
||||||
|
FeeBaseMSat: btcutil.Amount(10),
|
||||||
|
FeeProportionalMillionths: btcutil.Amount(10000),
|
||||||
|
}
|
||||||
|
edgePolicy.Flags = 0
|
||||||
|
|
||||||
|
if err := ctx.router.UpdateEdge(edgePolicy); err != nil {
|
||||||
|
t.Fatalf("unable to update edge policy: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
edgePolicy = &channeldb.ChannelEdgePolicy{
|
||||||
|
Signature: testSig,
|
||||||
|
ChannelID: edge.ChannelID,
|
||||||
|
LastUpdate: time.Now(),
|
||||||
|
TimeLockDelta: 10,
|
||||||
|
MinHTLC: btcutil.Amount(1),
|
||||||
|
FeeBaseMSat: btcutil.Amount(10),
|
||||||
|
FeeProportionalMillionths: btcutil.Amount(10000),
|
||||||
|
}
|
||||||
|
edgePolicy.Flags = 1
|
||||||
|
|
||||||
|
if err := ctx.router.UpdateEdge(edgePolicy); err != nil {
|
||||||
|
t.Fatalf("unable to update edge policy: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should now be able to find one route to node 2.
|
||||||
|
const paymentAmt = btcutil.Amount(100)
|
||||||
|
targetNode := priv2.PubKey()
|
||||||
|
routes, err := ctx.router.FindRoutes(targetNode, paymentAmt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to find any routes: %v", err)
|
||||||
|
}
|
||||||
|
if len(routes) != 1 {
|
||||||
|
t.Fatalf("expected to find 1 route, found: %v", len(routes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check that we can update the node info for the partial node
|
||||||
|
// without messing up the channel graph.
|
||||||
|
n1 := &channeldb.LightningNode{
|
||||||
|
HaveNodeAnnouncement: true,
|
||||||
|
LastUpdate: time.Unix(123, 0),
|
||||||
|
Addresses: testAddrs,
|
||||||
|
PubKey: priv1.PubKey(),
|
||||||
|
Color: color.RGBA{1, 2, 3, 0},
|
||||||
|
Alias: "node11",
|
||||||
|
AuthSig: testSig,
|
||||||
|
Features: testFeatures,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.router.AddNode(n1); err != nil {
|
||||||
|
t.Fatalf("could not add node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n2 := &channeldb.LightningNode{
|
||||||
|
HaveNodeAnnouncement: true,
|
||||||
|
LastUpdate: time.Unix(123, 0),
|
||||||
|
Addresses: testAddrs,
|
||||||
|
PubKey: priv2.PubKey(),
|
||||||
|
Color: color.RGBA{1, 2, 3, 0},
|
||||||
|
Alias: "node22",
|
||||||
|
AuthSig: testSig,
|
||||||
|
Features: testFeatures,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.router.AddNode(n2); err != nil {
|
||||||
|
t.Fatalf("could not add node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should still be able to find the route, and the info should be
|
||||||
|
// updated.
|
||||||
|
routes, err = ctx.router.FindRoutes(targetNode, paymentAmt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to find any routes: %v", err)
|
||||||
|
}
|
||||||
|
if len(routes) != 1 {
|
||||||
|
t.Fatalf("expected to find 1 route, found: %v", len(routes))
|
||||||
|
}
|
||||||
|
|
||||||
|
copy1, err := ctx.graph.FetchLightningNode(priv1.PubKey())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to fetch node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if copy1.Alias != n1.Alias {
|
||||||
|
t.Fatalf("fetched node not equal to original")
|
||||||
|
}
|
||||||
|
|
||||||
|
copy2, err := ctx.graph.FetchLightningNode(priv2.PubKey())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to fetch node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if copy2.Alias != n2.Alias {
|
||||||
|
t.Fatalf("fetched node not equal to original")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user