lnd.xprv/channeldb/graph_test.go

508 lines
14 KiB
Go
Raw Normal View History

package channeldb
import (
"bytes"
"fmt"
"image/color"
prand "math/rand"
"net"
"reflect"
"testing"
"time"
"github.com/btcsuite/fastsha256"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
)
var (
testAddr, _ = net.ResolveTCPAddr("tcp", "10.0.0.1:9000")
)
func createTestVertex(db *DB) (*LightningNode, error) {
updateTime := prand.Int63()
priv, err := btcec.NewPrivateKey(btcec.S256())
if err != nil {
return nil, err
}
pub := priv.PubKey().SerializeCompressed()
return &LightningNode{
LastUpdate: time.Unix(updateTime, 0),
Address: testAddr,
PubKey: priv.PubKey(),
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek" + string(pub[:]),
db: db,
}, nil
}
func TestNodeInsertionAndDeletion(t *testing.T) {
db, cleanUp, err := makeTestDB()
if err != nil {
t.Fatalf("unable to make test database: %v", err)
}
defer cleanUp()
graph := db.ChannelGraph()
// We'd like to test basic insertion/deletion for vertexes from the
// graph, so we'll create a test vertex to start with.
_, testPub := btcec.PrivKeyFromBytes(btcec.S256(), key[:])
node := &LightningNode{
LastUpdate: time.Unix(1232342, 0),
Address: testAddr,
PubKey: testPub,
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek",
db: db,
}
// First, insert the node into the graph DB. This should succeed
// without any errors.
if err := graph.AddLightningNode(node); err != nil {
t.Fatalf("unable to add node: %v", err)
}
// Next, fetch the node from the database to ensure everything was
// serialized properly.
dbNode, err := graph.FetchLightningNode(testPub)
if err != nil {
t.Fatalf("unable to locate node: %v", err)
}
// The two nodes should match exactly!
if !reflect.DeepEqual(node, dbNode) {
t.Fatalf("retrieved node doesn't match: expected %#v\n, got %#v\n",
node, dbNode)
}
// Next, delete the node from the graph, this should purge all data
// related to the node.
if err := graph.DeleteLightningNode(testPub); err != nil {
t.Fatalf("unable to delete node; %v", err)
}
// Finally, attempt to fetch the node again. This should fail as the
// node should've been deleted from the database.
_, err = graph.FetchLightningNode(testPub)
if err != ErrGraphNodeNotFound {
t.Fatalf("fetch after delete should fail!")
}
}
func TestAliasLookup(t *testing.T) {
db, cleanUp, err := makeTestDB()
if err != nil {
t.Fatalf("unable to make test database: %v", err)
}
defer cleanUp()
graph := db.ChannelGraph()
// We'd like to test the alias index within the database, so first
// create a new test node.
testNode, err := createTestVertex(db)
if err != nil {
t.Fatalf("unable to create test node: %v", err)
}
// Add the node to the graph's database, this should also insert an
// entry into the alias index for this node.
if err := graph.AddLightningNode(testNode); err != nil {
t.Fatalf("unable to add node: %v", err)
}
// Next, attempt to lookup the alias. The alias should exactly match
// the one which the test node was assigned.
dbAlias, err := graph.LookupAlias(testNode.PubKey)
if err != nil {
t.Fatalf("unable to find alias: %v", err)
}
if dbAlias != testNode.Alias {
t.Fatalf("aliases don't match, expected %v got %v",
testNode.Alias, dbAlias)
}
// Ensure that looking up a non-existent alias results in an error.
node, err := createTestVertex(db)
if err != nil {
t.Fatalf("unable to create test node: %v", err)
}
_, err = graph.LookupAlias(node.PubKey)
if err != ErrNodeAliasNotFound {
t.Fatalf("alias lookup should fail for non-existent pubkey")
}
}
func TestSourceNode(t *testing.T) {
db, cleanUp, err := makeTestDB()
if err != nil {
t.Fatalf("unable to make test database: %v", err)
}
defer cleanUp()
graph := db.ChannelGraph()
// We'd like to test the setting/getting of the source node, so we
// first create a fake node to use within the test.
testNode, err := createTestVertex(db)
if err != nil {
t.Fatalf("unable to create test node: %v", err)
}
// Attempt to fetch the source node, this should return an error as the
// source node hasn't yet been set.
if _, err := graph.SourceNode(); err != ErrSourceNodeNotSet {
t.Fatalf("source node shouldn't be set in new graph")
}
// Set the source the source node, this should insert the node into the
// database in a special way indicating it's the source node.
if err := graph.SetSourceNode(testNode); err != nil {
t.Fatalf("unable to set source node: %v", err)
}
// Retrieve the source node from the database, it should exactly match
// the one we set above.
sourceNode, err := graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
if !reflect.DeepEqual(testNode, sourceNode) {
t.Fatalf("nodes don't match, expected %#v \n got %#v",
testNode, sourceNode)
}
}
func TestEdgeInsertionDeletion(t *testing.T) {
db, cleanUp, err := makeTestDB()
if err != nil {
t.Fatalf("unable to make test database: %v", err)
}
defer cleanUp()
graph := db.ChannelGraph()
// We'd like to test the insertion/deletion of edges, so we create two
// vertexes to connect.
node1, err := createTestVertex(db)
if err != nil {
t.Fatalf("unable to create test node: %v", err)
}
node2, err := createTestVertex(db)
if err != nil {
t.Fatalf("unable to create test node: %v", err)
}
// In in addition to the fake vertexes we create some fake channel
// identifiers.
chanID := uint64(prand.Int63())
outpoint := wire.OutPoint{
Hash: rev,
Index: 9,
}
// Add the new edge to the database, this should proceed without any
// errors.
if err := graph.AddChannelEdge(node1.PubKey, node2.PubKey, &outpoint,
chanID); err != nil {
t.Fatalf("unable to create channel edge: %v", err)
}
// Check for existence of the edge within the database, it should be
// found.
found, err := graph.HasChannelEdge(chanID)
if err != nil {
t.Fatalf("unable to query for edge: %v", err)
} else if !found {
t.Fatalf("graph should have of inserted edge")
}
// Next, attempt to delete the edge from the database, again this
// should proceed without any issues.
if err := graph.DeleteChannelEdge(&outpoint); err != nil {
t.Fatalf("unable to delete edge: %v", err)
}
// Finally, attempt to delete a (now) non-existent edge within the
// database, this should result in an error.
err = graph.DeleteChannelEdge(&outpoint)
if err != ErrEdgeNotFound {
t.Fatalf("deleting a non-existent edge should fail!")
}
}
func TestEdgeInfoUpdates(t *testing.T) {
db, cleanUp, err := makeTestDB()
if err != nil {
t.Fatalf("unable to make test database: %v", err)
}
defer cleanUp()
graph := db.ChannelGraph()
// We'd like to test the update of edges inserted into the database, so
// we create two vertexes to connect.
node1, err := createTestVertex(db)
if err != nil {
t.Fatalf("unable to create test node: %v", err)
}
if err := graph.AddLightningNode(node1); err != nil {
t.Fatalf("unable to add node: %v", err)
}
node2, err := createTestVertex(db)
if err != nil {
t.Fatalf("unable to create test node: %v", err)
}
if err := graph.AddLightningNode(node2); err != nil {
t.Fatalf("unable to add node: %v", err)
}
var (
firstNode *LightningNode
secondNode *LightningNode
)
node1Bytes := node1.PubKey.SerializeCompressed()
node2Bytes := node2.PubKey.SerializeCompressed()
if bytes.Compare(node1Bytes, node2Bytes) == -1 {
firstNode = node1
secondNode = node2
} else {
firstNode = node2
secondNode = node1
}
// In in addition to the fake vertexes we create some fake channel
// identifiers.
chanID := uint64(prand.Int63())
outpoint := wire.OutPoint{
Hash: rev,
Index: 9,
}
// Add the new edge to the database, this should proceed without any
// errors.
if err := graph.AddChannelEdge(node1.PubKey, node2.PubKey, &outpoint,
chanID); err != nil {
t.Fatalf("unable to create channel edge: %v", err)
}
// With the edge added, we can now create some fake edge information to
// update for both edges.
edge1 := &ChannelEdge{
ChannelID: chanID,
ChannelPoint: outpoint,
LastUpdate: time.Unix(433453, 0),
Flags: 0,
Expiry: 99,
MinHTLC: 2342135,
FeeBaseMSat: 4352345,
FeeProportionalMillionths: 3452352,
Capacity: 9903453,
Node: secondNode,
db: db,
}
edge2 := &ChannelEdge{
ChannelID: chanID,
ChannelPoint: outpoint,
LastUpdate: time.Unix(124234, 0),
Flags: 1,
Expiry: 99,
MinHTLC: 2342135,
FeeBaseMSat: 4352345,
FeeProportionalMillionths: 90392423,
Capacity: 324523,
Node: firstNode,
db: db,
}
// Next, insert both nodes into the database, they should both be
// inserted without any issues.
if err := graph.UpdateEdgeInfo(edge1); err != nil {
t.Fatalf("unable to update edge: %v", err)
}
if err := graph.UpdateEdgeInfo(edge2); err != nil {
t.Fatalf("unable to update edge: %v", err)
}
// With the edges inserted, perform some queries to ensure that they've
// been inserted properly.
dbEdge1, dbEdge2, err := graph.FetchChannelEdgesByID(chanID)
if err != nil {
t.Fatalf("unable to fetch channel by ID: %v", err)
}
if !reflect.DeepEqual(dbEdge1, edge1) {
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge1,
dbEdge1)
}
if !reflect.DeepEqual(dbEdge2, edge2) {
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge2,
dbEdge2)
}
// Next, attempt to query the channel edges according to the outpoint
// of the channel.
dbEdge1, dbEdge2, err = graph.FetchChannelEdgesByOutpoint(&outpoint)
if err != nil {
t.Fatalf("unable to fetch channel by ID: %v", err)
}
if !reflect.DeepEqual(dbEdge1, edge1) {
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge1,
dbEdge1)
}
if !reflect.DeepEqual(dbEdge2, edge2) {
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge2,
dbEdge2)
}
}
func randEdge(chanID uint64, op wire.OutPoint, db *DB) *ChannelEdge {
update := prand.Int63()
return &ChannelEdge{
ChannelID: chanID,
ChannelPoint: op,
LastUpdate: time.Unix(update, 0),
Expiry: uint16(prand.Int63()),
MinHTLC: btcutil.Amount(prand.Int63()),
FeeBaseMSat: btcutil.Amount(prand.Int63()),
FeeProportionalMillionths: btcutil.Amount(prand.Int63()),
Capacity: btcutil.Amount(prand.Int63()),
db: db,
}
}
func TestGraphTraversal(t *testing.T) {
db, cleanUp, err := makeTestDB()
if err != nil {
t.Fatalf("unable to make test database: %v", err)
}
defer cleanUp()
graph := db.ChannelGraph()
// We'd like to test some of the graph traversal capabilities within
// the DB, so we'll create a series of fake nodes to insert into the
// graph.
const numNodes = 20
nodes := make([]*LightningNode, numNodes)
nodeIndex := map[string]struct{}{}
for i := 0; i < numNodes; i++ {
node, err := createTestVertex(db)
if err != nil {
t.Fatalf("unable to create node: %v", err)
}
nodes[i] = node
nodeIndex[node.Alias] = struct{}{}
}
// Add each of the nodes into the graph, they should be inserted
// without error.
for _, node := range nodes {
if err := graph.AddLightningNode(node); err != nil {
t.Fatalf("unable to add node: %v", err)
}
}
// Iterate over each node as returned by the graph, if all nodes are
// reached, then the map created above should be empty.
err = graph.ForEachNode(func(node *LightningNode) error {
delete(nodeIndex, node.Alias)
return nil
})
if err != nil {
t.Fatalf("for each failure: %v", err)
}
if len(nodeIndex) != 0 {
t.Fatalf("all nodes not reached within ForEach")
}
// Determine which node is "smaller", we'll need this in order to
// properly create the edges for the graph.
var firstNode, secondNode *LightningNode
node1Bytes := nodes[0].PubKey.SerializeCompressed()
node2Bytes := nodes[1].PubKey.SerializeCompressed()
if bytes.Compare(node1Bytes, node2Bytes) == -1 {
firstNode = nodes[0]
secondNode = nodes[1]
} else {
firstNode = nodes[0]
secondNode = nodes[1]
}
// Create 5 channels between the first two nodes we generated above.
const numChannels = 5
chanIndex := map[uint64]struct{}{}
for i := 0; i < numChannels; i++ {
txHash := fastsha256.Sum256([]byte{byte(i)})
chanID := uint64(i + 1)
op := wire.OutPoint{
Hash: txHash,
Index: 0,
}
err := graph.AddChannelEdge(nodes[0].PubKey, nodes[1].PubKey,
&op, chanID)
if err != nil {
t.Fatalf("unable to add node: %v", err)
}
// Create and add an edge with random data that points from
// node1 -> node2.
edge := randEdge(chanID, op, db)
edge.Flags = 0
edge.Node = secondNode
if err := graph.UpdateEdgeInfo(edge); err != nil {
t.Fatalf("unable to update edge: %v", err)
}
// Create another random edge that points from node2 -> node1
// this time.
edge = randEdge(chanID, op, db)
edge.Flags = 1
edge.Node = firstNode
if err := graph.UpdateEdgeInfo(edge); err != nil {
t.Fatalf("unable to update edge: %v", err)
}
chanIndex[chanID] = struct{}{}
}
// Iterate through all the known channels within the graph DB, once
// again if the map is empty that that indicates that all edges have
// properly been reached.
err = graph.ForEachChannel(func(_, e *ChannelEdge) error {
delete(chanIndex, e.ChannelID)
return nil
})
if err != nil {
t.Fatalf("for each failure: %v", err)
}
if len(chanIndex) != 0 {
t.Fatalf("all edges not reached within ForEach")
}
// Finally, we want to test the ability to iterate over all the
// outgoing channels for a particular node.
numNodeChans := 0
err = firstNode.ForEachChannel(func(c *ChannelEdge) error {
// Each each should indicate that it's outgoing (pointed
// towards the second node).
if !c.Node.PubKey.IsEqual(secondNode.PubKey) {
return fmt.Errorf("wrong outgoing edge")
}
numNodeChans += 1
return nil
})
if err != nil {
t.Fatalf("for each failure: %v", err)
}
if numNodeChans != numChannels {
t.Fatalf("all edges for node reached within ForEach")
}
}