graph+autopilot: let autopilot use new graph ForEachNode method

Which passes a NodeRTx to the call-back instead of a `kvdb.RTx`.
This commit is contained in:
Elle Mouton committed 2025-02-10 09:46:15 +02:00
1 parent 14cedef58e
commit 9b86ee53db
6 files changed
+95 -66

No files matched your search

+31 -48
View File
@@ -10,7 +10,6 @@ import (
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
graphdb "github.com/lightningnetwork/lnd/graph/db" graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/route"
) )
@@ -51,11 +50,7 @@ func ChannelGraphFromDatabase(db *graphdb.ChannelGraph) ChannelGraph {
// channeldb.LightningNode. The wrapper method implement the autopilot.Node // channeldb.LightningNode. The wrapper method implement the autopilot.Node
// interface. // interface.
type dbNode struct { type dbNode struct {
db *graphdb.ChannelGraph tx graphdb.NodeRTx
tx kvdb.RTx
node *models.LightningNode
} }
// A compile time assertion to ensure dbNode meets the autopilot.Node // A compile time assertion to ensure dbNode meets the autopilot.Node
@@ -68,7 +63,7 @@ var _ Node = (*dbNode)(nil)
// //
// NOTE: Part of the autopilot.Node interface. // NOTE: Part of the autopilot.Node interface.
func (d *dbNode) PubKey() [33]byte { func (d *dbNode) PubKey() [33]byte {
return d.node.PubKeyBytes return d.tx.Node().PubKeyBytes
} }
// Addrs returns a slice of publicly reachable public TCP addresses that the // Addrs returns a slice of publicly reachable public TCP addresses that the
@@ -76,7 +71,7 @@ func (d *dbNode) PubKey() [33]byte {
// //
// NOTE: Part of the autopilot.Node interface. // NOTE: Part of the autopilot.Node interface.
func (d *dbNode) Addrs() []net.Addr { func (d *dbNode) Addrs() []net.Addr {
return d.node.Addresses return d.tx.Node().Addresses
} }
// ForEachChannel is a higher-order function that will be used to iterate // ForEachChannel is a higher-order function that will be used to iterate
@@ -86,43 +81,35 @@ func (d *dbNode) Addrs() []net.Addr {
// //
// NOTE: Part of the autopilot.Node interface. // NOTE: Part of the autopilot.Node interface.
func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error { func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
return d.db.ForEachNodeChannelTx(d.tx, d.node.PubKeyBytes, return d.tx.ForEachChannel(func(ei *models.ChannelEdgeInfo, ep,
func(tx kvdb.RTx, ei *models.ChannelEdgeInfo, ep, _ *models.ChannelEdgePolicy) error {
_ *models.ChannelEdgePolicy) error {
// Skip channels for which no outgoing edge policy is // Skip channels for which no outgoing edge policy is available.
// available. //
// // TODO(joostjager): Ideally the case where channels have a nil
// TODO(joostjager): Ideally the case where channels // policy should be supported, as autopilot is not looking at
// have a nil policy should be supported, as autopilot // the policies. For now, it is not easily possible to get a
// is not looking at the policies. For now, it is not // reference to the other end LightningNode object without
// easily possible to get a reference to the other end // retrieving the policy.
// LightningNode object without retrieving the policy. if ep == nil {
if ep == nil { return nil
return nil }
}
node, err := d.db.FetchLightningNodeTx( node, err := d.tx.FetchNode(ep.ToNode)
tx, ep.ToNode, if err != nil {
) return err
if err != nil { }
return err
}
edge := ChannelEdge{ edge := ChannelEdge{
ChanID: lnwire.NewShortChanIDFromInt( ChanID: lnwire.NewShortChanIDFromInt(ep.ChannelID),
ep.ChannelID, Capacity: ei.Capacity,
), Peer: &dbNode{
Capacity: ei.Capacity, tx: node,
Peer: &dbNode{ },
tx: tx, }
db: d.db,
node: node,
},
}
return cb(edge) return cb(edge)
}) })
} }
// ForEachNode is a higher-order function that should be called once for each // ForEachNode is a higher-order function that should be called once for each
@@ -131,20 +118,16 @@ func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
// //
// NOTE: Part of the autopilot.ChannelGraph interface. // NOTE: Part of the autopilot.ChannelGraph interface.
func (d *databaseChannelGraph) ForEachNode(cb func(Node) error) error { func (d *databaseChannelGraph) ForEachNode(cb func(Node) error) error {
return d.db.ForEachNode(func(tx kvdb.RTx, return d.db.ForEachNode(func(nodeTx graphdb.NodeRTx) error {
n *models.LightningNode) error {
// We'll skip over any node that doesn't have any advertised // We'll skip over any node that doesn't have any advertised
// addresses. As we won't be able to reach them to actually // addresses. As we won't be able to reach them to actually
// open any channels. // open any channels.
if len(n.Addresses) == 0 { if len(nodeTx.Node().Addresses) == 0 {
return nil return nil
} }
node := &dbNode{ node := &dbNode{
db: d.db, tx: nodeTx,
tx: tx,
node: n,
} }
return cb(node) return cb(node)
}) })
+40 -6
View File
@@ -514,18 +514,18 @@ func (d *testDBGraph) addRandChannel(node1, node2 *btcec.PublicKey,
return &ChannelEdge{ return &ChannelEdge{
ChanID: chanID, ChanID: chanID,
Capacity: capacity, Capacity: capacity,
Peer: &dbNode{ Peer: &dbNode{tx: &testNodeTx{
db: d.db, db: d,
node: vertex1, node: vertex1,
}, }},
}, },
&ChannelEdge{ &ChannelEdge{
ChanID: chanID, ChanID: chanID,
Capacity: capacity, Capacity: capacity,
Peer: &dbNode{ Peer: &dbNode{tx: &testNodeTx{
db: d.db, db: d,
node: vertex2, node: vertex2,
}, }},
}, },
nil nil
} }
@@ -702,3 +702,37 @@ func (m *memChannelGraph) addRandNode() (*btcec.PublicKey, error) {
return newPub, nil return newPub, nil
} }
type testNodeTx struct {
db *testDBGraph
node *models.LightningNode
}
func (t *testNodeTx) Node() *models.LightningNode {
return t.node
}
func (t *testNodeTx) ForEachChannel(f func(*models.ChannelEdgeInfo,
*models.ChannelEdgePolicy, *models.ChannelEdgePolicy) error) error {
return t.db.db.ForEachNodeChannel(t.node.PubKeyBytes, func(_ kvdb.RTx,
edge *models.ChannelEdgeInfo, policy1,
policy2 *models.ChannelEdgePolicy) error {
return f(edge, policy1, policy2)
})
}
func (t *testNodeTx) FetchNode(pub route.Vertex) (graphdb.NodeRTx, error) {
node, err := t.db.db.FetchLightningNode(pub)
if err != nil {
return nil, err
}
return &testNodeTx{
db: t.db,
node: node,
}, nil
}
var _ graphdb.NodeRTx = (*testNodeTx)(nil)
+18 -4
View File
@@ -588,9 +588,9 @@ func (c *ChannelGraph) FetchNodeFeatures(
} }
} }
// ForEachNodeCached is similar to ForEachNode, but it utilizes the channel // ForEachNodeCached is similar to forEachNode, but it utilizes the channel
// graph cache instead. Note that this doesn't return all the information the // graph cache instead. Note that this doesn't return all the information the
// regular ForEachNode method does. // regular forEachNode method does.
// //
// NOTE: The callback contents MUST not be modified. // NOTE: The callback contents MUST not be modified.
func (c *ChannelGraph) ForEachNodeCached(cb func(node route.Vertex, func (c *ChannelGraph) ForEachNodeCached(cb func(node route.Vertex,
@@ -604,7 +604,7 @@ func (c *ChannelGraph) ForEachNodeCached(cb func(node route.Vertex,
// We'll iterate over each node, then the set of channels for each // We'll iterate over each node, then the set of channels for each
// node, and construct a similar callback functiopn signature as the // node, and construct a similar callback functiopn signature as the
// main funcotin expects. // main funcotin expects.
return c.ForEachNode(func(tx kvdb.RTx, return c.forEachNode(func(tx kvdb.RTx,
node *models.LightningNode) error { node *models.LightningNode) error {
channels := make(map[uint64]*DirectedChannel) channels := make(map[uint64]*DirectedChannel)
@@ -716,11 +716,25 @@ func (c *ChannelGraph) DisabledChannelIDs() ([]uint64, error) {
// ForEachNode iterates through all the stored vertices/nodes in the graph, // ForEachNode iterates through all the stored vertices/nodes in the graph,
// executing the passed callback with each node encountered. If the callback // executing the passed callback with each node encountered. If the callback
// returns an error, then the transaction is aborted and the iteration stops // returns an error, then the transaction is aborted and the iteration stops
// early. Any operations performed on the NodeTx passed to the call-back are
// executed under the same read transaction and so, methods on the NodeTx object
// _MUST_ only be called from within the call-back.
func (c *ChannelGraph) ForEachNode(cb func(tx NodeRTx) error) error {
return c.forEachNode(func(tx kvdb.RTx,
node *models.LightningNode) error {
return cb(newChanGraphNodeTx(tx, c, node))
})
}
// forEachNode iterates through all the stored vertices/nodes in the graph,
// executing the passed callback with each node encountered. If the callback
// returns an error, then the transaction is aborted and the iteration stops
// early. // early.
// //
// TODO(roasbeef): add iterator interface to allow for memory efficient graph // TODO(roasbeef): add iterator interface to allow for memory efficient graph
// traversal when graph gets mega // traversal when graph gets mega
func (c *ChannelGraph) ForEachNode( func (c *ChannelGraph) forEachNode(
cb func(kvdb.RTx, *models.LightningNode) error) error { cb func(kvdb.RTx, *models.LightningNode) error) error {
traversal := func(tx kvdb.RTx) error { traversal := func(tx kvdb.RTx) error {
+1 -1
View File
@@ -121,7 +121,7 @@ func TestGraphCacheAddNode(t *testing.T) {
assertCachedPolicyEqual(t, outPolicy1, toChannels[0].InPolicy) assertCachedPolicyEqual(t, outPolicy1, toChannels[0].InPolicy)
// Now that we've inserted two nodes into the graph, check that // Now that we've inserted two nodes into the graph, check that
// we'll recover the same set of channels during ForEachNode. // we'll recover the same set of channels during forEachNode.
nodes := make(map[route.Vertex]struct{}) nodes := make(map[route.Vertex]struct{})
chans := make(map[uint64]struct{}) chans := make(map[uint64]struct{})
_ = cache.ForEachNode(func(node route.Vertex, _ = cache.ForEachNode(func(node route.Vertex,
+3 -3
View File
@@ -1092,7 +1092,7 @@ func TestGraphTraversalCacheable(t *testing.T) {
// Create a map of all nodes with the iteration we know works (because // Create a map of all nodes with the iteration we know works (because
// it is tested in another test). // it is tested in another test).
nodeMap := make(map[route.Vertex]struct{}) nodeMap := make(map[route.Vertex]struct{})
err = graph.ForEachNode( err = graph.forEachNode(
func(tx kvdb.RTx, n *models.LightningNode) error { func(tx kvdb.RTx, n *models.LightningNode) error {
nodeMap[n.PubKeyBytes] = struct{}{} nodeMap[n.PubKeyBytes] = struct{}{}
@@ -1217,7 +1217,7 @@ func fillTestGraph(t require.TestingT, graph *ChannelGraph, numNodes,
// Iterate over each node as returned by the graph, if all nodes are // Iterate over each node as returned by the graph, if all nodes are
// reached, then the map created above should be empty. // reached, then the map created above should be empty.
err := graph.ForEachNode( err := graph.forEachNode(
func(_ kvdb.RTx, node *models.LightningNode) error { func(_ kvdb.RTx, node *models.LightningNode) error {
delete(nodeIndex, node.Alias) delete(nodeIndex, node.Alias)
return nil return nil
@@ -1329,7 +1329,7 @@ func assertNumChans(t *testing.T, graph *ChannelGraph, n int) {
func assertNumNodes(t *testing.T, graph *ChannelGraph, n int) { func assertNumNodes(t *testing.T, graph *ChannelGraph, n int) {
numNodes := 0 numNodes := 0
err := graph.ForEachNode( err := graph.forEachNode(
func(_ kvdb.RTx, _ *models.LightningNode) error { func(_ kvdb.RTx, _ *models.LightningNode) error {
numNodes++ numNodes++
return nil return nil
+2 -4
View File
@@ -6533,10 +6533,8 @@ func (r *rpcServer) DescribeGraph(ctx context.Context,
// First iterate through all the known nodes (connected or unconnected // First iterate through all the known nodes (connected or unconnected
// within the graph), collating their current state into the RPC // within the graph), collating their current state into the RPC
// response. // response.
err := graph.ForEachNode(func(_ kvdb.RTx, err := graph.ForEachNode(func(nodeTx graphdb.NodeRTx) error {
node *models.LightningNode) error { lnNode := marshalNode(nodeTx.Node())
lnNode := marshalNode(node)
resp.Nodes = append(resp.Nodes, lnNode) resp.Nodes = append(resp.Nodes, lnNode)