channeldb+routing: add in-memory graph
Adds an in-memory channel graph cache for faster pathfinding. Original PoC by: Joost Jager Co-Authored by: Oliver Gugger
This commit is contained in:
1 parent
d6fa912188
commit
369c09be61
11 files changed
+652
-133
No files matched your search
+4
-40
@@ -2,7 +2,6 @@ package routing
|
||||
|
||||
import (
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
@@ -12,8 +11,7 @@ import (
|
||||
type routingGraph interface {
|
||||
// forEachNodeChannel calls the callback for every channel of the given node.
|
||||
forEachNodeChannel(nodePub route.Vertex,
|
||||
cb func(*channeldb.ChannelEdgeInfo, *channeldb.ChannelEdgePolicy,
|
||||
*channeldb.ChannelEdgePolicy) error) error
|
||||
cb func(channel *channeldb.DirectedChannel) error) error
|
||||
|
||||
// sourceNode returns the source node of the graph.
|
||||
sourceNode() route.Vertex
|
||||
@@ -26,7 +24,6 @@ type routingGraph interface {
|
||||
// database.
|
||||
type dbRoutingTx struct {
|
||||
graph *channeldb.ChannelGraph
|
||||
tx kvdb.RTx
|
||||
source route.Vertex
|
||||
}
|
||||
|
||||
@@ -38,37 +35,19 @@ func newDbRoutingTx(graph *channeldb.ChannelGraph) (*dbRoutingTx, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx, err := graph.Database().BeginReadTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dbRoutingTx{
|
||||
graph: graph,
|
||||
tx: tx,
|
||||
source: sourceNode.PubKeyBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// close closes the underlying db transaction.
|
||||
func (g *dbRoutingTx) close() error {
|
||||
return g.tx.Rollback()
|
||||
}
|
||||
|
||||
// forEachNodeChannel calls the callback for every channel of the given node.
|
||||
//
|
||||
// NOTE: Part of the routingGraph interface.
|
||||
func (g *dbRoutingTx) forEachNodeChannel(nodePub route.Vertex,
|
||||
cb func(*channeldb.ChannelEdgeInfo, *channeldb.ChannelEdgePolicy,
|
||||
*channeldb.ChannelEdgePolicy) error) error {
|
||||
cb func(channel *channeldb.DirectedChannel) error) error {
|
||||
|
||||
txCb := func(_ kvdb.RTx, info *channeldb.ChannelEdgeInfo,
|
||||
p1, p2 *channeldb.ChannelEdgePolicy) error {
|
||||
|
||||
return cb(info, p1, p2)
|
||||
}
|
||||
|
||||
return g.graph.ForEachNodeChannel(g.tx, nodePub[:], txCb)
|
||||
return g.graph.ForEachNodeChannel(nodePub, cb)
|
||||
}
|
||||
|
||||
// sourceNode returns the source node of the graph.
|
||||
@@ -85,20 +64,5 @@ func (g *dbRoutingTx) sourceNode() route.Vertex {
|
||||
func (g *dbRoutingTx) fetchNodeFeatures(nodePub route.Vertex) (
|
||||
*lnwire.FeatureVector, error) {
|
||||
|
||||
targetNode, err := g.graph.FetchLightningNode(nodePub)
|
||||
switch err {
|
||||
|
||||
// If the node exists and has features, return them directly.
|
||||
case nil:
|
||||
return targetNode.Features, nil
|
||||
|
||||
// If we couldn't find a node announcement, populate a blank feature
|
||||
// vector.
|
||||
case channeldb.ErrGraphNodeNotFound:
|
||||
return lnwire.EmptyFeatureVector(), nil
|
||||
|
||||
// Otherwise bubble the error up.
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
return g.graph.FetchNodeFeatures(nodePub)
|
||||
}
|
||||
+22
-21
@@ -159,8 +159,7 @@ func (m *mockGraph) addChannel(id uint64, node1id, node2id byte,
|
||||
//
|
||||
// NOTE: Part of the routingGraph interface.
|
||||
func (m *mockGraph) forEachNodeChannel(nodePub route.Vertex,
|
||||
cb func(*channeldb.ChannelEdgeInfo, *channeldb.ChannelEdgePolicy,
|
||||
*channeldb.ChannelEdgePolicy) error) error {
|
||||
cb func(channel *channeldb.DirectedChannel) error) error {
|
||||
|
||||
// Look up the mock node.
|
||||
node, ok := m.nodes[nodePub]
|
||||
@@ -171,36 +170,38 @@ func (m *mockGraph) forEachNodeChannel(nodePub route.Vertex,
|
||||
// Iterate over all of its channels.
|
||||
for peer, channel := range node.channels {
|
||||
// Lexicographically sort the pubkeys.
|
||||
var node1, node2 route.Vertex
|
||||
var node1 route.Vertex
|
||||
if bytes.Compare(nodePub[:], peer[:]) == -1 {
|
||||
node1, node2 = peer, nodePub
|
||||
node1 = peer
|
||||
} else {
|
||||
node1, node2 = nodePub, peer
|
||||
node1 = nodePub
|
||||
}
|
||||
|
||||
peerNode := m.nodes[peer]
|
||||
|
||||
// Call the per channel callback.
|
||||
err := cb(
|
||||
&channeldb.ChannelEdgeInfo{
|
||||
NodeKey1Bytes: node1,
|
||||
NodeKey2Bytes: node2,
|
||||
},
|
||||
&channeldb.ChannelEdgePolicy{
|
||||
&channeldb.DirectedChannel{
|
||||
ChannelID: channel.id,
|
||||
Node: &channeldb.LightningNode{
|
||||
PubKeyBytes: peer,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
IsNode1: nodePub == node1,
|
||||
OtherNode: peer,
|
||||
Capacity: channel.capacity,
|
||||
OutPolicy: &channeldb.ChannelEdgePolicy{
|
||||
ChannelID: channel.id,
|
||||
Node: &channeldb.LightningNode{
|
||||
PubKeyBytes: peer,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
},
|
||||
FeeBaseMSat: node.baseFee,
|
||||
},
|
||||
FeeBaseMSat: node.baseFee,
|
||||
},
|
||||
&channeldb.ChannelEdgePolicy{
|
||||
ChannelID: channel.id,
|
||||
Node: &channeldb.LightningNode{
|
||||
PubKeyBytes: nodePub,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
InPolicy: &channeldb.ChannelEdgePolicy{
|
||||
ChannelID: channel.id,
|
||||
Node: &channeldb.LightningNode{
|
||||
PubKeyBytes: nodePub,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
},
|
||||
FeeBaseMSat: peerNode.baseFee,
|
||||
},
|
||||
FeeBaseMSat: peerNode.baseFee,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
+6
-9
@@ -359,14 +359,12 @@ func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
|
||||
g routingGraph) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, error) {
|
||||
|
||||
var max, total lnwire.MilliSatoshi
|
||||
cb := func(edgeInfo *channeldb.ChannelEdgeInfo, outEdge,
|
||||
_ *channeldb.ChannelEdgePolicy) error {
|
||||
|
||||
if outEdge == nil {
|
||||
cb := func(channel *channeldb.DirectedChannel) error {
|
||||
if channel.OutPolicy == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
chanID := outEdge.ChannelID
|
||||
chanID := channel.ChannelID
|
||||
|
||||
// Enforce outgoing channel restriction.
|
||||
if outgoingChans != nil {
|
||||
@@ -381,9 +379,7 @@ func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
|
||||
// This can happen when a channel is added to the graph after
|
||||
// we've already queried the bandwidth hints.
|
||||
if !ok {
|
||||
bandwidth = lnwire.NewMSatFromSatoshis(
|
||||
edgeInfo.Capacity,
|
||||
)
|
||||
bandwidth = lnwire.NewMSatFromSatoshis(channel.Capacity)
|
||||
}
|
||||
|
||||
if bandwidth > max {
|
||||
@@ -889,7 +885,8 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
||||
// Determine the next hop forward using the next map.
|
||||
currentNodeWithDist, ok := distance[currentNode]
|
||||
if !ok {
|
||||
// If the node doesnt have a next hop it means we didn't find a path.
|
||||
// If the node doesn't have a next hop it means we
|
||||
// didn't find a path.
|
||||
return nil, errNoPathFound
|
||||
}
|
||||
|
||||
|
||||
@@ -304,6 +304,16 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
|
||||
}
|
||||
}
|
||||
|
||||
aliasForNode := func(node route.Vertex) string {
|
||||
for alias, pubKey := range aliasMap {
|
||||
if pubKey == node {
|
||||
return alias
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// With all the vertexes inserted, we can now insert the edges into the
|
||||
// test graph.
|
||||
for _, edge := range g.Edges {
|
||||
@@ -353,10 +363,17 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
channelFlags := lnwire.ChanUpdateChanFlags(edge.ChannelFlags)
|
||||
isUpdate1 := channelFlags&lnwire.ChanUpdateDirection == 0
|
||||
targetNode := edgeInfo.NodeKey1Bytes
|
||||
if isUpdate1 {
|
||||
targetNode = edgeInfo.NodeKey2Bytes
|
||||
}
|
||||
|
||||
edgePolicy := &channeldb.ChannelEdgePolicy{
|
||||
SigBytes: testSig.Serialize(),
|
||||
MessageFlags: lnwire.ChanUpdateMsgFlags(edge.MessageFlags),
|
||||
ChannelFlags: lnwire.ChanUpdateChanFlags(edge.ChannelFlags),
|
||||
ChannelFlags: channelFlags,
|
||||
ChannelID: edge.ChannelID,
|
||||
LastUpdate: testTime,
|
||||
TimeLockDelta: edge.Expiry,
|
||||
@@ -364,6 +381,10 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
|
||||
MaxHTLC: lnwire.MilliSatoshi(edge.MaxHTLC),
|
||||
FeeBaseMSat: lnwire.MilliSatoshi(edge.FeeBaseMsat),
|
||||
FeeProportionalMillionths: lnwire.MilliSatoshi(edge.FeeRate),
|
||||
Node: &channeldb.LightningNode{
|
||||
Alias: aliasForNode(targetNode),
|
||||
PubKeyBytes: targetNode,
|
||||
},
|
||||
}
|
||||
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
||||
return nil, err
|
||||
@@ -635,6 +656,11 @@ func createTestGraphFromChannels(testChannels []*testChannel, source string) (
|
||||
channelFlags |= lnwire.ChanUpdateDisabled
|
||||
}
|
||||
|
||||
node2Features := lnwire.EmptyFeatureVector()
|
||||
if node2.testChannelPolicy != nil {
|
||||
node2Features = node2.Features
|
||||
}
|
||||
|
||||
edgePolicy := &channeldb.ChannelEdgePolicy{
|
||||
SigBytes: testSig.Serialize(),
|
||||
MessageFlags: msgFlags,
|
||||
@@ -646,6 +672,11 @@ func createTestGraphFromChannels(testChannels []*testChannel, source string) (
|
||||
MaxHTLC: node1.MaxHTLC,
|
||||
FeeBaseMSat: node1.FeeBaseMsat,
|
||||
FeeProportionalMillionths: node1.FeeRate,
|
||||
Node: &channeldb.LightningNode{
|
||||
Alias: node2.Alias,
|
||||
PubKeyBytes: node2Vertex,
|
||||
Features: node2Features,
|
||||
},
|
||||
}
|
||||
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
||||
return nil, err
|
||||
@@ -663,6 +694,11 @@ func createTestGraphFromChannels(testChannels []*testChannel, source string) (
|
||||
}
|
||||
channelFlags |= lnwire.ChanUpdateDirection
|
||||
|
||||
node1Features := lnwire.EmptyFeatureVector()
|
||||
if node1.testChannelPolicy != nil {
|
||||
node1Features = node1.Features
|
||||
}
|
||||
|
||||
edgePolicy := &channeldb.ChannelEdgePolicy{
|
||||
SigBytes: testSig.Serialize(),
|
||||
MessageFlags: msgFlags,
|
||||
@@ -674,6 +710,11 @@ func createTestGraphFromChannels(testChannels []*testChannel, source string) (
|
||||
MaxHTLC: node2.MaxHTLC,
|
||||
FeeBaseMSat: node2.FeeBaseMsat,
|
||||
FeeProportionalMillionths: node2.FeeRate,
|
||||
Node: &channeldb.LightningNode{
|
||||
Alias: node1.Alias,
|
||||
PubKeyBytes: node1Vertex,
|
||||
Features: node1Features,
|
||||
},
|
||||
}
|
||||
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
||||
return nil, err
|
||||
@@ -2980,12 +3021,6 @@ func dbFindPath(graph *channeldb.ChannelGraph,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err := routingTx.close()
|
||||
if err != nil {
|
||||
log.Errorf("Error closing db tx: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return findPath(
|
||||
&graphParams{
|
||||
|
||||
@@ -47,12 +47,7 @@ func (m *SessionSource) getRoutingGraph() (routingGraph, func(), error) {
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return routingTx, func() {
|
||||
err := routingTx.close()
|
||||
if err != nil {
|
||||
log.Errorf("Error closing db tx: %v", err)
|
||||
}
|
||||
}, nil
|
||||
return routingTx, func() {}, nil
|
||||
}
|
||||
|
||||
// NewPaymentSession creates a new payment session backed by the latest prune
|
||||
|
||||
@@ -1756,12 +1756,6 @@ func (r *ChannelRouter) FindRoute(source, target route.Vertex,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err := routingTx.close()
|
||||
if err != nil {
|
||||
log.Errorf("Error closing db tx: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
path, err := findPath(
|
||||
&graphParams{
|
||||
@@ -2763,12 +2757,6 @@ func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err := routingTx.close()
|
||||
if err != nil {
|
||||
log.Errorf("Error closing db tx: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Traverse hops backwards to accumulate fees in the running amounts.
|
||||
source := r.selfNode.PubKeyBytes
|
||||
|
||||
@@ -1393,6 +1393,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
MinHTLC: 1,
|
||||
FeeBaseMSat: 10,
|
||||
FeeProportionalMillionths: 10000,
|
||||
Node: &channeldb.LightningNode{
|
||||
PubKeyBytes: edge.NodeKey2Bytes,
|
||||
},
|
||||
}
|
||||
edgePolicy.ChannelFlags = 0
|
||||
|
||||
@@ -1409,6 +1412,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
MinHTLC: 1,
|
||||
FeeBaseMSat: 10,
|
||||
FeeProportionalMillionths: 10000,
|
||||
Node: &channeldb.LightningNode{
|
||||
PubKeyBytes: edge.NodeKey1Bytes,
|
||||
},
|
||||
}
|
||||
edgePolicy.ChannelFlags = 1
|
||||
|
||||
@@ -1490,6 +1496,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
MinHTLC: 1,
|
||||
FeeBaseMSat: 10,
|
||||
FeeProportionalMillionths: 10000,
|
||||
Node: &channeldb.LightningNode{
|
||||
PubKeyBytes: edge.NodeKey2Bytes,
|
||||
},
|
||||
}
|
||||
edgePolicy.ChannelFlags = 0
|
||||
|
||||
@@ -1505,6 +1514,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
MinHTLC: 1,
|
||||
FeeBaseMSat: 10,
|
||||
FeeProportionalMillionths: 10000,
|
||||
Node: &channeldb.LightningNode{
|
||||
PubKeyBytes: edge.NodeKey1Bytes,
|
||||
},
|
||||
}
|
||||
edgePolicy.ChannelFlags = 1
|
||||
|
||||
|
||||
@@ -69,24 +69,18 @@ func (u *unifiedPolicies) addPolicy(fromNode route.Vertex,
|
||||
// addGraphPolicies adds all policies that are known for the toNode in the
|
||||
// graph.
|
||||
func (u *unifiedPolicies) addGraphPolicies(g routingGraph) error {
|
||||
cb := func(edgeInfo *channeldb.ChannelEdgeInfo, _,
|
||||
inEdge *channeldb.ChannelEdgePolicy) error {
|
||||
|
||||
cb := func(channel *channeldb.DirectedChannel) error {
|
||||
// If there is no edge policy for this candidate node, skip.
|
||||
// Note that we are searching backwards so this node would have
|
||||
// come prior to the pivot node in the route.
|
||||
if inEdge == nil {
|
||||
if channel.InPolicy == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The node on the other end of this channel is the from node.
|
||||
fromNode, err := edgeInfo.OtherNodeKeyBytes(u.toNode[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add this policy to the unified policies map.
|
||||
u.addPolicy(fromNode, inEdge, edgeInfo.Capacity)
|
||||
u.addPolicy(
|
||||
channel.OtherNode, channel.InPolicy, channel.Capacity,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user