mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-21 14:04:06 +01:00
Merge pull request #8848 from ellemouton/graphManager
refactor: move graph responsibilities from routing.ChannelRouter to new graph.Builder
This commit is contained in:
commit
fae7e0c5b0
45 changed files with 6094 additions and 4330 deletions
|
@ -89,7 +89,7 @@ func (d *dbNode) Addrs() []net.Addr {
|
|||
//
|
||||
// NOTE: Part of the autopilot.Node interface.
|
||||
func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
|
||||
return d.db.ForEachNodeChannel(d.tx, d.node.PubKeyBytes,
|
||||
return d.db.ForEachNodeChannelTx(d.tx, d.node.PubKeyBytes,
|
||||
func(tx kvdb.RTx, ei *models.ChannelEdgeInfo, ep,
|
||||
_ *models.ChannelEdgePolicy) error {
|
||||
|
||||
|
@ -105,7 +105,9 @@ func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
node, err := d.db.FetchLightningNode(tx, ep.ToNode)
|
||||
node, err := d.db.FetchLightningNodeTx(
|
||||
tx, ep.ToNode,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -164,7 +166,7 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
dbNode, err := d.db.FetchLightningNode(nil, vertex)
|
||||
dbNode, err := d.db.FetchLightningNode(vertex)
|
||||
switch {
|
||||
case err == channeldb.ErrGraphNodeNotFound:
|
||||
fallthrough
|
||||
|
|
|
@ -6,9 +6,9 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
)
|
||||
|
||||
// ManagerCfg houses a set of values and methods that is passed to the Manager
|
||||
|
@ -36,7 +36,7 @@ type ManagerCfg struct {
|
|||
|
||||
// SubscribeTopology is used to get a subscription for topology changes
|
||||
// on the network.
|
||||
SubscribeTopology func() (*routing.TopologyClient, error)
|
||||
SubscribeTopology func() (*graph.TopologyClient, error)
|
||||
}
|
||||
|
||||
// Manager is struct that manages an autopilot agent, making it possible to
|
||||
|
|
|
@ -1351,7 +1351,7 @@ func (d *DB) AddrsForNode(nodePub *btcec.PublicKey) ([]net.Addr,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
graphNode, err := d.graph.FetchLightningNode(nil, pubKey)
|
||||
graphNode, err := d.graph.FetchLightningNode(pubKey)
|
||||
if err != nil && err != ErrGraphNodeNotFound {
|
||||
return nil, err
|
||||
} else if err == ErrGraphNodeNotFound {
|
||||
|
|
|
@ -529,7 +529,7 @@ func (c *ChannelGraph) FetchNodeFeatures(
|
|||
}
|
||||
|
||||
// Fallback that uses the database.
|
||||
targetNode, err := c.FetchLightningNode(nil, node)
|
||||
targetNode, err := c.FetchLightningNode(node)
|
||||
switch err {
|
||||
// If the node exists and has features, return them directly.
|
||||
case nil:
|
||||
|
@ -565,7 +565,7 @@ func (c *ChannelGraph) ForEachNodeCached(cb func(node route.Vertex,
|
|||
return c.ForEachNode(func(tx kvdb.RTx, node *LightningNode) error {
|
||||
channels := make(map[uint64]*DirectedChannel)
|
||||
|
||||
err := c.ForEachNodeChannel(tx, node.PubKeyBytes,
|
||||
err := c.ForEachNodeChannelTx(tx, node.PubKeyBytes,
|
||||
func(tx kvdb.RTx, e *models.ChannelEdgeInfo,
|
||||
p1 *models.ChannelEdgePolicy,
|
||||
p2 *models.ChannelEdgePolicy) error {
|
||||
|
@ -2374,10 +2374,19 @@ func (c *ChannelGraph) FilterChannelRange(startHeight,
|
|||
// skipped and the result will contain only those edges that exist at the time
|
||||
// of the query. This can be used to respond to peer queries that are seeking to
|
||||
// fill in gaps in their view of the channel graph.
|
||||
func (c *ChannelGraph) FetchChanInfos(chanIDs []uint64) ([]ChannelEdge, error) {
|
||||
return c.fetchChanInfos(nil, chanIDs)
|
||||
}
|
||||
|
||||
// fetchChanInfos returns the set of channel edges that correspond to the passed
|
||||
// channel ID's. If an edge is the query is unknown to the database, it will
|
||||
// skipped and the result will contain only those edges that exist at the time
|
||||
// of the query. This can be used to respond to peer queries that are seeking to
|
||||
// fill in gaps in their view of the channel graph.
|
||||
//
|
||||
// NOTE: An optional transaction may be provided. If none is provided, then a
|
||||
// new one will be created.
|
||||
func (c *ChannelGraph) FetchChanInfos(tx kvdb.RTx, chanIDs []uint64) (
|
||||
func (c *ChannelGraph) fetchChanInfos(tx kvdb.RTx, chanIDs []uint64) (
|
||||
[]ChannelEdge, error) {
|
||||
// TODO(roasbeef): sort cids?
|
||||
|
||||
|
@ -2922,7 +2931,7 @@ func (c *ChannelGraph) isPublic(tx kvdb.RTx, nodePub route.Vertex,
|
|||
// used to terminate the check early.
|
||||
nodeIsPublic := false
|
||||
errDone := errors.New("done")
|
||||
err := c.ForEachNodeChannel(tx, nodePub, func(tx kvdb.RTx,
|
||||
err := c.ForEachNodeChannelTx(tx, nodePub, func(tx kvdb.RTx,
|
||||
info *models.ChannelEdgeInfo, _ *models.ChannelEdgePolicy,
|
||||
_ *models.ChannelEdgePolicy) error {
|
||||
|
||||
|
@ -2954,12 +2963,31 @@ func (c *ChannelGraph) isPublic(tx kvdb.RTx, nodePub route.Vertex,
|
|||
return nodeIsPublic, nil
|
||||
}
|
||||
|
||||
// FetchLightningNodeTx attempts to look up a target node by its identity
|
||||
// public key. If the node isn't found in the database, then
|
||||
// ErrGraphNodeNotFound is returned. An optional transaction may be provided.
|
||||
// If none is provided, then a new one will be created.
|
||||
func (c *ChannelGraph) FetchLightningNodeTx(tx kvdb.RTx, nodePub route.Vertex) (
|
||||
*LightningNode, error) {
|
||||
|
||||
return c.fetchLightningNode(tx, nodePub)
|
||||
}
|
||||
|
||||
// FetchLightningNode attempts to look up a target node by its identity public
|
||||
// key. If the node isn't found in the database, then ErrGraphNodeNotFound is
|
||||
// returned.
|
||||
func (c *ChannelGraph) FetchLightningNode(nodePub route.Vertex) (*LightningNode,
|
||||
error) {
|
||||
|
||||
return c.fetchLightningNode(nil, nodePub)
|
||||
}
|
||||
|
||||
// fetchLightningNode attempts to look up a target node by its identity public
|
||||
// key. If the node isn't found in the database, then ErrGraphNodeNotFound is
|
||||
// returned. An optional transaction may be provided. If none is provided, then
|
||||
// a new one will be created.
|
||||
func (c *ChannelGraph) FetchLightningNode(tx kvdb.RTx, nodePub route.Vertex) (
|
||||
*LightningNode, error) {
|
||||
func (c *ChannelGraph) fetchLightningNode(tx kvdb.RTx,
|
||||
nodePub route.Vertex) (*LightningNode, error) {
|
||||
|
||||
var node *LightningNode
|
||||
fetch := func(tx kvdb.RTx) error {
|
||||
|
@ -3196,13 +3224,29 @@ func nodeTraversal(tx kvdb.RTx, nodePub []byte, db kvdb.Backend,
|
|||
// halted with the error propagated back up to the caller.
|
||||
//
|
||||
// Unknown policies are passed into the callback as nil values.
|
||||
func (c *ChannelGraph) ForEachNodeChannel(nodePub route.Vertex,
|
||||
cb func(kvdb.RTx, *models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy) error) error {
|
||||
|
||||
return nodeTraversal(nil, nodePub[:], c.db, cb)
|
||||
}
|
||||
|
||||
// ForEachNodeChannelTx iterates through all channels of the given node,
|
||||
// executing the passed callback with an edge info structure and the policies
|
||||
// of each end of the channel. The first edge policy is the outgoing edge *to*
|
||||
// the connecting node, while the second is the incoming edge *from* the
|
||||
// connecting node. If the callback returns an error, then the iteration is
|
||||
// halted with the error propagated back up to the caller.
|
||||
//
|
||||
// Unknown policies are passed into the callback as nil values.
|
||||
//
|
||||
// If the caller wishes to re-use an existing boltdb transaction, then it
|
||||
// should be passed as the first argument. Otherwise the first argument should
|
||||
// should be passed as the first argument. Otherwise, the first argument should
|
||||
// be nil and a fresh transaction will be created to execute the graph
|
||||
// traversal.
|
||||
func (c *ChannelGraph) ForEachNodeChannel(tx kvdb.RTx, nodePub route.Vertex,
|
||||
cb func(kvdb.RTx, *models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
func (c *ChannelGraph) ForEachNodeChannelTx(tx kvdb.RTx,
|
||||
nodePub route.Vertex, cb func(kvdb.RTx, *models.ChannelEdgeInfo,
|
||||
*models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy) error) error {
|
||||
|
||||
return nodeTraversal(tx, nodePub[:], c.db, cb)
|
||||
|
@ -3705,7 +3749,7 @@ func (c *ChannelGraph) markEdgeLiveUnsafe(tx kvdb.RwTx, chanID uint64) error {
|
|||
// We need to add the channel back into our graph cache, otherwise we
|
||||
// won't use it for path finding.
|
||||
if c.graphCache != nil {
|
||||
edgeInfos, err := c.FetchChanInfos(tx, []uint64{chanID})
|
||||
edgeInfos, err := c.fetchChanInfos(tx, []uint64{chanID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
|
|||
|
||||
// Next, fetch the node from the database to ensure everything was
|
||||
// serialized properly.
|
||||
dbNode, err := graph.FetchLightningNode(nil, testPub)
|
||||
dbNode, err := graph.FetchLightningNode(testPub)
|
||||
require.NoError(t, err, "unable to locate node")
|
||||
|
||||
if _, exists, err := graph.HasLightningNode(dbNode.PubKeyBytes); err != nil {
|
||||
|
@ -164,7 +164,7 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
|
|||
|
||||
// Finally, attempt to fetch the node again. This should fail as the
|
||||
// node should have been deleted from the database.
|
||||
_, err = graph.FetchLightningNode(nil, testPub)
|
||||
_, err = graph.FetchLightningNode(testPub)
|
||||
if err != ErrGraphNodeNotFound {
|
||||
t.Fatalf("fetch after delete should fail!")
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ func TestPartialNode(t *testing.T) {
|
|||
|
||||
// Next, fetch the node from the database to ensure everything was
|
||||
// serialized properly.
|
||||
dbNode, err := graph.FetchLightningNode(nil, testPub)
|
||||
dbNode, err := graph.FetchLightningNode(testPub)
|
||||
require.NoError(t, err, "unable to locate node")
|
||||
|
||||
if _, exists, err := graph.HasLightningNode(dbNode.PubKeyBytes); err != nil {
|
||||
|
@ -222,7 +222,7 @@ func TestPartialNode(t *testing.T) {
|
|||
|
||||
// Finally, attempt to fetch the node again. This should fail as the
|
||||
// node should have been deleted from the database.
|
||||
_, err = graph.FetchLightningNode(nil, testPub)
|
||||
_, err = graph.FetchLightningNode(testPub)
|
||||
if err != ErrGraphNodeNotFound {
|
||||
t.Fatalf("fetch after delete should fail!")
|
||||
}
|
||||
|
@ -1055,7 +1055,7 @@ func TestGraphTraversal(t *testing.T) {
|
|||
// outgoing channels for a particular node.
|
||||
numNodeChans := 0
|
||||
firstNode, secondNode := nodeList[0], nodeList[1]
|
||||
err = graph.ForEachNodeChannel(nil, firstNode.PubKeyBytes,
|
||||
err = graph.ForEachNodeChannel(firstNode.PubKeyBytes,
|
||||
func(_ kvdb.RTx, _ *models.ChannelEdgeInfo, outEdge,
|
||||
inEdge *models.ChannelEdgePolicy) error {
|
||||
|
||||
|
@ -2685,7 +2685,7 @@ func TestFetchChanInfos(t *testing.T) {
|
|||
// We'll now attempt to query for the range of channel ID's we just
|
||||
// inserted into the database. We should get the exact same set of
|
||||
// edges back.
|
||||
resp, err := graph.FetchChanInfos(nil, edgeQuery)
|
||||
resp, err := graph.FetchChanInfos(edgeQuery)
|
||||
require.NoError(t, err, "unable to fetch chan edges")
|
||||
if len(resp) != len(edges) {
|
||||
t.Fatalf("expected %v edges, instead got %v", len(edges),
|
||||
|
@ -2737,7 +2737,7 @@ func TestIncompleteChannelPolicies(t *testing.T) {
|
|||
// Ensure that channel is reported with unknown policies.
|
||||
checkPolicies := func(node *LightningNode, expectedIn, expectedOut bool) {
|
||||
calls := 0
|
||||
err := graph.ForEachNodeChannel(nil, node.PubKeyBytes,
|
||||
err := graph.ForEachNodeChannel(node.PubKeyBytes,
|
||||
func(_ kvdb.RTx, _ *models.ChannelEdgeInfo, outEdge,
|
||||
inEdge *models.ChannelEdgePolicy) error {
|
||||
|
||||
|
@ -3014,7 +3014,7 @@ func TestPruneGraphNodes(t *testing.T) {
|
|||
|
||||
// Finally, we'll ensure that node3, the only fully unconnected node as
|
||||
// properly deleted from the graph and not another node in its place.
|
||||
_, err = graph.FetchLightningNode(nil, node3.PubKeyBytes)
|
||||
_, err = graph.FetchLightningNode(node3.PubKeyBytes)
|
||||
if err == nil {
|
||||
t.Fatalf("node 3 should have been deleted!")
|
||||
}
|
||||
|
@ -3048,13 +3048,13 @@ func TestAddChannelEdgeShellNodes(t *testing.T) {
|
|||
|
||||
// Ensure that node1 was inserted as a full node, while node2 only has
|
||||
// a shell node present.
|
||||
node1, err = graph.FetchLightningNode(nil, node1.PubKeyBytes)
|
||||
node1, err = graph.FetchLightningNode(node1.PubKeyBytes)
|
||||
require.NoError(t, err, "unable to fetch node1")
|
||||
if !node1.HaveNodeAnnouncement {
|
||||
t.Fatalf("have shell announcement for node1, shouldn't")
|
||||
}
|
||||
|
||||
node2, err = graph.FetchLightningNode(nil, node2.PubKeyBytes)
|
||||
node2, err = graph.FetchLightningNode(node2.PubKeyBytes)
|
||||
require.NoError(t, err, "unable to fetch node2")
|
||||
if node2.HaveNodeAnnouncement {
|
||||
t.Fatalf("should have shell announcement for node2, but is full")
|
||||
|
|
141
channeldb/graphsession/graph_session.go
Normal file
141
channeldb/graphsession/graph_session.go
Normal file
|
@ -0,0 +1,141 @@
|
|||
package graphsession
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
// Factory implements the routing.GraphSessionFactory and can be used to start
|
||||
// a session with a ReadOnlyGraph.
|
||||
type Factory struct {
|
||||
graph ReadOnlyGraph
|
||||
}
|
||||
|
||||
// NewGraphSessionFactory constructs a new Factory which can then be used to
|
||||
// start a new session.
|
||||
func NewGraphSessionFactory(graph ReadOnlyGraph) routing.GraphSessionFactory {
|
||||
return &Factory{
|
||||
graph: graph,
|
||||
}
|
||||
}
|
||||
|
||||
// NewGraphSession will produce a new Graph to use for a path-finding session.
|
||||
// It returns the Graph along with a call-back that must be called once Graph
|
||||
// access is complete. This call-back will close any read-only transaction that
|
||||
// was created at Graph construction time.
|
||||
//
|
||||
// NOTE: This is part of the routing.GraphSessionFactory interface.
|
||||
func (g *Factory) NewGraphSession() (routing.Graph, func() error, error) {
|
||||
tx, err := g.graph.NewPathFindTx()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
session := &session{
|
||||
graph: g.graph,
|
||||
tx: tx,
|
||||
}
|
||||
|
||||
return session, session.close, nil
|
||||
}
|
||||
|
||||
// A compile-time check to ensure that Factory implements the
|
||||
// routing.GraphSessionFactory interface.
|
||||
var _ routing.GraphSessionFactory = (*Factory)(nil)
|
||||
|
||||
// session is an implementation of the routing.Graph interface where the same
|
||||
// read-only transaction is held across calls to the graph and can be used to
|
||||
// access the backing channel graph.
|
||||
type session struct {
|
||||
graph graph
|
||||
tx kvdb.RTx
|
||||
}
|
||||
|
||||
// NewRoutingGraph constructs a session that which does not first start a
|
||||
// read-only transaction and so each call on the routing.Graph will create a
|
||||
// new transaction.
|
||||
func NewRoutingGraph(graph ReadOnlyGraph) routing.Graph {
|
||||
return &session{
|
||||
graph: graph,
|
||||
}
|
||||
}
|
||||
|
||||
// close closes the read-only transaction being used to access the backing
|
||||
// graph. If no transaction was started then this is a no-op.
|
||||
func (g *session) close() error {
|
||||
if g.tx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := g.tx.Rollback()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error closing db tx: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForEachNodeChannel calls the callback for every channel of the given node.
|
||||
//
|
||||
// NOTE: Part of the routing.Graph interface.
|
||||
func (g *session) ForEachNodeChannel(nodePub route.Vertex,
|
||||
cb func(channel *channeldb.DirectedChannel) error) error {
|
||||
|
||||
return g.graph.ForEachNodeDirectedChannel(g.tx, nodePub, cb)
|
||||
}
|
||||
|
||||
// FetchNodeFeatures returns the features of the given node. If the node is
|
||||
// unknown, assume no additional features are supported.
|
||||
//
|
||||
// NOTE: Part of the routing.Graph interface.
|
||||
func (g *session) FetchNodeFeatures(nodePub route.Vertex) (
|
||||
*lnwire.FeatureVector, error) {
|
||||
|
||||
return g.graph.FetchNodeFeatures(nodePub)
|
||||
}
|
||||
|
||||
// A compile-time check to ensure that *session implements the
|
||||
// routing.Graph interface.
|
||||
var _ routing.Graph = (*session)(nil)
|
||||
|
||||
// ReadOnlyGraph is a graph extended with a call to create a new read-only
|
||||
// transaction that can then be used to make further queries to the graph.
|
||||
type ReadOnlyGraph interface {
|
||||
// NewPathFindTx returns a new read transaction that can be used for a
|
||||
// single path finding session. Will return nil if the graph cache is
|
||||
// enabled.
|
||||
NewPathFindTx() (kvdb.RTx, error)
|
||||
|
||||
graph
|
||||
}
|
||||
|
||||
// graph describes the API necessary for a graph source to have access to on a
|
||||
// database implementation, like channeldb.ChannelGraph, in order to be used by
|
||||
// the Router for pathfinding.
|
||||
type graph interface {
|
||||
// ForEachNodeDirectedChannel iterates through all channels of a given
|
||||
// node, executing the passed callback on the directed edge representing
|
||||
// the channel and its incoming policy. If the callback returns an
|
||||
// error, then the iteration is halted with the error propagated back
|
||||
// up to the caller.
|
||||
//
|
||||
// Unknown policies are passed into the callback as nil values.
|
||||
//
|
||||
// NOTE: if a nil tx is provided, then it is expected that the
|
||||
// implementation create a read only tx.
|
||||
ForEachNodeDirectedChannel(tx kvdb.RTx, node route.Vertex,
|
||||
cb func(channel *channeldb.DirectedChannel) error) error
|
||||
|
||||
// FetchNodeFeatures returns the features of a given node. If no
|
||||
// features are known for the node, an empty feature vector is returned.
|
||||
FetchNodeFeatures(node route.Vertex) (*lnwire.FeatureVector, error)
|
||||
}
|
||||
|
||||
// A compile-time check to ensure that *channeldb.ChannelGraph implements the
|
||||
// graph interface.
|
||||
var _ graph = (*channeldb.ChannelGraph)(nil)
|
|
@ -5,9 +5,9 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/netann"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
|
@ -136,7 +136,7 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash,
|
|||
if edge1 != nil {
|
||||
// We don't want to send channel updates that don't
|
||||
// conform to the spec (anymore).
|
||||
err := routing.ValidateChannelUpdateFields(0, edge1)
|
||||
err := graph.ValidateChannelUpdateFields(0, edge1)
|
||||
if err != nil {
|
||||
log.Errorf("not sending invalid channel "+
|
||||
"update %v: %v", edge1, err)
|
||||
|
@ -145,7 +145,7 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash,
|
|||
}
|
||||
}
|
||||
if edge2 != nil {
|
||||
err := routing.ValidateChannelUpdateFields(0, edge2)
|
||||
err := graph.ValidateChannelUpdateFields(0, edge2)
|
||||
if err != nil {
|
||||
log.Errorf("not sending invalid channel "+
|
||||
"update %v: %v", edge2, err)
|
||||
|
@ -249,7 +249,7 @@ func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash,
|
|||
chanIDs = append(chanIDs, chanID.ToUint64())
|
||||
}
|
||||
|
||||
channels, err := c.graph.FetchChanInfos(nil, chanIDs)
|
||||
channels, err := c.graph.FetchChanInfos(chanIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
"github.com/lightningnetwork/lnd/lnpeer"
|
||||
|
@ -28,7 +29,6 @@ import (
|
|||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/multimutex"
|
||||
"github.com/lightningnetwork/lnd/netann"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/ticker"
|
||||
"golang.org/x/time/rate"
|
||||
|
@ -165,11 +165,11 @@ type Config struct {
|
|||
// * also need to do same for Notifier
|
||||
ChainHash chainhash.Hash
|
||||
|
||||
// Router is the subsystem which is responsible for managing the
|
||||
// Graph is the subsystem which is responsible for managing the
|
||||
// topology of lightning network. After incoming channel, node, channel
|
||||
// updates announcements are validated they are sent to the router in
|
||||
// order to be included in the LN graph.
|
||||
Router routing.ChannelGraphSource
|
||||
Graph graph.ChannelGraphSource
|
||||
|
||||
// ChanSeries is an interfaces that provides access to a time series
|
||||
// view of the current known channel graph. Each GossipSyncer enabled
|
||||
|
@ -591,7 +591,7 @@ func (d *AuthenticatedGossiper) start() error {
|
|||
}
|
||||
d.blockEpochs = blockEpochs
|
||||
|
||||
height, err := d.cfg.Router.CurrentBlockHeight()
|
||||
height, err := d.cfg.Graph.CurrentBlockHeight()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1360,7 +1360,7 @@ func (d *AuthenticatedGossiper) networkHandler() {
|
|||
|
||||
// We'll use this validation to ensure that we process jobs in their
|
||||
// dependency order during parallel validation.
|
||||
validationBarrier := routing.NewValidationBarrier(1000, d.quit)
|
||||
validationBarrier := graph.NewValidationBarrier(1000, d.quit)
|
||||
|
||||
for {
|
||||
select {
|
||||
|
@ -1485,7 +1485,7 @@ func (d *AuthenticatedGossiper) networkHandler() {
|
|||
//
|
||||
// NOTE: must be run as a goroutine.
|
||||
func (d *AuthenticatedGossiper) handleNetworkMessages(nMsg *networkMsg,
|
||||
deDuped *deDupedAnnouncements, vb *routing.ValidationBarrier) {
|
||||
deDuped *deDupedAnnouncements, vb *graph.ValidationBarrier) {
|
||||
|
||||
defer d.wg.Done()
|
||||
defer vb.CompleteJob()
|
||||
|
@ -1501,10 +1501,10 @@ func (d *AuthenticatedGossiper) handleNetworkMessages(nMsg *networkMsg,
|
|||
log.Debugf("Validating network message %s got err: %v",
|
||||
nMsg.msg.MsgType(), err)
|
||||
|
||||
if !routing.IsError(
|
||||
if !graph.IsError(
|
||||
err,
|
||||
routing.ErrVBarrierShuttingDown,
|
||||
routing.ErrParentValidationFailed,
|
||||
graph.ErrVBarrierShuttingDown,
|
||||
graph.ErrParentValidationFailed,
|
||||
) {
|
||||
|
||||
log.Warnf("unexpected error during validation "+
|
||||
|
@ -1595,7 +1595,7 @@ func (d *AuthenticatedGossiper) retransmitStaleAnns(now time.Time) error {
|
|||
havePublicChannels bool
|
||||
edgesToUpdate []updateTuple
|
||||
)
|
||||
err := d.cfg.Router.ForAllOutgoingChannels(func(
|
||||
err := d.cfg.Graph.ForAllOutgoingChannels(func(
|
||||
_ kvdb.RTx,
|
||||
info *models.ChannelEdgeInfo,
|
||||
edge *models.ChannelEdgePolicy) error {
|
||||
|
@ -1831,7 +1831,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge(
|
|||
|
||||
// First, we'll fetch the state of the channel as we know if from the
|
||||
// database.
|
||||
chanInfo, e1, e2, err := d.cfg.Router.GetChannelByID(
|
||||
chanInfo, e1, e2, err := d.cfg.Graph.GetChannelByID(
|
||||
chanAnnMsg.ShortChannelID,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -1860,7 +1860,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = routing.ValidateChannelAnn(chanAnn)
|
||||
err = graph.ValidateChannelAnn(chanAnn)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("assembled channel announcement proof "+
|
||||
"for shortChanID=%v isn't valid: %v",
|
||||
|
@ -1871,7 +1871,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge(
|
|||
|
||||
// If everything checks out, then we'll add the fully assembled proof
|
||||
// to the database.
|
||||
err = d.cfg.Router.AddProof(chanAnnMsg.ShortChannelID, proof)
|
||||
err = d.cfg.Graph.AddProof(chanAnnMsg.ShortChannelID, proof)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("unable add proof to shortChanID=%v: %w",
|
||||
chanAnnMsg.ShortChannelID, err)
|
||||
|
@ -1909,7 +1909,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge(
|
|||
func (d *AuthenticatedGossiper) addNode(msg *lnwire.NodeAnnouncement,
|
||||
op ...batch.SchedulerOption) error {
|
||||
|
||||
if err := routing.ValidateNodeAnn(msg); err != nil {
|
||||
if err := graph.ValidateNodeAnn(msg); err != nil {
|
||||
return fmt.Errorf("unable to validate node announcement: %w",
|
||||
err)
|
||||
}
|
||||
|
@ -1928,7 +1928,7 @@ func (d *AuthenticatedGossiper) addNode(msg *lnwire.NodeAnnouncement,
|
|||
ExtraOpaqueData: msg.ExtraOpaqueData,
|
||||
}
|
||||
|
||||
return d.cfg.Router.AddNode(node, op...)
|
||||
return d.cfg.Graph.AddNode(node, op...)
|
||||
}
|
||||
|
||||
// isPremature decides whether a given network message has a block height+delta
|
||||
|
@ -2063,7 +2063,7 @@ func (d *AuthenticatedGossiper) processZombieUpdate(
|
|||
"with chan_id=%v", msg.ShortChannelID)
|
||||
}
|
||||
|
||||
err := routing.VerifyChannelUpdateSignature(msg, pubKey)
|
||||
err := graph.VerifyChannelUpdateSignature(msg, pubKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to verify channel "+
|
||||
"update signature: %v", err)
|
||||
|
@ -2072,7 +2072,7 @@ func (d *AuthenticatedGossiper) processZombieUpdate(
|
|||
// With the signature valid, we'll proceed to mark the
|
||||
// edge as live and wait for the channel announcement to
|
||||
// come through again.
|
||||
err = d.cfg.Router.MarkEdgeLive(scid)
|
||||
err = d.cfg.Graph.MarkEdgeLive(scid)
|
||||
switch {
|
||||
case errors.Is(err, channeldb.ErrZombieEdgeNotFound):
|
||||
log.Errorf("edge with chan_id=%v was not found in the "+
|
||||
|
@ -2099,7 +2099,7 @@ func (d *AuthenticatedGossiper) processZombieUpdate(
|
|||
func (d *AuthenticatedGossiper) fetchNodeAnn(
|
||||
pubKey [33]byte) (*lnwire.NodeAnnouncement, error) {
|
||||
|
||||
node, err := d.cfg.Router.FetchLightningNode(pubKey)
|
||||
node, err := d.cfg.Graph.FetchLightningNode(pubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2112,7 +2112,7 @@ func (d *AuthenticatedGossiper) fetchNodeAnn(
|
|||
func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool {
|
||||
switch msg := msg.(type) {
|
||||
case *lnwire.AnnounceSignatures:
|
||||
chanInfo, _, _, err := d.cfg.Router.GetChannelByID(
|
||||
chanInfo, _, _, err := d.cfg.Graph.GetChannelByID(
|
||||
msg.ShortChannelID,
|
||||
)
|
||||
|
||||
|
@ -2134,7 +2134,7 @@ func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool {
|
|||
return chanInfo.AuthProof != nil
|
||||
|
||||
case *lnwire.ChannelUpdate:
|
||||
_, p1, p2, err := d.cfg.Router.GetChannelByID(msg.ShortChannelID)
|
||||
_, p1, p2, err := d.cfg.Graph.GetChannelByID(msg.ShortChannelID)
|
||||
|
||||
// If the channel cannot be found, it is most likely a leftover
|
||||
// message for a channel that was closed, so we can consider it
|
||||
|
@ -2200,14 +2200,16 @@ func (d *AuthenticatedGossiper) updateChannel(info *models.ChannelEdgeInfo,
|
|||
|
||||
// To ensure that our signature is valid, we'll verify it ourself
|
||||
// before committing it to the slice returned.
|
||||
err = routing.ValidateChannelUpdateAnn(d.selfKey, info.Capacity, chanUpdate)
|
||||
err = graph.ValidateChannelUpdateAnn(
|
||||
d.selfKey, info.Capacity, chanUpdate,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("generated invalid channel "+
|
||||
"update sig: %v", err)
|
||||
}
|
||||
|
||||
// Finally, we'll write the new edge policy to disk.
|
||||
if err := d.cfg.Router.UpdateEdge(edge); err != nil {
|
||||
if err := d.cfg.Graph.UpdateEdge(edge); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
@ -2327,7 +2329,7 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(nMsg *networkMsg,
|
|||
|
||||
// We'll quickly ask the router if it already has a newer update for
|
||||
// this node so we can skip validating signatures if not required.
|
||||
if d.cfg.Router.IsStaleNode(nodeAnn.NodeID, timestamp) {
|
||||
if d.cfg.Graph.IsStaleNode(nodeAnn.NodeID, timestamp) {
|
||||
log.Debugf("Skipped processing stale node: %x", nodeAnn.NodeID)
|
||||
nMsg.err <- nil
|
||||
return nil, true
|
||||
|
@ -2337,11 +2339,11 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(nMsg *networkMsg,
|
|||
log.Debugf("Adding node: %x got error: %v", nodeAnn.NodeID,
|
||||
err)
|
||||
|
||||
if !routing.IsError(
|
||||
if !graph.IsError(
|
||||
err,
|
||||
routing.ErrOutdated,
|
||||
routing.ErrIgnored,
|
||||
routing.ErrVBarrierShuttingDown,
|
||||
graph.ErrOutdated,
|
||||
graph.ErrIgnored,
|
||||
graph.ErrVBarrierShuttingDown,
|
||||
) {
|
||||
|
||||
log.Error(err)
|
||||
|
@ -2354,7 +2356,7 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(nMsg *networkMsg,
|
|||
// In order to ensure we don't leak unadvertised nodes, we'll make a
|
||||
// quick check to ensure this node intends to publicly advertise itself
|
||||
// to the network.
|
||||
isPublic, err := d.cfg.Router.IsPublicNode(nodeAnn.NodeID)
|
||||
isPublic, err := d.cfg.Graph.IsPublicNode(nodeAnn.NodeID)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to determine if node %x is advertised: %v",
|
||||
nodeAnn.NodeID, err)
|
||||
|
@ -2447,7 +2449,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
|
|||
|
||||
// At this point, we'll now ask the router if this is a zombie/known
|
||||
// edge. If so we can skip all the processing below.
|
||||
if d.cfg.Router.IsKnownEdge(ann.ShortChannelID) {
|
||||
if d.cfg.Graph.IsKnownEdge(ann.ShortChannelID) {
|
||||
nMsg.err <- nil
|
||||
return nil, true
|
||||
}
|
||||
|
@ -2456,7 +2458,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
|
|||
// the signatures within the proof as it should be well formed.
|
||||
var proof *models.ChannelAuthProof
|
||||
if nMsg.isRemote {
|
||||
if err := routing.ValidateChannelAnn(ann); err != nil {
|
||||
if err := graph.ValidateChannelAnn(ann); err != nil {
|
||||
err := fmt.Errorf("unable to validate announcement: "+
|
||||
"%v", err)
|
||||
|
||||
|
@ -2527,9 +2529,9 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
|
|||
// database and is now making decisions based on this DB state, before
|
||||
// it writes to the DB.
|
||||
d.channelMtx.Lock(ann.ShortChannelID.ToUint64())
|
||||
err := d.cfg.Router.AddEdge(edge, ops...)
|
||||
err := d.cfg.Graph.AddEdge(edge, ops...)
|
||||
if err != nil {
|
||||
log.Debugf("Router rejected edge for short_chan_id(%v): %v",
|
||||
log.Debugf("Graph rejected edge for short_chan_id(%v): %v",
|
||||
ann.ShortChannelID.ToUint64(), err)
|
||||
|
||||
defer d.channelMtx.Unlock(ann.ShortChannelID.ToUint64())
|
||||
|
@ -2537,7 +2539,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
|
|||
// If the edge was rejected due to already being known, then it
|
||||
// may be the case that this new message has a fresh channel
|
||||
// proof, so we'll check.
|
||||
if routing.IsError(err, routing.ErrIgnored) {
|
||||
if graph.IsError(err, graph.ErrIgnored) {
|
||||
// Attempt to process the rejected message to see if we
|
||||
// get any new announcements.
|
||||
anns, rErr := d.processRejectedEdge(ann, proof)
|
||||
|
@ -2725,7 +2727,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
|
|||
graphScid = upd.ShortChannelID
|
||||
}
|
||||
|
||||
if d.cfg.Router.IsStaleEdgePolicy(
|
||||
if d.cfg.Graph.IsStaleEdgePolicy(
|
||||
graphScid, timestamp, upd.ChannelFlags,
|
||||
) {
|
||||
|
||||
|
@ -2749,7 +2751,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
|
|||
d.channelMtx.Lock(graphScid.ToUint64())
|
||||
defer d.channelMtx.Unlock(graphScid.ToUint64())
|
||||
|
||||
chanInfo, e1, e2, err := d.cfg.Router.GetChannelByID(graphScid)
|
||||
chanInfo, e1, e2, err := d.cfg.Graph.GetChannelByID(graphScid)
|
||||
switch {
|
||||
// No error, break.
|
||||
case err == nil:
|
||||
|
@ -2861,7 +2863,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
|
|||
// Validate the channel announcement with the expected public key and
|
||||
// channel capacity. In the case of an invalid channel update, we'll
|
||||
// return an error to the caller and exit early.
|
||||
err = routing.ValidateChannelUpdateAnn(pubKey, chanInfo.Capacity, upd)
|
||||
err = graph.ValidateChannelUpdateAnn(pubKey, chanInfo.Capacity, upd)
|
||||
if err != nil {
|
||||
rErr := fmt.Errorf("unable to validate channel update "+
|
||||
"announcement for short_chan_id=%v: %v",
|
||||
|
@ -2945,11 +2947,11 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
|
|||
ExtraOpaqueData: upd.ExtraOpaqueData,
|
||||
}
|
||||
|
||||
if err := d.cfg.Router.UpdateEdge(update, ops...); err != nil {
|
||||
if routing.IsError(
|
||||
err, routing.ErrOutdated,
|
||||
routing.ErrIgnored,
|
||||
routing.ErrVBarrierShuttingDown,
|
||||
if err := d.cfg.Graph.UpdateEdge(update, ops...); err != nil {
|
||||
if graph.IsError(
|
||||
err, graph.ErrOutdated,
|
||||
graph.ErrIgnored,
|
||||
graph.ErrVBarrierShuttingDown,
|
||||
) {
|
||||
|
||||
log.Debugf("Update edge for short_chan_id(%v) got: %v",
|
||||
|
@ -3092,7 +3094,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg,
|
|||
d.channelMtx.Lock(ann.ShortChannelID.ToUint64())
|
||||
defer d.channelMtx.Unlock(ann.ShortChannelID.ToUint64())
|
||||
|
||||
chanInfo, e1, e2, err := d.cfg.Router.GetChannelByID(
|
||||
chanInfo, e1, e2, err := d.cfg.Graph.GetChannelByID(
|
||||
ann.ShortChannelID,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -3267,7 +3269,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg,
|
|||
|
||||
// With all the necessary components assembled validate the full
|
||||
// channel announcement proof.
|
||||
if err := routing.ValidateChannelAnn(chanAnn); err != nil {
|
||||
if err := graph.ValidateChannelAnn(chanAnn); err != nil {
|
||||
err := fmt.Errorf("channel announcement proof for "+
|
||||
"short_chan_id=%v isn't valid: %v", shortChanID, err)
|
||||
|
||||
|
@ -3282,7 +3284,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg,
|
|||
// attest to the bitcoin keys by validating the signatures of
|
||||
// announcement. If proof is valid then we'll populate the channel edge
|
||||
// with it, so we can announce it on peer connect.
|
||||
err = d.cfg.Router.AddProof(ann.ShortChannelID, &dbProof)
|
||||
err = d.cfg.Graph.AddProof(ann.ShortChannelID, &dbProof)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("unable add proof to the channel chanID=%v:"+
|
||||
" %v", ann.ChannelID, err)
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
"github.com/lightningnetwork/lnd/lnpeer"
|
||||
|
@ -32,7 +33,6 @@ import (
|
|||
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/netann"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/ticker"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -108,7 +108,7 @@ func newMockRouter(height uint32) *mockGraphSource {
|
|||
}
|
||||
}
|
||||
|
||||
var _ routing.ChannelGraphSource = (*mockGraphSource)(nil)
|
||||
var _ graph.ChannelGraphSource = (*mockGraphSource)(nil)
|
||||
|
||||
func (r *mockGraphSource) AddNode(node *channeldb.LightningNode,
|
||||
_ ...batch.SchedulerOption) error {
|
||||
|
@ -350,7 +350,7 @@ func (r *mockGraphSource) IsStaleEdgePolicy(chanID lnwire.ShortChannelID,
|
|||
// Since it exists within our zombie index, we'll check that it
|
||||
// respects the router's live edge horizon to determine whether
|
||||
// it is stale or not.
|
||||
return time.Since(timestamp) > routing.DefaultChannelPruneExpiry
|
||||
return time.Since(timestamp) > graph.DefaultChannelPruneExpiry
|
||||
}
|
||||
|
||||
switch {
|
||||
|
@ -783,7 +783,7 @@ func createTestCtx(t *testing.T, startHeight uint32) (*testCtx, error) {
|
|||
Timestamp: testTimestamp,
|
||||
}, nil
|
||||
},
|
||||
Router: router,
|
||||
Graph: router,
|
||||
TrickleDelay: trickleDelay,
|
||||
RetransmitTicker: ticker.NewForce(retransmitDelay),
|
||||
RebroadcastInterval: rebroadcastInterval,
|
||||
|
@ -1457,7 +1457,7 @@ func TestSignatureAnnouncementRetryAtStartup(t *testing.T) {
|
|||
NotifyWhenOffline: ctx.gossiper.reliableSender.cfg.NotifyWhenOffline,
|
||||
FetchSelfAnnouncement: ctx.gossiper.cfg.FetchSelfAnnouncement,
|
||||
UpdateSelfAnnouncement: ctx.gossiper.cfg.UpdateSelfAnnouncement,
|
||||
Router: ctx.gossiper.cfg.Router,
|
||||
Graph: ctx.gossiper.cfg.Graph,
|
||||
TrickleDelay: trickleDelay,
|
||||
RetransmitTicker: ticker.NewForce(retransmitDelay),
|
||||
RebroadcastInterval: rebroadcastInterval,
|
||||
|
@ -2257,7 +2257,7 @@ func TestProcessZombieEdgeNowLive(t *testing.T) {
|
|||
|
||||
// We'll generate a channel update with a timestamp far enough in the
|
||||
// past to consider it a zombie.
|
||||
zombieTimestamp := time.Now().Add(-routing.DefaultChannelPruneExpiry)
|
||||
zombieTimestamp := time.Now().Add(-graph.DefaultChannelPruneExpiry)
|
||||
batch.chanUpdAnn2.Timestamp = uint32(zombieTimestamp.Unix())
|
||||
if err := signUpdate(remoteKeyPriv2, batch.chanUpdAnn2); err != nil {
|
||||
t.Fatalf("unable to sign update with new timestamp: %v", err)
|
||||
|
|
|
@ -40,10 +40,6 @@
|
|||
## BOLT Spec Updates
|
||||
## Testing
|
||||
## Database
|
||||
|
||||
* [Fixed](https://github.com/lightningnetwork/lnd/pull/8854) pagination issues
|
||||
in SQL invoicedb queries.
|
||||
|
||||
## Code Health
|
||||
## Tooling and Documentation
|
||||
|
||||
|
|
|
@ -100,7 +100,16 @@
|
|||
invoice database. Invoices with incorrect expiry values will be updated to
|
||||
24-hour expiry, which is the default behavior in LND.
|
||||
|
||||
* [Fixed](https://github.com/lightningnetwork/lnd/pull/8854) pagination issues
|
||||
in SQL invoicedb queries.
|
||||
|
||||
## Code Health
|
||||
|
||||
* [Move graph building and
|
||||
maintaining](https://github.com/lightningnetwork/lnd/pull/8848) duties from
|
||||
the `routing.ChannelRouter` to the new `graph.Builder` sub-system and also
|
||||
remove the `channeldb.ChannelGraph` pointer from the `ChannelRouter`.
|
||||
|
||||
## Tooling and Documentation
|
||||
|
||||
# Contributors (Alphabetical Order)
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/discovery"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/labels"
|
||||
|
@ -33,7 +34,6 @@ import (
|
|||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"golang.org/x/crypto/salsa20"
|
||||
)
|
||||
|
||||
|
@ -629,11 +629,11 @@ const (
|
|||
// but we still haven't announced the channel to the network.
|
||||
channelReadySent
|
||||
|
||||
// addedToRouterGraph is the opening state of a channel if the
|
||||
// channel has been successfully added to the router graph
|
||||
// immediately after the channelReady message has been sent, but
|
||||
// we still haven't announced the channel to the network.
|
||||
addedToRouterGraph
|
||||
// addedToGraph is the opening state of a channel if the channel has
|
||||
// been successfully added to the graph immediately after the
|
||||
// channelReady message has been sent, but we still haven't announced
|
||||
// the channel to the network.
|
||||
addedToGraph
|
||||
)
|
||||
|
||||
func (c channelOpeningState) String() string {
|
||||
|
@ -642,8 +642,8 @@ func (c channelOpeningState) String() string {
|
|||
return "markedOpen"
|
||||
case channelReadySent:
|
||||
return "channelReadySent"
|
||||
case addedToRouterGraph:
|
||||
return "addedToRouterGraph"
|
||||
case addedToGraph:
|
||||
return "addedToGraph"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
|
@ -1039,9 +1039,9 @@ func (f *Manager) reservationCoordinator() {
|
|||
// advanceFundingState will advance the channel through the steps after the
|
||||
// funding transaction is broadcasted, up until the point where the channel is
|
||||
// ready for operation. This includes waiting for the funding transaction to
|
||||
// confirm, sending channel_ready to the peer, adding the channel to the
|
||||
// router graph, and announcing the channel. The updateChan can be set non-nil
|
||||
// to get OpenStatusUpdates.
|
||||
// confirm, sending channel_ready to the peer, adding the channel to the graph,
|
||||
// and announcing the channel. The updateChan can be set non-nil to get
|
||||
// OpenStatusUpdates.
|
||||
//
|
||||
// NOTE: This MUST be run as a goroutine.
|
||||
func (f *Manager) advanceFundingState(channel *channeldb.OpenChannel,
|
||||
|
@ -1152,7 +1152,7 @@ func (f *Manager) stateStep(channel *channeldb.OpenChannel,
|
|||
return nil
|
||||
|
||||
// channelReady was sent to peer, but the channel was not added to the
|
||||
// router graph and the channel announcement was not sent.
|
||||
// graph and the channel announcement was not sent.
|
||||
case channelReadySent:
|
||||
// We must wait until we've received the peer's channel_ready
|
||||
// before sending a channel_update according to BOLT#07.
|
||||
|
@ -1183,7 +1183,7 @@ func (f *Manager) stateStep(channel *channeldb.OpenChannel,
|
|||
|
||||
// The channel was added to the Router's topology, but the channel
|
||||
// announcement was not sent.
|
||||
case addedToRouterGraph:
|
||||
case addedToGraph:
|
||||
if channel.IsZeroConf() {
|
||||
// If this is a zero-conf channel, then we will wait
|
||||
// for it to be confirmed before announcing it to the
|
||||
|
@ -3377,15 +3377,15 @@ func (f *Manager) extractAnnounceParams(c *channeldb.OpenChannel) (
|
|||
return fwdMinHTLC, fwdMaxHTLC
|
||||
}
|
||||
|
||||
// addToRouterGraph sends a ChannelAnnouncement and a ChannelUpdate to the
|
||||
// gossiper so that the channel is added to the Router's internal graph.
|
||||
// addToGraph sends a ChannelAnnouncement and a ChannelUpdate to the
|
||||
// gossiper so that the channel is added to the graph builder's internal graph.
|
||||
// These announcement messages are NOT broadcasted to the greater network,
|
||||
// only to the channel counter party. The proofs required to announce the
|
||||
// channel to the greater network will be created and sent in annAfterSixConfs.
|
||||
// The peerAlias is used for zero-conf channels to give the counter-party a
|
||||
// ChannelUpdate they understand. ourPolicy may be set for various
|
||||
// option-scid-alias channels to re-use the same policy.
|
||||
func (f *Manager) addToRouterGraph(completeChan *channeldb.OpenChannel,
|
||||
func (f *Manager) addToGraph(completeChan *channeldb.OpenChannel,
|
||||
shortChanID *lnwire.ShortChannelID,
|
||||
peerAlias *lnwire.ShortChannelID,
|
||||
ourPolicy *models.ChannelEdgePolicy) error {
|
||||
|
@ -3415,10 +3415,10 @@ func (f *Manager) addToRouterGraph(completeChan *channeldb.OpenChannel,
|
|||
select {
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
if routing.IsError(err, routing.ErrOutdated,
|
||||
routing.ErrIgnored) {
|
||||
if graph.IsError(err, graph.ErrOutdated,
|
||||
graph.ErrIgnored) {
|
||||
|
||||
log.Debugf("Router rejected "+
|
||||
log.Debugf("Graph rejected "+
|
||||
"ChannelAnnouncement: %v", err)
|
||||
} else {
|
||||
return fmt.Errorf("error sending channel "+
|
||||
|
@ -3435,10 +3435,10 @@ func (f *Manager) addToRouterGraph(completeChan *channeldb.OpenChannel,
|
|||
select {
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
if routing.IsError(err, routing.ErrOutdated,
|
||||
routing.ErrIgnored) {
|
||||
if graph.IsError(err, graph.ErrOutdated,
|
||||
graph.ErrIgnored) {
|
||||
|
||||
log.Debugf("Router rejected "+
|
||||
log.Debugf("Graph rejected "+
|
||||
"ChannelUpdate: %v", err)
|
||||
} else {
|
||||
return fmt.Errorf("error sending channel "+
|
||||
|
@ -3454,8 +3454,8 @@ func (f *Manager) addToRouterGraph(completeChan *channeldb.OpenChannel,
|
|||
|
||||
// annAfterSixConfs broadcasts the necessary channel announcement messages to
|
||||
// the network after 6 confs. Should be called after the channelReady message
|
||||
// is sent and the channel is added to the router graph (channelState is
|
||||
// 'addedToRouterGraph') and the channel is ready to be used. This is the last
|
||||
// is sent and the channel is added to the graph (channelState is
|
||||
// 'addedToGraph') and the channel is ready to be used. This is the last
|
||||
// step in the channel opening process, and the opening state will be deleted
|
||||
// from the database if successful.
|
||||
func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
|
||||
|
@ -3566,7 +3566,7 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
|
|||
}
|
||||
|
||||
// We'll delete the edge and add it again via
|
||||
// addToRouterGraph. This is because the peer may have
|
||||
// addToGraph. This is because the peer may have
|
||||
// sent us a ChannelUpdate with an alias and we don't
|
||||
// want to relay this.
|
||||
ourPolicy, err := f.cfg.DeleteAliasEdge(baseScid)
|
||||
|
@ -3576,12 +3576,12 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
|
|||
err)
|
||||
}
|
||||
|
||||
err = f.addToRouterGraph(
|
||||
err = f.addToGraph(
|
||||
completeChan, &baseScid, nil, ourPolicy,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to re-add to "+
|
||||
"router graph: %v", err)
|
||||
"graph: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3605,9 +3605,9 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
|
|||
return nil
|
||||
}
|
||||
|
||||
// waitForZeroConfChannel is called when the state is addedToRouterGraph with
|
||||
// waitForZeroConfChannel is called when the state is addedToGraph with
|
||||
// a zero-conf channel. This will wait for the real confirmation, add the
|
||||
// confirmed SCID to the router graph, and then announce after six confs.
|
||||
// confirmed SCID to the graph, and then announce after six confs.
|
||||
func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel) error {
|
||||
// First we'll check whether the channel is confirmed on-chain. If it
|
||||
// is already confirmed, the chainntnfs subsystem will return with the
|
||||
|
@ -3662,15 +3662,15 @@ func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel) error {
|
|||
}
|
||||
|
||||
// We'll need to update the graph with the new ShortChannelID
|
||||
// via an addToRouterGraph call. We don't pass in the peer's
|
||||
// via an addToGraph call. We don't pass in the peer's
|
||||
// alias since we'll be using the confirmed SCID from now on
|
||||
// regardless if it's public or not.
|
||||
err = f.addToRouterGraph(
|
||||
err = f.addToGraph(
|
||||
c, &confChan.shortChanID, nil, ourPolicy,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed adding confirmed zero-conf "+
|
||||
"SCID to router graph: %v", err)
|
||||
"SCID to graph: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3972,7 +3972,7 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, //nolint:funlen
|
|||
// handleChannelReadyReceived is called once the remote's channelReady message
|
||||
// is received and processed. At this stage, we must have sent out our
|
||||
// channelReady message, once the remote's channelReady is processed, the
|
||||
// channel is now active, thus we change its state to `addedToRouterGraph` to
|
||||
// channel is now active, thus we change its state to `addedToGraph` to
|
||||
// let the channel start handling routing.
|
||||
func (f *Manager) handleChannelReadyReceived(channel *channeldb.OpenChannel,
|
||||
scid *lnwire.ShortChannelID, pendingChanID [32]byte,
|
||||
|
@ -4004,9 +4004,9 @@ func (f *Manager) handleChannelReadyReceived(channel *channeldb.OpenChannel,
|
|||
peerAlias = &foundAlias
|
||||
}
|
||||
|
||||
err := f.addToRouterGraph(channel, scid, peerAlias, nil)
|
||||
err := f.addToGraph(channel, scid, peerAlias, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed adding to router graph: %w", err)
|
||||
return fmt.Errorf("failed adding to graph: %w", err)
|
||||
}
|
||||
|
||||
// As the channel is now added to the ChannelRouter's topology, the
|
||||
|
@ -4014,15 +4014,15 @@ func (f *Manager) handleChannelReadyReceived(channel *channeldb.OpenChannel,
|
|||
// moved to the last state (actually deleted from the database) after
|
||||
// the channel is finally announced.
|
||||
err = f.saveChannelOpeningState(
|
||||
&channel.FundingOutpoint, addedToRouterGraph, scid,
|
||||
&channel.FundingOutpoint, addedToGraph, scid,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting channel state to"+
|
||||
" addedToRouterGraph: %w", err)
|
||||
" addedToGraph: %w", err)
|
||||
}
|
||||
|
||||
log.Debugf("Channel(%v) with ShortChanID %v: successfully "+
|
||||
"added to router graph", chanID, scid)
|
||||
"added to graph", chanID, scid)
|
||||
|
||||
// Give the caller a final update notifying them that the channel is
|
||||
fundingPoint := channel.FundingOutpoint
|
||||
|
@ -4347,17 +4347,17 @@ func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey,
|
|||
}
|
||||
|
||||
// We only send the channel proof announcement and the node announcement
|
||||
// because addToRouterGraph previously sent the ChannelAnnouncement and
|
||||
// because addToGraph previously sent the ChannelAnnouncement and
|
||||
// the ChannelUpdate announcement messages. The channel proof and node
|
||||
// announcements are broadcast to the greater network.
|
||||
errChan := f.cfg.SendAnnouncement(ann.chanProof)
|
||||
select {
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
if routing.IsError(err, routing.ErrOutdated,
|
||||
routing.ErrIgnored) {
|
||||
if graph.IsError(err, graph.ErrOutdated,
|
||||
graph.ErrIgnored) {
|
||||
|
||||
log.Debugf("Router rejected "+
|
||||
log.Debugf("Graph rejected "+
|
||||
"AnnounceSignatures: %v", err)
|
||||
} else {
|
||||
log.Errorf("Unable to send channel "+
|
||||
|
@ -4384,10 +4384,10 @@ func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey,
|
|||
select {
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
if routing.IsError(err, routing.ErrOutdated,
|
||||
routing.ErrIgnored) {
|
||||
if graph.IsError(err, graph.ErrOutdated,
|
||||
graph.ErrIgnored) {
|
||||
|
||||
log.Debugf("Router rejected "+
|
||||
log.Debugf("Graph rejected "+
|
||||
"NodeAnnouncement: %v", err)
|
||||
} else {
|
||||
log.Errorf("Unable to send node "+
|
||||
|
|
|
@ -1140,13 +1140,13 @@ func assertChannelReadySent(t *testing.T, alice, bob *testNode,
|
|||
assertDatabaseState(t, bob, fundingOutPoint, channelReadySent)
|
||||
}
|
||||
|
||||
func assertAddedToRouterGraph(t *testing.T, alice, bob *testNode,
|
||||
func assertAddedToGraph(t *testing.T, alice, bob *testNode,
|
||||
fundingOutPoint *wire.OutPoint) {
|
||||
|
||||
t.Helper()
|
||||
|
||||
assertDatabaseState(t, alice, fundingOutPoint, addedToRouterGraph)
|
||||
assertDatabaseState(t, bob, fundingOutPoint, addedToRouterGraph)
|
||||
assertDatabaseState(t, alice, fundingOutPoint, addedToGraph)
|
||||
assertDatabaseState(t, bob, fundingOutPoint, addedToGraph)
|
||||
}
|
||||
|
||||
// assertChannelAnnouncements checks that alice and bob both sends the expected
|
||||
|
@ -1523,7 +1523,7 @@ func testNormalWorkflow(t *testing.T, chanType *lnwire.ChannelType) {
|
|||
assertChannelAnnouncements(t, alice, bob, capacity, nil, nil, nil, nil)
|
||||
|
||||
// Check that the state machine is updated accordingly
|
||||
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint)
|
||||
assertAddedToGraph(t, alice, bob, fundingOutPoint)
|
||||
|
||||
// The funding transaction is now confirmed, wait for the
|
||||
// OpenStatusUpdate_ChanOpen update
|
||||
|
@ -1877,7 +1877,7 @@ func TestFundingManagerRestartBehavior(t *testing.T) {
|
|||
assertChannelAnnouncements(t, alice, bob, capacity, nil, nil, nil, nil)
|
||||
|
||||
// Check that the state machine is updated accordingly
|
||||
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint)
|
||||
assertAddedToGraph(t, alice, bob, fundingOutPoint)
|
||||
|
||||
// Next, we check that Alice sends the announcement signatures
|
||||
// on restart after six confirmations. Bob should as expected send
|
||||
|
@ -2042,7 +2042,7 @@ func TestFundingManagerOfflinePeer(t *testing.T) {
|
|||
assertChannelAnnouncements(t, alice, bob, capacity, nil, nil, nil, nil)
|
||||
|
||||
// Check that the state machine is updated accordingly
|
||||
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint)
|
||||
assertAddedToGraph(t, alice, bob, fundingOutPoint)
|
||||
|
||||
// The funding transaction is now confirmed, wait for the
|
||||
// OpenStatusUpdate_ChanOpen update
|
||||
|
@ -2501,7 +2501,7 @@ func TestFundingManagerReceiveChannelReadyTwice(t *testing.T) {
|
|||
assertChannelAnnouncements(t, alice, bob, capacity, nil, nil, nil, nil)
|
||||
|
||||
// Check that the state machine is updated accordingly
|
||||
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint)
|
||||
assertAddedToGraph(t, alice, bob, fundingOutPoint)
|
||||
|
||||
// The funding transaction is now confirmed, wait for the
|
||||
// OpenStatusUpdate_ChanOpen update
|
||||
|
@ -2594,7 +2594,7 @@ func TestFundingManagerRestartAfterChanAnn(t *testing.T) {
|
|||
assertChannelAnnouncements(t, alice, bob, capacity, nil, nil, nil, nil)
|
||||
|
||||
// Check that the state machine is updated accordingly
|
||||
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint)
|
||||
assertAddedToGraph(t, alice, bob, fundingOutPoint)
|
||||
|
||||
// The funding transaction is now confirmed, wait for the
|
||||
// OpenStatusUpdate_ChanOpen update
|
||||
|
@ -2698,7 +2698,7 @@ func TestFundingManagerRestartAfterReceivingChannelReady(t *testing.T) {
|
|||
assertChannelAnnouncements(t, alice, bob, capacity, nil, nil, nil, nil)
|
||||
|
||||
// Check that the state machine is updated accordingly
|
||||
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint)
|
||||
assertAddedToGraph(t, alice, bob, fundingOutPoint)
|
||||
|
||||
// Notify that six confirmations has been reached on funding
|
||||
// transaction.
|
||||
|
@ -2912,9 +2912,9 @@ func TestFundingManagerPrivateRestart(t *testing.T) {
|
|||
// announcements.
|
||||
assertChannelAnnouncements(t, alice, bob, capacity, nil, nil, nil, nil)
|
||||
|
||||
// Note: We don't check for the addedToRouterGraph state because in
|
||||
// Note: We don't check for the addedToGraph state because in
|
||||
// the private channel mode, the state is quickly changed from
|
||||
// addedToRouterGraph to deleted from the database since the public
|
||||
// addedToGraph to deleted from the database since the public
|
||||
// announcement phase is skipped.
|
||||
|
||||
// The funding transaction is now confirmed, wait for the
|
||||
|
@ -4563,8 +4563,8 @@ func testZeroConf(t *testing.T, chanType *lnwire.ChannelType) {
|
|||
// We'll now wait for the OpenStatusUpdate_ChanOpen update.
|
||||
waitForOpenUpdate(t, updateChan)
|
||||
|
||||
// Assert that both Alice & Bob are in the addedToRouterGraph state.
|
||||
assertAddedToRouterGraph(t, alice, bob, fundingOp)
|
||||
// Assert that both Alice & Bob are in the addedToGraph state.
|
||||
assertAddedToGraph(t, alice, bob, fundingOp)
|
||||
|
||||
// We'll now restart Alice's funding manager and assert that the tx
|
||||
// is rebroadcast.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package routing
|
||||
package graph
|
||||
|
||||
import (
|
||||
"bytes"
|
1837
graph/builder.go
Normal file
1837
graph/builder.go
Normal file
File diff suppressed because it is too large
Load diff
2073
graph/builder_test.go
Normal file
2073
graph/builder_test.go
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
package routing
|
||||
package graph
|
||||
|
||||
import "github.com/go-errors/errors"
|
||||
|
||||
|
@ -39,27 +39,27 @@ const (
|
|||
ErrParentValidationFailed
|
||||
)
|
||||
|
||||
// routerError is a structure that represent the error inside the routing package,
|
||||
// graphError is a structure that represent the error inside the graph package,
|
||||
// this structure carries additional information about error code in order to
|
||||
// be able distinguish errors outside of the current package.
|
||||
type routerError struct {
|
||||
type graphError struct {
|
||||
err *errors.Error
|
||||
code errorCode
|
||||
}
|
||||
|
||||
// Error represents errors as the string
|
||||
// NOTE: Part of the error interface.
|
||||
func (e *routerError) Error() string {
|
||||
func (e *graphError) Error() string {
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
// A compile time check to ensure routerError implements the error interface.
|
||||
var _ error = (*routerError)(nil)
|
||||
// A compile time check to ensure graphError implements the error interface.
|
||||
var _ error = (*graphError)(nil)
|
||||
|
||||
// newErrf creates a routerError by the given error formatted description and
|
||||
// newErrf creates a graphError by the given error formatted description and
|
||||
// its corresponding error code.
|
||||
func newErrf(code errorCode, format string, a ...interface{}) *routerError {
|
||||
return &routerError{
|
||||
func newErrf(code errorCode, format string, a ...interface{}) *graphError {
|
||||
return &graphError{
|
||||
code: code,
|
||||
err: errors.Errorf(format, a...),
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ func newErrf(code errorCode, format string, a ...interface{}) *routerError {
|
|||
// IsError is a helper function which is needed to have ability to check that
|
||||
// returned error has specific error code.
|
||||
func IsError(e interface{}, codes ...errorCode) bool {
|
||||
err, ok := e.(*routerError)
|
||||
err, ok := e.(*graphError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
280
graph/interfaces.go
Normal file
280
graph/interfaces.go
Normal file
|
@ -0,0 +1,280 @@
|
|||
package graph
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/batch"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
// ChannelGraphSource represents the source of information about the topology
|
||||
// of the lightning network. It's responsible for the addition of nodes, edges,
|
||||
// applying edge updates, and returning the current block height with which the
|
||||
// topology is synchronized.
|
||||
//
|
||||
//nolint:interfacebloat
|
||||
type ChannelGraphSource interface {
|
||||
// AddNode is used to add information about a node to the router
|
||||
// database. If the node with this pubkey is not present in an existing
|
||||
// channel, it will be ignored.
|
||||
AddNode(node *channeldb.LightningNode,
|
||||
op ...batch.SchedulerOption) error
|
||||
|
||||
// AddEdge is used to add edge/channel to the topology of the router,
|
||||
// after all information about channel will be gathered this
|
||||
// edge/channel might be used in construction of payment path.
|
||||
AddEdge(edge *models.ChannelEdgeInfo,
|
||||
op ...batch.SchedulerOption) error
|
||||
|
||||
// AddProof updates the channel edge info with proof which is needed to
|
||||
// properly announce the edge to the rest of the network.
|
||||
AddProof(chanID lnwire.ShortChannelID,
|
||||
proof *models.ChannelAuthProof) error
|
||||
|
||||
// UpdateEdge is used to update edge information, without this message
|
||||
// edge considered as not fully constructed.
|
||||
UpdateEdge(policy *models.ChannelEdgePolicy,
|
||||
op ...batch.SchedulerOption) error
|
||||
|
||||
// IsStaleNode returns true if the graph source has a node announcement
|
||||
// for the target node with a more recent timestamp. This method will
|
||||
// also return true if we don't have an active channel announcement for
|
||||
// the target node.
|
||||
IsStaleNode(node route.Vertex, timestamp time.Time) bool
|
||||
|
||||
// IsPublicNode determines whether the given vertex is seen as a public
|
||||
// node in the graph from the graph's source node's point of view.
|
||||
IsPublicNode(node route.Vertex) (bool, error)
|
||||
|
||||
// IsKnownEdge returns true if the graph source already knows of the
|
||||
// passed channel ID either as a live or zombie edge.
|
||||
IsKnownEdge(chanID lnwire.ShortChannelID) bool
|
||||
|
||||
// IsStaleEdgePolicy returns true if the graph source has a channel
|
||||
// edge for the passed channel ID (and flags) that have a more recent
|
||||
// timestamp.
|
||||
IsStaleEdgePolicy(chanID lnwire.ShortChannelID, timestamp time.Time,
|
||||
flags lnwire.ChanUpdateChanFlags) bool
|
||||
|
||||
// MarkEdgeLive clears an edge from our zombie index, deeming it as
|
||||
// live.
|
||||
MarkEdgeLive(chanID lnwire.ShortChannelID) error
|
||||
|
||||
// ForAllOutgoingChannels is used to iterate over all channels
|
||||
// emanating from the "source" node which is the center of the
|
||||
// star-graph.
|
||||
ForAllOutgoingChannels(cb func(tx kvdb.RTx,
|
||||
c *models.ChannelEdgeInfo,
|
||||
e *models.ChannelEdgePolicy) error) error
|
||||
|
||||
// CurrentBlockHeight returns the block height from POV of the router
|
||||
// subsystem.
|
||||
CurrentBlockHeight() (uint32, error)
|
||||
|
||||
// GetChannelByID return the channel by the channel id.
|
||||
GetChannelByID(chanID lnwire.ShortChannelID) (
|
||||
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy, error)
|
||||
|
||||
// FetchLightningNode attempts to look up a target node by its identity
|
||||
// public key. channeldb.ErrGraphNodeNotFound is returned if the node
|
||||
// doesn't exist within the graph.
|
||||
FetchLightningNode(route.Vertex) (*channeldb.LightningNode, error)
|
||||
|
||||
// ForEachNode is used to iterate over every node in the known graph.
|
||||
ForEachNode(func(node *channeldb.LightningNode) error) error
|
||||
}
|
||||
|
||||
// DB is an interface describing a persisted Lightning Network graph.
|
||||
//
|
||||
//nolint:interfacebloat
|
||||
type DB interface {
|
||||
// PruneTip returns the block height and hash of the latest block that
|
||||
// has been used to prune channels in the graph. Knowing the "prune tip"
|
||||
// allows callers to tell if the graph is currently in sync with the
|
||||
// current best known UTXO state.
|
||||
PruneTip() (*chainhash.Hash, uint32, error)
|
||||
|
||||
// PruneGraph prunes newly closed channels from the channel graph in
|
||||
// response to a new block being solved on the network. Any transactions
|
||||
// which spend the funding output of any known channels within the graph
|
||||
// will be deleted. Additionally, the "prune tip", or the last block
|
||||
// which has been used to prune the graph is stored so callers can
|
||||
// ensure the graph is fully in sync with the current UTXO state. A
|
||||
// slice of channels that have been closed by the target block are
|
||||
// returned if the function succeeds without error.
|
||||
PruneGraph(spentOutputs []*wire.OutPoint, blockHash *chainhash.Hash,
|
||||
blockHeight uint32) ([]*models.ChannelEdgeInfo, error)
|
||||
|
||||
// ChannelView returns the verifiable edge information for each active
|
||||
// channel within the known channel graph. The set of UTXO's (along with
|
||||
// their scripts) returned are the ones that need to be watched on
|
||||
// chain to detect channel closes on the resident blockchain.
|
||||
ChannelView() ([]channeldb.EdgePoint, error)
|
||||
|
||||
// PruneGraphNodes is a garbage collection method which attempts to
|
||||
// prune out any nodes from the channel graph that are currently
|
||||
// unconnected. This ensure that we only maintain a graph of reachable
|
||||
// nodes. In the event that a pruned node gains more channels, it will
|
||||
// be re-added back to the graph.
|
||||
PruneGraphNodes() error
|
||||
|
||||
// SourceNode returns the source node of the graph. The source node is
|
||||
// treated as the center node within a star-graph. This method may be
|
||||
// used to kick off a path finding algorithm in order to explore the
|
||||
// reachability of another node based off the source node.
|
||||
SourceNode() (*channeldb.LightningNode, error)
|
||||
|
||||
// DisabledChannelIDs returns the channel ids of disabled channels.
|
||||
// A channel is disabled when two of the associated ChanelEdgePolicies
|
||||
// have their disabled bit on.
|
||||
DisabledChannelIDs() ([]uint64, error)
|
||||
|
||||
// FetchChanInfos returns the set of channel edges that correspond to
|
||||
// the passed channel ID's. If an edge is the query is unknown to the
|
||||
// database, it will skipped and the result will contain only those
|
||||
// edges that exist at the time of the query. This can be used to
|
||||
// respond to peer queries that are seeking to fill in gaps in their
|
||||
// view of the channel graph.
|
||||
FetchChanInfos(chanIDs []uint64) ([]channeldb.ChannelEdge, error)
|
||||
|
||||
// ChanUpdatesInHorizon returns all the known channel edges which have
|
||||
// at least one edge that has an update timestamp within the specified
|
||||
// horizon.
|
||||
ChanUpdatesInHorizon(startTime, endTime time.Time) (
|
||||
[]channeldb.ChannelEdge, error)
|
||||
|
||||
// DeleteChannelEdges removes edges with the given channel IDs from the
|
||||
// database and marks them as zombies. This ensures that we're unable to
|
||||
// re-add it to our database once again. If an edge does not exist
|
||||
// within the database, then ErrEdgeNotFound will be returned. If
|
||||
// strictZombiePruning is true, then when we mark these edges as
|
||||
// zombies, we'll set up the keys such that we require the node that
|
||||
// failed to send the fresh update to be the one that resurrects the
|
||||
// channel from its zombie state. The markZombie bool denotes whether
|
||||
// to mark the channel as a zombie.
|
||||
DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
||||
chanIDs ...uint64) error
|
||||
|
||||
// DisconnectBlockAtHeight is used to indicate that the block specified
|
||||
// by the passed height has been disconnected from the main chain. This
|
||||
// will "rewind" the graph back to the height below, deleting channels
|
||||
// that are no longer confirmed from the graph. The prune log will be
|
||||
// set to the last prune height valid for the remaining chain.
|
||||
// Channels that were removed from the graph resulting from the
|
||||
// disconnected block are returned.
|
||||
DisconnectBlockAtHeight(height uint32) ([]*models.ChannelEdgeInfo,
|
||||
error)
|
||||
|
||||
// HasChannelEdge returns true if the database knows of a channel edge
|
||||
// with the passed channel ID, and false otherwise. If an edge with that
|
||||
// ID is found within the graph, then two time stamps representing the
|
||||
// last time the edge was updated for both directed edges are returned
|
||||
// along with the boolean. If it is not found, then the zombie index is
|
||||
// checked and its result is returned as the second boolean.
|
||||
HasChannelEdge(chanID uint64) (time.Time, time.Time, bool, bool, error)
|
||||
|
||||
// FetchChannelEdgesByID attempts to lookup the two directed edges for
|
||||
// the channel identified by the channel ID. If the channel can't be
|
||||
// found, then ErrEdgeNotFound is returned. A struct which houses the
|
||||
// general information for the channel itself is returned as well as
|
||||
// two structs that contain the routing policies for the channel in
|
||||
// either direction.
|
||||
//
|
||||
// ErrZombieEdge an be returned if the edge is currently marked as a
|
||||
// zombie within the database. In this case, the ChannelEdgePolicy's
|
||||
// will be nil, and the ChannelEdgeInfo will only include the public
|
||||
// keys of each node.
|
||||
FetchChannelEdgesByID(chanID uint64) (*models.ChannelEdgeInfo,
|
||||
*models.ChannelEdgePolicy, *models.ChannelEdgePolicy, error)
|
||||
|
||||
// AddLightningNode adds a vertex/node to the graph database. If the
|
||||
// node is not in the database from before, this will add a new,
|
||||
// unconnected one to the graph. If it is present from before, this will
|
||||
// update that node's information. Note that this method is expected to
|
||||
// only be called to update an already present node from a node
|
||||
// announcement, or to insert a node found in a channel update.
|
||||
AddLightningNode(node *channeldb.LightningNode,
|
||||
op ...batch.SchedulerOption) error
|
||||
|
||||
// AddChannelEdge adds a new (undirected, blank) edge to the graph
|
||||
// database. An undirected edge from the two target nodes are created.
|
||||
// The information stored denotes the static attributes of the channel,
|
||||
// such as the channelID, the keys involved in creation of the channel,
|
||||
// and the set of features that the channel supports. The chanPoint and
|
||||
// chanID are used to uniquely identify the edge globally within the
|
||||
// database.
|
||||
AddChannelEdge(edge *models.ChannelEdgeInfo,
|
||||
op ...batch.SchedulerOption) error
|
||||
|
||||
// MarkEdgeZombie attempts to mark a channel identified by its channel
|
||||
// ID as a zombie. This method is used on an ad-hoc basis, when channels
|
||||
// need to be marked as zombies outside the normal pruning cycle.
|
||||
MarkEdgeZombie(chanID uint64, pubKey1, pubKey2 [33]byte) error
|
||||
|
||||
// UpdateEdgePolicy updates the edge routing policy for a single
|
||||
// directed edge within the database for the referenced channel. The
|
||||
// `flags` attribute within the ChannelEdgePolicy determines which of
|
||||
// the directed edges are being updated. If the flag is 1, then the
|
||||
// first node's information is being updated, otherwise it's the second
|
||||
// node's information. The node ordering is determined by the
|
||||
// lexicographical ordering of the identity public keys of the nodes on
|
||||
// either side of the channel.
|
||||
UpdateEdgePolicy(edge *models.ChannelEdgePolicy,
|
||||
op ...batch.SchedulerOption) error
|
||||
|
||||
// HasLightningNode determines if the graph has a vertex identified by
|
||||
// the target node identity public key. If the node exists in the
|
||||
// database, a timestamp of when the data for the node was lasted
|
||||
// updated is returned along with a true boolean. Otherwise, an empty
|
||||
// time.Time is returned with a false boolean.
|
||||
HasLightningNode(nodePub [33]byte) (time.Time, bool, error)
|
||||
|
||||
// FetchLightningNode attempts to look up a target node by its identity
|
||||
// public key. If the node isn't found in the database, then
|
||||
// ErrGraphNodeNotFound is returned.
|
||||
FetchLightningNode(nodePub route.Vertex) (*channeldb.LightningNode,
|
||||
error)
|
||||
|
||||
// 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.
|
||||
ForEachNode(cb func(kvdb.RTx, *channeldb.LightningNode) error) error
|
||||
|
||||
// ForEachNodeChannel iterates through all channels of the given node,
|
||||
// executing the passed callback with an edge info structure and the
|
||||
// policies of each end of the channel. The first edge policy is the
|
||||
// outgoing edge *to* the connecting node, while the second is the
|
||||
// incoming edge *from* the connecting node. If the callback returns an
|
||||
// error, then the iteration is halted with the error propagated back up
|
||||
// to the caller.
|
||||
//
|
||||
// Unknown policies are passed into the callback as nil values.
|
||||
ForEachNodeChannel(nodePub route.Vertex, cb func(kvdb.RTx,
|
||||
*models.ChannelEdgeInfo,
|
||||
*models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy) error) error
|
||||
|
||||
// UpdateChannelEdge retrieves and update edge of the graph database.
|
||||
// Method only reserved for updating an edge info after its already been
|
||||
// created. In order to maintain this constraints, we return an error in
|
||||
// the scenario that an edge info hasn't yet been created yet, but
|
||||
// someone attempts to update it.
|
||||
UpdateChannelEdge(edge *models.ChannelEdgeInfo) error
|
||||
|
||||
// IsPublicNode is a helper method that determines whether the node with
|
||||
// the given public key is seen as a public node in the graph from the
|
||||
// graph's source node's point of view.
|
||||
IsPublicNode(pubKey [33]byte) (bool, error)
|
||||
|
||||
// MarkEdgeLive clears an edge from our zombie index, deeming it as
|
||||
// live.
|
||||
MarkEdgeLive(chanID uint64) error
|
||||
}
|
47
graph/log.go
Normal file
47
graph/log.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package graph
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btclog"
|
||||
"github.com/lightningnetwork/lnd/build"
|
||||
)
|
||||
|
||||
// log is a logger that is initialized with no output filters. This means the
|
||||
// package will not perform any logging by default until the caller requests
|
||||
// it.
|
||||
var log btclog.Logger
|
||||
|
||||
const Subsystem = "GRPH"
|
||||
|
||||
// The default amount of logging is none.
|
||||
func init() {
|
||||
UseLogger(build.NewSubLogger(Subsystem, nil))
|
||||
}
|
||||
|
||||
// DisableLog disables all library log output. Logging output is disabled by
|
||||
// default until UseLogger is called.
|
||||
func DisableLog() {
|
||||
UseLogger(btclog.Disabled)
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info. This
|
||||
// should be used in preference to SetLogWriter if the caller is also using
|
||||
// btclog.
|
||||
func UseLogger(logger btclog.Logger) {
|
||||
log = logger
|
||||
}
|
||||
|
||||
// logClosure is used to provide a closure over expensive logging operations so
|
||||
// don't have to be performed when the logging level doesn't warrant it.
|
||||
type logClosure func() string
|
||||
|
||||
// String invokes the underlying function and returns the result.
|
||||
func (c logClosure) String() string {
|
||||
return c()
|
||||
}
|
||||
|
||||
// newLogClosure returns a new closure over a function that returns a string
|
||||
// which itself provides a Stringer interface so that it can be used with the
|
||||
// logging system.
|
||||
func newLogClosure(c func() string) logClosure {
|
||||
return logClosure(c)
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
package routing
|
||||
package graph
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
|
@ -56,16 +55,16 @@ type topologyClientUpdate struct {
|
|||
// topology occurs. Changes that will be sent at notifications include: new
|
||||
// nodes appearing, node updating their attributes, new channels, channels
|
||||
// closing, and updates in the routing policies of a channel's directed edges.
|
||||
func (r *ChannelRouter) SubscribeTopology() (*TopologyClient, error) {
|
||||
func (b *Builder) SubscribeTopology() (*TopologyClient, error) {
|
||||
// If the router is not yet started, return an error to avoid a
|
||||
// deadlock waiting for it to handle the subscription request.
|
||||
if atomic.LoadUint32(&r.started) == 0 {
|
||||
if !b.started.Load() {
|
||||
return nil, fmt.Errorf("router not started")
|
||||
}
|
||||
|
||||
// We'll first atomically obtain the next ID for this client from the
|
||||
// incrementing client ID counter.
|
||||
clientID := atomic.AddUint64(&r.ntfnClientCounter, 1)
|
||||
clientID := b.ntfnClientCounter.Add(1)
|
||||
|
||||
log.Debugf("New graph topology client subscription, client %v",
|
||||
clientID)
|
||||
|
@ -73,12 +72,12 @@ func (r *ChannelRouter) SubscribeTopology() (*TopologyClient, error) {
|
|||
ntfnChan := make(chan *TopologyChange, 10)
|
||||
|
||||
select {
|
||||
case r.ntfnClientUpdates <- &topologyClientUpdate{
|
||||
case b.ntfnClientUpdates <- &topologyClientUpdate{
|
||||
cancel: false,
|
||||
clientID: clientID,
|
||||
ntfnChan: ntfnChan,
|
||||
}:
|
||||
case <-r.quit:
|
||||
case <-b.quit:
|
||||
return nil, errors.New("ChannelRouter shutting down")
|
||||
}
|
||||
|
||||
|
@ -86,11 +85,11 @@ func (r *ChannelRouter) SubscribeTopology() (*TopologyClient, error) {
|
|||
TopologyChanges: ntfnChan,
|
||||
Cancel: func() {
|
||||
select {
|
||||
case r.ntfnClientUpdates <- &topologyClientUpdate{
|
||||
case b.ntfnClientUpdates <- &topologyClientUpdate{
|
||||
cancel: true,
|
||||
clientID: clientID,
|
||||
}:
|
||||
case <-r.quit:
|
||||
case <-b.quit:
|
||||
return
|
||||
}
|
||||
},
|
||||
|
@ -116,8 +115,7 @@ type topologyClient struct {
|
|||
|
||||
// notifyTopologyChange notifies all registered clients of a new change in
|
||||
// graph topology in a non-blocking.
|
||||
func (r *ChannelRouter) notifyTopologyChange(topologyDiff *TopologyChange) {
|
||||
|
||||
func (b *Builder) notifyTopologyChange(topologyDiff *TopologyChange) {
|
||||
// notifyClient is a helper closure that will send topology updates to
|
||||
// the given client.
|
||||
notifyClient := func(clientID uint64, client *topologyClient) bool {
|
||||
|
@ -145,7 +143,7 @@ func (r *ChannelRouter) notifyTopologyChange(topologyDiff *TopologyChange) {
|
|||
|
||||
// Similarly, if the ChannelRouter itself exists early,
|
||||
// then we'll also exit ourselves.
|
||||
case <-r.quit:
|
||||
case <-b.quit:
|
||||
|
||||
}
|
||||
}(client)
|
||||
|
@ -157,7 +155,7 @@ func (r *ChannelRouter) notifyTopologyChange(topologyDiff *TopologyChange) {
|
|||
|
||||
// Range over the set of active clients, and attempt to send the
|
||||
// topology updates.
|
||||
r.topologyClients.Range(notifyClient)
|
||||
b.topologyClients.Range(notifyClient)
|
||||
}
|
||||
|
||||
// TopologyChange represents a new set of modifications to the channel graph.
|
||||
|
@ -313,7 +311,7 @@ type ChannelEdgeUpdate struct {
|
|||
// constitutes. This function will also fetch any required auxiliary
|
||||
// information required to create the topology change update from the graph
|
||||
// database.
|
||||
func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange,
|
||||
func addToTopologyChange(graph DB, update *TopologyChange,
|
||||
msg interface{}) error {
|
||||
|
||||
switch m := msg.(type) {
|
|
@ -1,7 +1,8 @@
|
|||
package routing
|
||||
package graph
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"image/color"
|
||||
prand "math/rand"
|
||||
|
@ -11,13 +12,17 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
lnmock "github.com/lightningnetwork/lnd/lntest/mock"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
|
@ -49,15 +54,34 @@ var (
|
|||
bitcoinKey2 = priv2.PubKey()
|
||||
|
||||
timeout = time.Second * 5
|
||||
|
||||
testRBytes, _ = hex.DecodeString(
|
||||
"8ce2bc69281ce27da07e6683571319d18e949ddfa2965fb6caa1bf03" +
|
||||
"14f882d7",
|
||||
)
|
||||
testSBytes, _ = hex.DecodeString(
|
||||
"299105481d63e0f4bc2a88121167221b6700d72a0ead154c03be696a2" +
|
||||
"92d24ae",
|
||||
)
|
||||
testRScalar = new(btcec.ModNScalar)
|
||||
testSScalar = new(btcec.ModNScalar)
|
||||
_ = testRScalar.SetByteSlice(testRBytes)
|
||||
_ = testSScalar.SetByteSlice(testSBytes)
|
||||
testSig = ecdsa.NewSignature(testRScalar, testSScalar)
|
||||
|
||||
testAuthProof = models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
}
|
||||
)
|
||||
|
||||
func createTestNode() (*channeldb.LightningNode, error) {
|
||||
func createTestNode(t *testing.T) *channeldb.LightningNode {
|
||||
updateTime := prand.Int63()
|
||||
|
||||
priv, err := btcec.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("unable create private key: %v", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
pub := priv.PubKey().SerializeCompressed()
|
||||
n := &channeldb.LightningNode{
|
||||
|
@ -71,7 +95,7 @@ func createTestNode() (*channeldb.LightningNode, error) {
|
|||
}
|
||||
copy(n.PubKeyBytes[:], pub)
|
||||
|
||||
return n, nil
|
||||
return n
|
||||
}
|
||||
|
||||
func randEdgePolicy(chanID *lnwire.ShortChannelID,
|
||||
|
@ -271,7 +295,7 @@ type mockChainView struct {
|
|||
}
|
||||
|
||||
// A compile time check to ensure mockChainView implements the
|
||||
// chainview.FilteredChainView.
|
||||
// chainview.FilteredChainViewReader.
|
||||
var _ chainview.FilteredChainView = (*mockChainView)(nil)
|
||||
|
||||
func newMockChainView(chain lnwallet.BlockChainIO) *mockChainView {
|
||||
|
@ -302,6 +326,15 @@ func (m *mockChainView) UpdateFilter(ops []channeldb.EdgePoint, updateHeight uin
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *mockChainView) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockChainView) Stop() error {
|
||||
close(m.quit)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockChainView) notifyBlock(hash chainhash.Hash, height uint32,
|
||||
txns []*wire.MsgTx, t *testing.T) {
|
||||
|
||||
|
@ -405,15 +438,6 @@ func (m *mockChainView) FilterBlock(blockHash *chainhash.Hash) (*chainview.Filte
|
|||
return filteredBlock, nil
|
||||
}
|
||||
|
||||
func (m *mockChainView) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockChainView) Stop() error {
|
||||
close(m.quit)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestEdgeUpdateNotification tests that when edges are updated or added,
|
||||
// a proper notification is sent of to all registered clients.
|
||||
func TestEdgeUpdateNotification(t *testing.T) {
|
||||
|
@ -437,10 +461,8 @@ func TestEdgeUpdateNotification(t *testing.T) {
|
|||
|
||||
// Next we'll create two test nodes that the fake channel will be open
|
||||
// between.
|
||||
node1, err := createTestNode()
|
||||
require.NoError(t, err, "unable to create test node")
|
||||
node2, err := createTestNode()
|
||||
require.NoError(t, err, "unable to create test node")
|
||||
node1 := createTestNode(t)
|
||||
node2 := createTestNode(t)
|
||||
|
||||
// Finally, to conclude our test set up, we'll create a channel
|
||||
// update to announce the created channel between the two nodes.
|
||||
|
@ -458,13 +480,13 @@ func TestEdgeUpdateNotification(t *testing.T) {
|
|||
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
|
||||
if err := ctx.router.AddEdge(edge); err != nil {
|
||||
if err := ctx.builder.AddEdge(edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
|
||||
// With the channel edge now in place, we'll subscribe for topology
|
||||
// notifications.
|
||||
ntfnClient, err := ctx.router.SubscribeTopology()
|
||||
ntfnClient, err := ctx.builder.SubscribeTopology()
|
||||
require.NoError(t, err, "unable to subscribe for channel notifications")
|
||||
|
||||
// Create random policy edges that are stemmed to the channel id
|
||||
|
@ -477,10 +499,10 @@ func TestEdgeUpdateNotification(t *testing.T) {
|
|||
require.NoError(t, err, "unable to create a random chan policy")
|
||||
edge2.ChannelFlags = 1
|
||||
|
||||
if err := ctx.router.UpdateEdge(edge1); err != nil {
|
||||
if err := ctx.builder.UpdateEdge(edge1); err != nil {
|
||||
t.Fatalf("unable to add edge update: %v", err)
|
||||
}
|
||||
if err := ctx.router.UpdateEdge(edge2); err != nil {
|
||||
if err := ctx.builder.UpdateEdge(edge2); err != nil {
|
||||
t.Fatalf("unable to add edge update: %v", err)
|
||||
}
|
||||
|
||||
|
@ -625,10 +647,8 @@ func TestNodeUpdateNotification(t *testing.T) {
|
|||
// 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()
|
||||
require.NoError(t, err, "unable to create test node")
|
||||
node2, err := createTestNode()
|
||||
require.NoError(t, err, "unable to create test node")
|
||||
node1 := createTestNode(t)
|
||||
node2 := createTestNode(t)
|
||||
|
||||
testFeaturesBuf := new(bytes.Buffer)
|
||||
require.NoError(t, testFeatures.Encode(testFeaturesBuf))
|
||||
|
@ -649,20 +669,20 @@ func TestNodeUpdateNotification(t *testing.T) {
|
|||
|
||||
// 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 {
|
||||
if err := ctx.builder.AddEdge(edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
|
||||
// Create a new client to receive notifications.
|
||||
ntfnClient, err := ctx.router.SubscribeTopology()
|
||||
ntfnClient, err := ctx.builder.SubscribeTopology()
|
||||
require.NoError(t, err, "unable to subscribe for channel notifications")
|
||||
|
||||
// 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.builder.AddNode(node1); err != nil {
|
||||
t.Fatalf("unable to add node: %v", err)
|
||||
}
|
||||
if err := ctx.router.AddNode(node2); err != nil {
|
||||
if err := ctx.builder.AddNode(node2); err != nil {
|
||||
t.Fatalf("unable to add node: %v", err)
|
||||
}
|
||||
|
||||
|
@ -756,7 +776,7 @@ func TestNodeUpdateNotification(t *testing.T) {
|
|||
nodeUpdateAnn.LastUpdate = node1.LastUpdate.Add(300 * time.Millisecond)
|
||||
|
||||
// Add new node topology update to the channel router.
|
||||
if err := ctx.router.AddNode(&nodeUpdateAnn); err != nil {
|
||||
if err := ctx.builder.AddNode(&nodeUpdateAnn); err != nil {
|
||||
t.Fatalf("unable to add node: %v", err)
|
||||
}
|
||||
|
||||
|
@ -788,7 +808,7 @@ func TestNotificationCancellation(t *testing.T) {
|
|||
ctx := createTestCtxSingleNode(t, startingBlockHeight)
|
||||
|
||||
// Create a new client to receive notifications.
|
||||
ntfnClient, err := ctx.router.SubscribeTopology()
|
||||
ntfnClient, err := ctx.builder.SubscribeTopology()
|
||||
require.NoError(t, err, "unable to subscribe for channel notifications")
|
||||
|
||||
// We'll create the utxo for a new channel.
|
||||
|
@ -808,10 +828,8 @@ func TestNotificationCancellation(t *testing.T) {
|
|||
|
||||
// We'll create a fresh new node topology update to feed to the channel
|
||||
// router.
|
||||
node1, err := createTestNode()
|
||||
require.NoError(t, err, "unable to create test node")
|
||||
node2, err := createTestNode()
|
||||
require.NoError(t, err, "unable to create test node")
|
||||
node1 := createTestNode(t)
|
||||
node2 := createTestNode(t)
|
||||
|
||||
// Before we send the message to the channel router, we'll cancel the
|
||||
// notifications for this client. As a result, the notification
|
||||
|
@ -832,15 +850,15 @@ func TestNotificationCancellation(t *testing.T) {
|
|||
}
|
||||
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
if err := ctx.router.AddEdge(edge); err != nil {
|
||||
if err := ctx.builder.AddEdge(edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
|
||||
if err := ctx.router.AddNode(node1); err != nil {
|
||||
if err := ctx.builder.AddNode(node1); err != nil {
|
||||
t.Fatalf("unable to add node: %v", err)
|
||||
}
|
||||
|
||||
if err := ctx.router.AddNode(node2); err != nil {
|
||||
if err := ctx.builder.AddNode(node2); err != nil {
|
||||
t.Fatalf("unable to add node: %v", err)
|
||||
}
|
||||
|
||||
|
@ -883,10 +901,8 @@ func TestChannelCloseNotification(t *testing.T) {
|
|||
|
||||
// Next we'll create two test nodes that the fake channel will be open
|
||||
// between.
|
||||
node1, err := createTestNode()
|
||||
require.NoError(t, err, "unable to create test node")
|
||||
node2, err := createTestNode()
|
||||
require.NoError(t, err, "unable to create test node")
|
||||
node1 := createTestNode(t)
|
||||
node2 := createTestNode(t)
|
||||
|
||||
// Finally, to conclude our test set up, we'll create a channel
|
||||
// announcement to announce the created channel between the two nodes.
|
||||
|
@ -903,13 +919,13 @@ func TestChannelCloseNotification(t *testing.T) {
|
|||
}
|
||||
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
if err := ctx.router.AddEdge(edge); err != nil {
|
||||
if err := ctx.builder.AddEdge(edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
|
||||
// With the channel edge now in place, we'll subscribe for topology
|
||||
// notifications.
|
||||
ntfnClient, err := ctx.router.SubscribeTopology()
|
||||
ntfnClient, err := ctx.builder.SubscribeTopology()
|
||||
require.NoError(t, err, "unable to subscribe for channel notifications")
|
||||
|
||||
// Next, we'll simulate the closure of our channel by generating a new
|
||||
|
@ -999,3 +1015,184 @@ func TestEncodeHexColor(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testCtx struct {
|
||||
builder *Builder
|
||||
|
||||
graph *channeldb.ChannelGraph
|
||||
|
||||
aliases map[string]route.Vertex
|
||||
|
||||
privKeys map[string]*btcec.PrivateKey
|
||||
|
||||
channelIDs map[route.Vertex]map[route.Vertex]uint64
|
||||
|
||||
chain *mockChain
|
||||
chainView *mockChainView
|
||||
|
||||
notifier *lnmock.ChainNotifier
|
||||
}
|
||||
|
||||
func createTestCtxSingleNode(t *testing.T,
|
||||
startingHeight uint32) *testCtx {
|
||||
|
||||
graph, graphBackend, err := makeTestGraph(t, true)
|
||||
require.NoError(t, err, "failed to make test graph")
|
||||
|
||||
sourceNode := createTestNode(t)
|
||||
|
||||
require.NoError(t,
|
||||
graph.SetSourceNode(sourceNode), "failed to set source node",
|
||||
)
|
||||
|
||||
graphInstance := &testGraphInstance{
|
||||
graph: graph,
|
||||
graphBackend: graphBackend,
|
||||
}
|
||||
|
||||
return createTestCtxFromGraphInstance(
|
||||
t, startingHeight, graphInstance, false,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *testCtx) RestartBuilder(t *testing.T) {
|
||||
c.chainView.Reset()
|
||||
|
||||
selfNode, err := c.graph.SourceNode()
|
||||
require.NoError(t, err)
|
||||
|
||||
// With the chainView reset, we'll now re-create the builder itself, and
|
||||
// start it.
|
||||
builder, err := NewBuilder(&Config{
|
||||
SelfNode: selfNode.PubKeyBytes,
|
||||
Graph: c.graph,
|
||||
Chain: c.chain,
|
||||
ChainView: c.chainView,
|
||||
Notifier: c.builder.cfg.Notifier,
|
||||
ChannelPruneExpiry: time.Hour * 24,
|
||||
GraphPruneInterval: time.Hour * 2,
|
||||
AssumeChannelValid: c.builder.cfg.AssumeChannelValid,
|
||||
FirstTimePruneDelay: c.builder.cfg.FirstTimePruneDelay,
|
||||
StrictZombiePruning: c.builder.cfg.StrictZombiePruning,
|
||||
IsAlias: func(scid lnwire.ShortChannelID) bool {
|
||||
return false
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, builder.Start())
|
||||
|
||||
// Finally, we'll swap out the pointer in the testCtx with this fresh
|
||||
// instance of the router.
|
||||
c.builder = builder
|
||||
}
|
||||
|
||||
// makeTestGraph creates a new instance of a channeldb.ChannelGraph for testing
|
||||
// purposes.
|
||||
func makeTestGraph(t *testing.T, useCache bool) (*channeldb.ChannelGraph,
|
||||
kvdb.Backend, error) {
|
||||
|
||||
// Create channelgraph for the first time.
|
||||
backend, backendCleanup, err := kvdb.GetTestBackend(t.TempDir(), "cgr")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
t.Cleanup(backendCleanup)
|
||||
|
||||
opts := channeldb.DefaultOptions()
|
||||
graph, err := channeldb.NewChannelGraph(
|
||||
backend, opts.RejectCacheSize, opts.ChannelCacheSize,
|
||||
opts.BatchCommitInterval, opts.PreAllocCacheNumNodes,
|
||||
useCache, false,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return graph, backend, nil
|
||||
}
|
||||
|
||||
type testGraphInstance struct {
|
||||
graph *channeldb.ChannelGraph
|
||||
graphBackend kvdb.Backend
|
||||
|
||||
// aliasMap is a map from a node's alias to its public key. This type is
|
||||
// provided in order to allow easily look up from the human memorable
|
||||
// alias to an exact node's public key.
|
||||
aliasMap map[string]route.Vertex
|
||||
|
||||
// privKeyMap maps a node alias to its private key. This is used to be
|
||||
// able to mock a remote node's signing behaviour.
|
||||
privKeyMap map[string]*btcec.PrivateKey
|
||||
|
||||
// channelIDs stores the channel ID for each node.
|
||||
channelIDs map[route.Vertex]map[route.Vertex]uint64
|
||||
|
||||
// links maps channel ids to a mock channel update handler.
|
||||
links map[lnwire.ShortChannelID]htlcswitch.ChannelLink
|
||||
}
|
||||
|
||||
func createTestCtxFromGraphInstance(t *testing.T,
|
||||
startingHeight uint32, graphInstance *testGraphInstance,
|
||||
strictPruning bool) *testCtx {
|
||||
|
||||
return createTestCtxFromGraphInstanceAssumeValid(
|
||||
t, startingHeight, graphInstance, false, strictPruning,
|
||||
)
|
||||
}
|
||||
|
||||
func createTestCtxFromGraphInstanceAssumeValid(t *testing.T,
|
||||
startingHeight uint32, graphInstance *testGraphInstance,
|
||||
assumeValid bool, strictPruning bool) *testCtx {
|
||||
|
||||
// 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)
|
||||
chainView := newMockChainView(chain)
|
||||
|
||||
notifier := &lnmock.ChainNotifier{
|
||||
EpochChan: make(chan *chainntnfs.BlockEpoch),
|
||||
SpendChan: make(chan *chainntnfs.SpendDetail),
|
||||
ConfChan: make(chan *chainntnfs.TxConfirmation),
|
||||
}
|
||||
|
||||
selfnode, err := graphInstance.graph.SourceNode()
|
||||
require.NoError(t, err)
|
||||
|
||||
graphBuilder, err := NewBuilder(&Config{
|
||||
SelfNode: selfnode.PubKeyBytes,
|
||||
Graph: graphInstance.graph,
|
||||
Chain: chain,
|
||||
ChainView: chainView,
|
||||
Notifier: notifier,
|
||||
ChannelPruneExpiry: time.Hour * 24,
|
||||
GraphPruneInterval: time.Hour * 2,
|
||||
AssumeChannelValid: assumeValid,
|
||||
FirstTimePruneDelay: 0,
|
||||
StrictZombiePruning: strictPruning,
|
||||
IsAlias: func(scid lnwire.ShortChannelID) bool {
|
||||
return false
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, graphBuilder.Start())
|
||||
|
||||
ctx := &testCtx{
|
||||
builder: graphBuilder,
|
||||
graph: graphInstance.graph,
|
||||
aliases: graphInstance.aliasMap,
|
||||
privKeys: graphInstance.privKeyMap,
|
||||
channelIDs: graphInstance.channelIDs,
|
||||
chain: chain,
|
||||
chainView: chainView,
|
||||
notifier: notifier,
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, graphBuilder.Stop())
|
||||
})
|
||||
|
||||
return ctx
|
||||
}
|
11
graph/setup_test.go
Normal file
11
graph/setup_test.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package graph
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
kvdb.RunTests(m)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package routing
|
||||
package graph
|
||||
|
||||
import (
|
||||
"fmt"
|
298
graph/testdata/basic_graph.json
vendored
Normal file
298
graph/testdata/basic_graph.json
vendored
Normal file
|
@ -0,0 +1,298 @@
|
|||
{
|
||||
"info": [
|
||||
"This file encodes a basic graph that resembles the following ascii graph:",
|
||||
"",
|
||||
" 50k satoshis ┌──────┐ ",
|
||||
" ┌───────────────────▶│luo ji│◀─┐ ",
|
||||
" │ └──────┘ │ ┌──────┐ ",
|
||||
" │ │ | elst | ",
|
||||
" │ │ └──────┘ ",
|
||||
" │ │ ▲ ",
|
||||
" │ │ | 100k sat ",
|
||||
" │ │ ▼ ",
|
||||
" ▼ │ ┌──────┐ ",
|
||||
" ┌────────┐ │ │sophon│◀┐ ",
|
||||
" │satoshi │ │ └──────┘ │ ",
|
||||
" └────────┘ │ ▲ │ ",
|
||||
" ▲ │ | │ 110k satoshis ",
|
||||
" │ ┌───────────────────┘ | │ ",
|
||||
" │ │ 100k satoshis | │ ",
|
||||
" │ │ | │ ",
|
||||
" │ │ 120k sat | │ ┌────────┐ ",
|
||||
" └──────────┤ (hi fee) ▼ └─▶│son goku│ ",
|
||||
" 10k satoshis │ ┌────────────┐ └────────┘ ",
|
||||
" │ | pham nuwen | ▲ ",
|
||||
" │ └────────────┘ │ ",
|
||||
" │ ▲ │ ",
|
||||
" ▼ | 120k sat (hi fee) │ ",
|
||||
" ┌──────────┐ | │ ",
|
||||
" │ roasbeef │◀──────────────┴──────────────────────┘ ",
|
||||
" └──────────┘ 100k satoshis ",
|
||||
|
||||
" the graph also includes a channel from roasbeef to sophon via pham nuwen"
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"source": true,
|
||||
"pubkey": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"alias": "roasbeef"
|
||||
},
|
||||
{
|
||||
"source": false,
|
||||
"pubkey": "026c43a8ac1cd8519985766e90748e1e06871dab0ff6b8af27e8c1a61640481318",
|
||||
"privkey": "82b266f659bd83a976bac11b2cc442baec5508e84e61085d7ec2b0fc52156c87",
|
||||
"alias": "songoku"
|
||||
},
|
||||
{
|
||||
"source": false,
|
||||
"pubkey": "03c19f0027ffbb0ae0e14a4d958788793f9d74e107462473ec0c3891e4feb12e99",
|
||||
"alias": "satoshi"
|
||||
},
|
||||
{
|
||||
"source": false,
|
||||
"pubkey": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||
"alias": "luoji"
|
||||
},
|
||||
{
|
||||
"source": false,
|
||||
"pubkey": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
||||
"alias": "sophon"
|
||||
},
|
||||
{
|
||||
"source": false,
|
||||
"pubkey": "02a1d2856be336a58af08989aea0d8c41e072ccc392c46f8ce0e6e069f002035f3",
|
||||
"alias": "phamnuwen"
|
||||
},
|
||||
{
|
||||
"source": false,
|
||||
"pubkey": "02a4b236b69b09b8efe6ccf822fa95ee95a0196451f4d066a450b7489e2e354a64",
|
||||
"alias": "elst"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"node_1": "02a4b236b69b09b8efe6ccf822fa95ee95a0196451f4d066a450b7489e2e354a64",
|
||||
"node_2": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
||||
"channel_id": 15433,
|
||||
"channel_point": "33bd5d49a50e284221561b91e781f1fca0d60341c9f9dd785b5e379a6d88af3d:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1000,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 200,
|
||||
"fee_rate": 0,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"node_1": "02a4b236b69b09b8efe6ccf822fa95ee95a0196451f4d066a450b7489e2e354a64",
|
||||
"node_2": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
||||
"channel_id": 15433,
|
||||
"channel_point": "33bd5d49a50e284221561b91e781f1fca0d60341c9f9dd785b5e379a6d88af3d:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1000,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 200,
|
||||
"fee_rate": 0,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"node_1": "02a1d2856be336a58af08989aea0d8c41e072ccc392c46f8ce0e6e069f002035f3",
|
||||
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"channel_id": 999991,
|
||||
"channel_point": "48a0e8b856fef01d9feda7d25a4fac6dae48749e28ba356b92d712ab7f5bd2d0:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1000,
|
||||
"max_htlc": 120000000,
|
||||
"fee_base_msat": 10000,
|
||||
"fee_rate": 100000,
|
||||
"capacity": 120000
|
||||
},
|
||||
{
|
||||
"node_1": "02a1d2856be336a58af08989aea0d8c41e072ccc392c46f8ce0e6e069f002035f3",
|
||||
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"channel_id": 999991,
|
||||
"channel_point": "48a0e8b856fef01d9feda7d25a4fac6dae48749e28ba356b92d712ab7f5bd2d0:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1000,
|
||||
"max_htlc": 120000000,
|
||||
"fee_base_msat": 10000,
|
||||
"fee_rate": 100000,
|
||||
"capacity": 120000
|
||||
},
|
||||
{
|
||||
"node_1": "02a1d2856be336a58af08989aea0d8c41e072ccc392c46f8ce0e6e069f002035f3",
|
||||
"node_2": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
||||
"channel_id": 99999,
|
||||
"channel_point": "05ffda8890d0a4fffe0ddca0b1932ba0415b1d5868a99515384a4e7883d96b88:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1000,
|
||||
"max_htlc": 120000000,
|
||||
"fee_base_msat": 10000,
|
||||
"fee_rate": 100000,
|
||||
"capacity": 120000
|
||||
},
|
||||
{
|
||||
"node_1": "02a1d2856be336a58af08989aea0d8c41e072ccc392c46f8ce0e6e069f002035f3",
|
||||
"node_2": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
||||
"channel_id": 99999,
|
||||
"channel_point": "05ffda8890d0a4fffe0ddca0b1932ba0415b1d5868a99515384a4e7883d96b88:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1000,
|
||||
"max_htlc": 120000000,
|
||||
"fee_base_msat": 10000,
|
||||
"fee_rate": 100000,
|
||||
"capacity": 120000
|
||||
},
|
||||
{
|
||||
"node_1": "026c43a8ac1cd8519985766e90748e1e06871dab0ff6b8af27e8c1a61640481318",
|
||||
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"channel_id": 12345,
|
||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1000,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 10,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"node_1": "026c43a8ac1cd8519985766e90748e1e06871dab0ff6b8af27e8c1a61640481318",
|
||||
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"channel_id": 12345,
|
||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 10,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"node_1": "026c43a8ac1cd8519985766e90748e1e06871dab0ff6b8af27e8c1a61640481318",
|
||||
"node_2": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
||||
"channel_id": 3495345,
|
||||
"channel_point": "9f155756b33a0a6827713965babbd561b55f9520444ac5db0cf7cb2eb0deb5bc:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 110000000,
|
||||
"fee_base_msat": 10,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 110000
|
||||
},
|
||||
{
|
||||
"node_1": "026c43a8ac1cd8519985766e90748e1e06871dab0ff6b8af27e8c1a61640481318",
|
||||
"node_2": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
||||
"channel_id": 3495345,
|
||||
"channel_point": "9f155756b33a0a6827713965babbd561b55f9520444ac5db0cf7cb2eb0deb5bc:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 110000000,
|
||||
"fee_base_msat": 10,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 110000
|
||||
},
|
||||
{
|
||||
"node_1": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"node_2": "03c19f0027ffbb0ae0e14a4d958788793f9d74e107462473ec0c3891e4feb12e99",
|
||||
"channel_id": 2340213491,
|
||||
"channel_point": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 10000000,
|
||||
"fee_base_msat": 10,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 10000
|
||||
},
|
||||
{
|
||||
"node_1": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"node_2": "03c19f0027ffbb0ae0e14a4d958788793f9d74e107462473ec0c3891e4feb12e99",
|
||||
"channel_id": 2340213491,
|
||||
"channel_point": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 10000000,
|
||||
"fee_base_msat": 10,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 10000
|
||||
},
|
||||
{
|
||||
"node_1": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"channel_id": 689530843,
|
||||
"channel_point": "25376aa6cb81913ad30416bd22d4083241bd6d68e811d0284d3c3a17795c458a:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 10,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 10,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"node_1": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"channel_id": 689530843,
|
||||
"channel_point": "25376aa6cb81913ad30416bd22d4083241bd6d68e811d0284d3c3a17795c458a:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 10,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"node_1": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||
"node_2": "03c19f0027ffbb0ae0e14a4d958788793f9d74e107462473ec0c3891e4feb12e99",
|
||||
"channel_id": 523452362,
|
||||
"channel_point": "704a5675c91b1c674309a6475fc51072c2913d6117ee6103c9f1b86956bcbe02:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 50000000,
|
||||
"fee_base_msat": 10,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 50000
|
||||
},
|
||||
{
|
||||
"node_1": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||
"node_2": "03c19f0027ffbb0ae0e14a4d958788793f9d74e107462473ec0c3891e4feb12e99",
|
||||
"channel_id": 523452362,
|
||||
"channel_point": "704a5675c91b1c674309a6475fc51072c2913d6117ee6103c9f1b86956bcbe02:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 1,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 50000000,
|
||||
"fee_base_msat": 10,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 50000
|
||||
}
|
||||
]
|
||||
}
|
147
graph/testdata/spec_example.json
vendored
Normal file
147
graph/testdata/spec_example.json
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
{
|
||||
"nodes": [
|
||||
{
|
||||
"source": false,
|
||||
"pubkey": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"alias": "A"
|
||||
},
|
||||
{
|
||||
"source": true,
|
||||
"pubkey": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
||||
"alias": "B"
|
||||
},
|
||||
{
|
||||
"source": false,
|
||||
"pubkey": "03c19f0027ffbb0ae0e14a4d958788793f9d74e107462473ec0c3891e4feb12e99",
|
||||
"alias": "C"
|
||||
},
|
||||
{
|
||||
"source": false,
|
||||
"pubkey": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||
"alias": "D"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
|
||||
"comment": "A -> B channel",
|
||||
"node_1": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
||||
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"channel_id": 12345,
|
||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 10,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 100,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"comment": "B -> A channel",
|
||||
"node_1": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
||||
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"channel_id": 12345,
|
||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 20,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 200,
|
||||
"fee_rate": 2000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"comment": "A -> D channel",
|
||||
"node_1": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"channel_id": 12345839,
|
||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 10,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 100,
|
||||
"fee_rate": 1000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"comment": "D -> A channel",
|
||||
"node_1": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||
"channel_id": 12345839,
|
||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 40,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 400,
|
||||
"fee_rate": 4000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"comment": "D -> C channel",
|
||||
"node_1": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||
"node_2": "03c19f0027ffbb0ae0e14a4d958788793f9d74e107462473ec0c3891e4feb12e99",
|
||||
"channel_id": 1234583,
|
||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 40,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 400,
|
||||
"fee_rate": 4000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"comment": "C -> D channel",
|
||||
"node_1": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||
"node_2": "03c19f0027ffbb0ae0e14a4d958788793f9d74e107462473ec0c3891e4feb12e99",
|
||||
"channel_id": 1234583,
|
||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 30,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 300,
|
||||
"fee_rate": 3000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"comment": "C -> B channel",
|
||||
"node_1": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
||||
"node_2": "03c19f0027ffbb0ae0e14a4d958788793f9d74e107462473ec0c3891e4feb12e99",
|
||||
"channel_id": 1234589,
|
||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||
"channel_flags": 1,
|
||||
"message_flags": 1,
|
||||
"expiry": 30,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 300,
|
||||
"fee_rate": 3000,
|
||||
"capacity": 100000
|
||||
},
|
||||
{
|
||||
"comment": "B -> C channel",
|
||||
"node_1": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
||||
"node_2": "03c19f0027ffbb0ae0e14a4d958788793f9d74e107462473ec0c3891e4feb12e99",
|
||||
"channel_id": 1234589,
|
||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||
"channel_flags": 0,
|
||||
"message_flags": 1,
|
||||
"expiry": 20,
|
||||
"min_htlc": 1,
|
||||
"max_htlc": 100000000,
|
||||
"fee_base_msat": 200,
|
||||
"fee_rate": 2000,
|
||||
"capacity": 100000
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package routing
|
||||
package graph
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,12 +1,12 @@
|
|||
package routing_test
|
||||
package graph_test
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
)
|
||||
|
||||
// TestValidationBarrierSemaphore checks basic properties of the validation
|
||||
|
@ -21,7 +21,7 @@ func TestValidationBarrierSemaphore(t *testing.T) {
|
|||
)
|
||||
|
||||
quit := make(chan struct{})
|
||||
barrier := routing.NewValidationBarrier(numTasks, quit)
|
||||
barrier := graph.NewValidationBarrier(numTasks, quit)
|
||||
|
||||
// Saturate the semaphore with jobs.
|
||||
for i := 0; i < numTasks; i++ {
|
||||
|
@ -69,7 +69,7 @@ func TestValidationBarrierQuit(t *testing.T) {
|
|||
)
|
||||
|
||||
quit := make(chan struct{})
|
||||
barrier := routing.NewValidationBarrier(2*numTasks, quit)
|
||||
barrier := graph.NewValidationBarrier(2*numTasks, quit)
|
||||
|
||||
// Create a set of unique channel announcements that we will prep for
|
||||
// validation.
|
||||
|
@ -141,8 +141,8 @@ func TestValidationBarrierQuit(t *testing.T) {
|
|||
|
||||
switch {
|
||||
// First half should return without failure.
|
||||
case i < numTasks/4 && !routing.IsError(
|
||||
err, routing.ErrParentValidationFailed,
|
||||
case i < numTasks/4 && !graph.IsError(
|
||||
err, graph.ErrParentValidationFailed,
|
||||
):
|
||||
t.Fatalf("unexpected failure while waiting: %v", err)
|
||||
|
||||
|
@ -150,11 +150,11 @@ func TestValidationBarrierQuit(t *testing.T) {
|
|||
t.Fatalf("unexpected failure while waiting: %v", err)
|
||||
|
||||
// Last half should return the shutdown error.
|
||||
case i >= numTasks/2 && !routing.IsError(
|
||||
err, routing.ErrVBarrierShuttingDown,
|
||||
case i >= numTasks/2 && !graph.IsError(
|
||||
err, graph.ErrVBarrierShuttingDown,
|
||||
):
|
||||
t.Fatalf("expected failure after quitting: want %v, "+
|
||||
"got %v", routing.ErrVBarrierShuttingDown, err)
|
||||
"got %v", graph.ErrVBarrierShuttingDown, err)
|
||||
}
|
||||
}
|
||||
}
|
2
log.go
2
log.go
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/contractcourt"
|
||||
"github.com/lightningnetwork/lnd/discovery"
|
||||
"github.com/lightningnetwork/lnd/funding"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/healthcheck"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
"github.com/lightningnetwork/lnd/invoices"
|
||||
|
@ -179,6 +180,7 @@ func SetupLoggers(root *build.RotatingLogWriter, interceptor signal.Interceptor)
|
|||
AddSubLogger(root, btcwallet.Subsystem, interceptor, btcwallet.UseLogger)
|
||||
AddSubLogger(root, rpcwallet.Subsystem, interceptor, rpcwallet.UseLogger)
|
||||
AddSubLogger(root, peersrpc.Subsystem, interceptor, peersrpc.UseLogger)
|
||||
AddSubLogger(root, graph.Subsystem, interceptor, graph.UseLogger)
|
||||
}
|
||||
|
||||
// AddSubLogger is a helper method to conveniently create and register the
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/netann"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
)
|
||||
|
||||
type mockSigner struct {
|
||||
|
@ -182,7 +182,7 @@ func TestUpdateDisableFlag(t *testing.T) {
|
|||
|
||||
// Finally, validate the signature using the router's
|
||||
// verification logic.
|
||||
err = routing.VerifyChannelUpdateSignature(
|
||||
err = graph.VerifyChannelUpdateSignature(
|
||||
newUpdate, pubKey,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
2
pilot.go
2
pilot.go
|
@ -295,6 +295,6 @@ func initAutoPilot(svr *server, cfg *lncfg.AutoPilot,
|
|||
}, nil
|
||||
},
|
||||
SubscribeTransactions: svr.cc.Wallet.SubscribeTransactions,
|
||||
SubscribeTopology: svr.chanRouter.SubscribeTopology,
|
||||
SubscribeTopology: svr.graphBuilder.SubscribeTopology,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ type bandwidthManager struct {
|
|||
// hints for the edges we directly have open ourselves. Obtaining these hints
|
||||
// allows us to reduce the number of extraneous attempts as we can skip channels
|
||||
// that are inactive, or just don't have enough bandwidth to carry the payment.
|
||||
func newBandwidthManager(graph routingGraph, sourceNode route.Vertex,
|
||||
func newBandwidthManager(graph Graph, sourceNode route.Vertex,
|
||||
linkQuery getLinkQuery) (*bandwidthManager, error) {
|
||||
|
||||
manager := &bandwidthManager{
|
||||
|
@ -49,7 +49,7 @@ func newBandwidthManager(graph routingGraph, sourceNode route.Vertex,
|
|||
|
||||
// First, we'll collect the set of outbound edges from the target
|
||||
// source node and add them to our bandwidth manager's map of channels.
|
||||
err := graph.forEachNodeChannel(sourceNode,
|
||||
err := graph.ForEachNodeChannel(sourceNode,
|
||||
func(channel *channeldb.DirectedChannel) error {
|
||||
shortID := lnwire.NewShortChanIDFromInt(
|
||||
channel.ChannelID,
|
||||
|
|
104
routing/graph.go
104
routing/graph.go
|
@ -5,110 +5,46 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
// routingGraph is an abstract interface that provides information about nodes
|
||||
// and edges to pathfinding.
|
||||
type routingGraph interface {
|
||||
// forEachNodeChannel calls the callback for every channel of the given
|
||||
// Graph is an abstract interface that provides information about nodes and
|
||||
// edges to pathfinding.
|
||||
type Graph interface {
|
||||
// ForEachNodeChannel calls the callback for every channel of the given
|
||||
// node.
|
||||
forEachNodeChannel(nodePub route.Vertex,
|
||||
ForEachNodeChannel(nodePub route.Vertex,
|
||||
cb func(channel *channeldb.DirectedChannel) error) error
|
||||
|
||||
// sourceNode returns the source node of the graph.
|
||||
sourceNode() route.Vertex
|
||||
|
||||
// fetchNodeFeatures returns the features of the given node.
|
||||
fetchNodeFeatures(nodePub route.Vertex) (*lnwire.FeatureVector, error)
|
||||
|
||||
// FetchAmountPairCapacity determines the maximal capacity between two
|
||||
// pairs of nodes.
|
||||
FetchAmountPairCapacity(nodeFrom, nodeTo route.Vertex,
|
||||
amount lnwire.MilliSatoshi) (btcutil.Amount, error)
|
||||
// FetchNodeFeatures returns the features of the given node.
|
||||
FetchNodeFeatures(nodePub route.Vertex) (*lnwire.FeatureVector, error)
|
||||
}
|
||||
|
||||
// CachedGraph is a routingGraph implementation that retrieves from the
|
||||
// database.
|
||||
type CachedGraph struct {
|
||||
graph *channeldb.ChannelGraph
|
||||
tx kvdb.RTx
|
||||
source route.Vertex
|
||||
}
|
||||
|
||||
// A compile time assertion to make sure CachedGraph implements the routingGraph
|
||||
// interface.
|
||||
var _ routingGraph = (*CachedGraph)(nil)
|
||||
|
||||
// NewCachedGraph instantiates a new db-connected routing graph. It implicitly
|
||||
// instantiates a new read transaction.
|
||||
func NewCachedGraph(sourceNode *channeldb.LightningNode,
|
||||
graph *channeldb.ChannelGraph) (*CachedGraph, error) {
|
||||
|
||||
tx, err := graph.NewPathFindTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CachedGraph{
|
||||
graph: graph,
|
||||
tx: tx,
|
||||
source: sourceNode.PubKeyBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close attempts to close the underlying db transaction. This is a no-op in
|
||||
// case the underlying graph uses an in-memory cache.
|
||||
func (g *CachedGraph) Close() error {
|
||||
if g.tx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return g.tx.Rollback()
|
||||
}
|
||||
|
||||
// forEachNodeChannel calls the callback for every channel of the given node.
|
||||
//
|
||||
// NOTE: Part of the routingGraph interface.
|
||||
func (g *CachedGraph) forEachNodeChannel(nodePub route.Vertex,
|
||||
cb func(channel *channeldb.DirectedChannel) error) error {
|
||||
|
||||
return g.graph.ForEachNodeDirectedChannel(g.tx, nodePub, cb)
|
||||
}
|
||||
|
||||
// sourceNode returns the source node of the graph.
|
||||
//
|
||||
// NOTE: Part of the routingGraph interface.
|
||||
func (g *CachedGraph) sourceNode() route.Vertex {
|
||||
return g.source
|
||||
}
|
||||
|
||||
// fetchNodeFeatures returns the features of the given node. If the node is
|
||||
// unknown, assume no additional features are supported.
|
||||
//
|
||||
// NOTE: Part of the routingGraph interface.
|
||||
func (g *CachedGraph) fetchNodeFeatures(nodePub route.Vertex) (
|
||||
*lnwire.FeatureVector, error) {
|
||||
|
||||
return g.graph.FetchNodeFeatures(nodePub)
|
||||
// GraphSessionFactory can be used to produce a new Graph instance which can
|
||||
// then be used for a path-finding session. Depending on the implementation,
|
||||
// the Graph session will represent a DB connection where a read-lock is being
|
||||
// held across calls to the backing Graph.
|
||||
type GraphSessionFactory interface {
|
||||
// NewGraphSession will produce a new Graph to use for a path-finding
|
||||
// session. It returns the Graph along with a call-back that must be
|
||||
// called once Graph access is complete. This call-back will close any
|
||||
// read-only transaction that was created at Graph construction time.
|
||||
NewGraphSession() (Graph, func() error, error)
|
||||
}
|
||||
|
||||
// FetchAmountPairCapacity determines the maximal public capacity between two
|
||||
// nodes depending on the amount we try to send.
|
||||
//
|
||||
// NOTE: Part of the routingGraph interface.
|
||||
func (g *CachedGraph) FetchAmountPairCapacity(nodeFrom, nodeTo route.Vertex,
|
||||
func FetchAmountPairCapacity(graph Graph, source, nodeFrom, nodeTo route.Vertex,
|
||||
amount lnwire.MilliSatoshi) (btcutil.Amount, error) {
|
||||
|
||||
// Create unified edges for all incoming connections.
|
||||
//
|
||||
// Note: Inbound fees are not used here because this method is only used
|
||||
// by a deprecated router rpc.
|
||||
u := newNodeEdgeUnifier(g.sourceNode(), nodeTo, false, nil)
|
||||
u := newNodeEdgeUnifier(source, nodeTo, false, nil)
|
||||
|
||||
err := u.addGraphPolicies(g)
|
||||
err := u.addGraphPolicies(graph)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
|
@ -163,7 +164,7 @@ func (c *integratedRoutingContext) testPayment(maxParts uint32,
|
|||
c.t.Fatal(err)
|
||||
}
|
||||
|
||||
getBandwidthHints := func(_ routingGraph) (bandwidthHints, error) {
|
||||
getBandwidthHints := func(_ Graph) (bandwidthHints, error) {
|
||||
// Create bandwidth hints based on local channel balances.
|
||||
bandwidthHints := map[uint64]lnwire.MilliSatoshi{}
|
||||
for _, ch := range c.graph.nodes[c.source.pubkey].channels {
|
||||
|
@ -200,11 +201,8 @@ func (c *integratedRoutingContext) testPayment(maxParts uint32,
|
|||
}
|
||||
|
||||
session, err := newPaymentSession(
|
||||
&payment, getBandwidthHints,
|
||||
func() (routingGraph, func(), error) {
|
||||
return c.graph, func() {}, nil
|
||||
},
|
||||
mc, c.pathFindingCfg,
|
||||
&payment, c.graph.source.pubkey, getBandwidthHints,
|
||||
newMockGraphSessionFactory(c.graph), mc, c.pathFindingCfg,
|
||||
)
|
||||
if err != nil {
|
||||
c.t.Fatal(err)
|
||||
|
@ -307,3 +305,88 @@ func getNodeIndex(route *route.Route, failureSource route.Vertex) *int {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockGraphSessionFactory struct {
|
||||
Graph
|
||||
}
|
||||
|
||||
func newMockGraphSessionFactory(graph Graph) GraphSessionFactory {
|
||||
return &mockGraphSessionFactory{Graph: graph}
|
||||
}
|
||||
|
||||
func (m *mockGraphSessionFactory) NewGraphSession() (Graph, func() error,
|
||||
error) {
|
||||
|
||||
return m, func() error {
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ GraphSessionFactory = (*mockGraphSessionFactory)(nil)
|
||||
var _ Graph = (*mockGraphSessionFactory)(nil)
|
||||
|
||||
type mockGraphSessionFactoryChanDB struct {
|
||||
graph *channeldb.ChannelGraph
|
||||
}
|
||||
|
||||
func newMockGraphSessionFactoryFromChanDB(
|
||||
graph *channeldb.ChannelGraph) *mockGraphSessionFactoryChanDB {
|
||||
|
||||
return &mockGraphSessionFactoryChanDB{
|
||||
graph: graph,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *mockGraphSessionFactoryChanDB) NewGraphSession() (Graph, func() error,
|
||||
error) {
|
||||
|
||||
tx, err := g.graph.NewPathFindTx()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
session := &mockGraphSessionChanDB{
|
||||
graph: g.graph,
|
||||
tx: tx,
|
||||
}
|
||||
|
||||
return session, session.close, nil
|
||||
}
|
||||
|
||||
var _ GraphSessionFactory = (*mockGraphSessionFactoryChanDB)(nil)
|
||||
|
||||
type mockGraphSessionChanDB struct {
|
||||
graph *channeldb.ChannelGraph
|
||||
tx kvdb.RTx
|
||||
}
|
||||
|
||||
func newMockGraphSessionChanDB(graph *channeldb.ChannelGraph) Graph {
|
||||
return &mockGraphSessionChanDB{
|
||||
graph: graph,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *mockGraphSessionChanDB) close() error {
|
||||
if g.tx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := g.tx.Rollback()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error closing db tx: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *mockGraphSessionChanDB) ForEachNodeChannel(nodePub route.Vertex,
|
||||
cb func(channel *channeldb.DirectedChannel) error) error {
|
||||
|
||||
return g.graph.ForEachNodeDirectedChannel(g.tx, nodePub, cb)
|
||||
}
|
||||
|
||||
func (g *mockGraphSessionChanDB) FetchNodeFeatures(nodePub route.Vertex) (
|
||||
*lnwire.FeatureVector, error) {
|
||||
|
||||
return g.graph.FetchNodeFeatures(nodePub)
|
||||
}
|
||||
|
|
|
@ -164,8 +164,8 @@ func (m *mockGraph) addChannel(id uint64, node1id, node2id byte,
|
|||
|
||||
// forEachNodeChannel calls the callback for every channel of the given node.
|
||||
//
|
||||
// NOTE: Part of the routingGraph interface.
|
||||
func (m *mockGraph) forEachNodeChannel(nodePub route.Vertex,
|
||||
// NOTE: Part of the Graph interface.
|
||||
func (m *mockGraph) ForEachNodeChannel(nodePub route.Vertex,
|
||||
cb func(channel *channeldb.DirectedChannel) error) error {
|
||||
|
||||
// Look up the mock node.
|
||||
|
@ -213,45 +213,20 @@ func (m *mockGraph) forEachNodeChannel(nodePub route.Vertex,
|
|||
|
||||
// sourceNode returns the source node of the graph.
|
||||
//
|
||||
// NOTE: Part of the routingGraph interface.
|
||||
// NOTE: Part of the Graph interface.
|
||||
func (m *mockGraph) sourceNode() route.Vertex {
|
||||
return m.source.pubkey
|
||||
}
|
||||
|
||||
// fetchNodeFeatures returns the features of the given node.
|
||||
//
|
||||
// NOTE: Part of the routingGraph interface.
|
||||
func (m *mockGraph) fetchNodeFeatures(nodePub route.Vertex) (
|
||||
// NOTE: Part of the Graph interface.
|
||||
func (m *mockGraph) FetchNodeFeatures(nodePub route.Vertex) (
|
||||
*lnwire.FeatureVector, error) {
|
||||
|
||||
return lnwire.EmptyFeatureVector(), nil
|
||||
}
|
||||
|
||||
// FetchAmountPairCapacity returns the maximal capacity between nodes in the
|
||||
// graph.
|
||||
//
|
||||
// NOTE: Part of the routingGraph interface.
|
||||
func (m *mockGraph) FetchAmountPairCapacity(nodeFrom, nodeTo route.Vertex,
|
||||
amount lnwire.MilliSatoshi) (btcutil.Amount, error) {
|
||||
|
||||
var capacity btcutil.Amount
|
||||
|
||||
cb := func(channel *channeldb.DirectedChannel) error {
|
||||
if channel.OtherNode == nodeTo {
|
||||
capacity = channel.Capacity
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := m.forEachNodeChannel(nodeFrom, cb)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return capacity, nil
|
||||
}
|
||||
|
||||
// htlcResult describes the resolution of an htlc. If failure is nil, the htlc
|
||||
// was settled.
|
||||
type htlcResult struct {
|
||||
|
@ -295,5 +270,5 @@ func (m *mockGraph) sendHtlc(route *route.Route) (htlcResult, error) {
|
|||
return source.fwd(nil, next)
|
||||
}
|
||||
|
||||
// Compile-time check for the routingGraph interface.
|
||||
var _ routingGraph = &mockGraph{}
|
||||
// Compile-time check for the Graph interface.
|
||||
var _ Graph = &mockGraph{}
|
||||
|
|
|
@ -48,7 +48,7 @@ const (
|
|||
|
||||
// pathFinder defines the interface of a path finding algorithm.
|
||||
type pathFinder = func(g *graphParams, r *RestrictParams,
|
||||
cfg *PathFindingConfig, source, target route.Vertex,
|
||||
cfg *PathFindingConfig, self, source, target route.Vertex,
|
||||
amt lnwire.MilliSatoshi, timePref float64, finalHtlcExpiry int32) (
|
||||
[]*unifiedEdge, float64, error)
|
||||
|
||||
|
@ -369,7 +369,7 @@ func edgeWeight(lockedAmt lnwire.MilliSatoshi, fee lnwire.MilliSatoshi,
|
|||
// graphParams wraps the set of graph parameters passed to findPath.
|
||||
type graphParams struct {
|
||||
// graph is the ChannelGraph to be used during path finding.
|
||||
graph routingGraph
|
||||
graph Graph
|
||||
|
||||
// additionalEdges is an optional set of edges that should be
|
||||
// considered during path finding, that is not already found in the
|
||||
|
@ -464,7 +464,7 @@ type PathFindingConfig struct {
|
|||
// available balance.
|
||||
func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
|
||||
bandwidthHints bandwidthHints,
|
||||
g routingGraph) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, error) {
|
||||
g Graph) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, error) {
|
||||
|
||||
var max, total lnwire.MilliSatoshi
|
||||
cb := func(channel *channeldb.DirectedChannel) error {
|
||||
|
@ -502,7 +502,7 @@ func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
|
|||
}
|
||||
|
||||
// Iterate over all channels of the to node.
|
||||
err := g.forEachNodeChannel(node, cb)
|
||||
err := g.ForEachNodeChannel(node, cb)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
@ -521,8 +521,9 @@ func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
|
|||
// path and accurately check the amount to forward at every node against the
|
||||
// available bandwidth.
|
||||
func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
||||
source, target route.Vertex, amt lnwire.MilliSatoshi, timePref float64,
|
||||
finalHtlcExpiry int32) ([]*unifiedEdge, float64, error) {
|
||||
self, source, target route.Vertex, amt lnwire.MilliSatoshi,
|
||||
timePref float64, finalHtlcExpiry int32) ([]*unifiedEdge, float64,
|
||||
error) {
|
||||
|
||||
// Pathfinding can be a significant portion of the total payment
|
||||
// latency, especially on low-powered devices. Log several metrics to
|
||||
|
@ -541,7 +542,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
|||
features := r.DestFeatures
|
||||
if features == nil {
|
||||
var err error
|
||||
features, err = g.graph.fetchNodeFeatures(target)
|
||||
features, err = g.graph.FetchNodeFeatures(target)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
@ -583,8 +584,6 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
|||
|
||||
// If we are routing from ourselves, check that we have enough local
|
||||
// balance available.
|
||||
self := g.graph.sourceNode()
|
||||
|
||||
if source == self {
|
||||
max, total, err := getOutgoingBalance(
|
||||
self, outgoingChanMap, g.bandwidthHints, g.graph,
|
||||
|
@ -921,7 +920,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
|||
}
|
||||
|
||||
// Fetch node features fresh from the graph.
|
||||
fromFeatures, err := g.graph.fetchNodeFeatures(node)
|
||||
fromFeatures, err := g.graph.FetchNodeFeatures(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -1392,10 +1392,6 @@ func TestNewRoute(t *testing.T) {
|
|||
// to fail or succeed.
|
||||
expectError bool
|
||||
|
||||
// expectedErrorCode indicates the expected error code when
|
||||
// expectError is true.
|
||||
expectedErrorCode errorCode
|
||||
|
||||
expectedMPP *record.MPP
|
||||
}{
|
||||
{
|
||||
|
@ -1606,23 +1602,9 @@ func TestNewRoute(t *testing.T) {
|
|||
metadata: testCase.metadata,
|
||||
}, nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if testCase.expectError {
|
||||
expectedCode := testCase.expectedErrorCode
|
||||
if err == nil || !IsError(err, expectedCode) {
|
||||
t.Fatalf("expected newRoute to fail "+
|
||||
"with error code %v but got "+
|
||||
"%v instead",
|
||||
expectedCode, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("unable to create path: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
assertRoute(t, route)
|
||||
}
|
||||
assertRoute(t, route)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -2227,17 +2209,12 @@ func TestPathFindSpecExample(t *testing.T) {
|
|||
// Carol, so we set "B" as the source node so path finding starts from
|
||||
// Bob.
|
||||
bob := ctx.aliases["B"]
|
||||
bobNode, err := ctx.graph.FetchLightningNode(nil, bob)
|
||||
require.NoError(t, err, "unable to find bob")
|
||||
if err := ctx.graph.SetSourceNode(bobNode); err != nil {
|
||||
t.Fatalf("unable to set source node: %v", err)
|
||||
}
|
||||
|
||||
// Query for a route of 4,999,999 mSAT to carol.
|
||||
carol := ctx.aliases["C"]
|
||||
const amt lnwire.MilliSatoshi = 4999999
|
||||
req, err := NewRouteRequest(
|
||||
bobNode.PubKeyBytes, &carol, amt, 0, noRestrictions, nil, nil,
|
||||
bob, &carol, amt, 0, noRestrictions, nil, nil,
|
||||
nil, MinCLTVDelta,
|
||||
)
|
||||
require.NoError(t, err, "invalid route request")
|
||||
|
@ -2249,49 +2226,23 @@ func TestPathFindSpecExample(t *testing.T) {
|
|||
//
|
||||
// It should be sending the exact payment amount as there are no
|
||||
// additional hops.
|
||||
if route.TotalAmount != amt {
|
||||
t.Fatalf("wrong total amount: got %v, expected %v",
|
||||
route.TotalAmount, amt)
|
||||
}
|
||||
if route.Hops[0].AmtToForward != amt {
|
||||
t.Fatalf("wrong forward amount: got %v, expected %v",
|
||||
route.Hops[0].AmtToForward, amt)
|
||||
}
|
||||
|
||||
fee := route.HopFee(0)
|
||||
if fee != 0 {
|
||||
t.Fatalf("wrong hop fee: got %v, expected %v", fee, 0)
|
||||
}
|
||||
require.Equal(t, amt, route.TotalAmount)
|
||||
require.Equal(t, amt, route.Hops[0].AmtToForward)
|
||||
require.Zero(t, route.HopFee(0))
|
||||
|
||||
// The CLTV expiry should be the current height plus 18 (the expiry for
|
||||
// the B -> C channel.
|
||||
if route.TotalTimeLock !=
|
||||
startingHeight+MinCLTVDelta {
|
||||
|
||||
t.Fatalf("wrong total time lock: got %v, expecting %v",
|
||||
route.TotalTimeLock,
|
||||
startingHeight+MinCLTVDelta)
|
||||
}
|
||||
require.EqualValues(t, startingHeight+MinCLTVDelta, route.TotalTimeLock)
|
||||
|
||||
// Next, we'll set A as the source node so we can assert that we create
|
||||
// the proper route for any queries starting with Alice.
|
||||
alice := ctx.aliases["A"]
|
||||
aliceNode, err := ctx.graph.FetchLightningNode(nil, alice)
|
||||
require.NoError(t, err, "unable to find alice")
|
||||
if err := ctx.graph.SetSourceNode(aliceNode); err != nil {
|
||||
t.Fatalf("unable to set source node: %v", err)
|
||||
}
|
||||
ctx.router.selfNode = aliceNode
|
||||
source, err := ctx.graph.SourceNode()
|
||||
require.NoError(t, err, "unable to retrieve source node")
|
||||
if source.PubKeyBytes != alice {
|
||||
t.Fatalf("source node not set")
|
||||
}
|
||||
ctx.router.cfg.SelfNode = alice
|
||||
|
||||
// We'll now request a route from A -> B -> C.
|
||||
req, err = NewRouteRequest(
|
||||
source.PubKeyBytes, &carol, amt, 0, noRestrictions, nil, nil,
|
||||
nil, MinCLTVDelta,
|
||||
alice, &carol, amt, 0, noRestrictions, nil, nil, nil,
|
||||
MinCLTVDelta,
|
||||
)
|
||||
require.NoError(t, err, "invalid route request")
|
||||
|
||||
|
@ -2299,32 +2250,21 @@ func TestPathFindSpecExample(t *testing.T) {
|
|||
require.NoError(t, err, "unable to find routes")
|
||||
|
||||
// The route should be two hops.
|
||||
if len(route.Hops) != 2 {
|
||||
t.Fatalf("route should be %v hops, is instead %v", 2,
|
||||
len(route.Hops))
|
||||
}
|
||||
require.Len(t, route.Hops, 2)
|
||||
|
||||
// The total amount should factor in a fee of 10199 and also use a CLTV
|
||||
// delta total of 38 (20 + 18),
|
||||
expectedAmt := lnwire.MilliSatoshi(5010198)
|
||||
if route.TotalAmount != expectedAmt {
|
||||
t.Fatalf("wrong amount: got %v, expected %v",
|
||||
route.TotalAmount, expectedAmt)
|
||||
}
|
||||
require.Equal(t, expectedAmt, route.TotalAmount)
|
||||
|
||||
expectedDelta := uint32(20 + MinCLTVDelta)
|
||||
if route.TotalTimeLock != startingHeight+expectedDelta {
|
||||
t.Fatalf("wrong total time lock: got %v, expecting %v",
|
||||
route.TotalTimeLock, startingHeight+expectedDelta)
|
||||
}
|
||||
require.Equal(t, startingHeight+expectedDelta, route.TotalTimeLock)
|
||||
|
||||
// Ensure that the hops of the route are properly crafted.
|
||||
//
|
||||
// After taking the fee, Bob should be forwarding the remainder which
|
||||
// is the exact payment to Bob.
|
||||
if route.Hops[0].AmtToForward != amt {
|
||||
t.Fatalf("wrong forward amount: got %v, expected %v",
|
||||
route.Hops[0].AmtToForward, amt)
|
||||
}
|
||||
require.Equal(t, amt, route.Hops[0].AmtToForward)
|
||||
|
||||
// We shouldn't pay any fee for the first, hop, but the fee for the
|
||||
// second hop posted fee should be exactly:
|
||||
|
@ -2333,59 +2273,33 @@ func TestPathFindSpecExample(t *testing.T) {
|
|||
// hop, so we should get a fee of exactly:
|
||||
//
|
||||
// * 200 + 4999999 * 2000 / 1000000 = 10199
|
||||
|
||||
fee = route.HopFee(0)
|
||||
if fee != 10199 {
|
||||
t.Fatalf("wrong hop fee: got %v, expected %v", fee, 10199)
|
||||
}
|
||||
require.EqualValues(t, 10199, route.HopFee(0))
|
||||
|
||||
// While for the final hop, as there's no additional hop afterwards, we
|
||||
// pay no fee.
|
||||
fee = route.HopFee(1)
|
||||
if fee != 0 {
|
||||
t.Fatalf("wrong hop fee: got %v, expected %v", fee, 0)
|
||||
}
|
||||
require.Zero(t, route.HopFee(1))
|
||||
|
||||
// The outgoing CLTV value itself should be the current height plus 30
|
||||
// to meet Carol's requirements.
|
||||
if route.Hops[0].OutgoingTimeLock !=
|
||||
startingHeight+MinCLTVDelta {
|
||||
|
||||
t.Fatalf("wrong total time lock: got %v, expecting %v",
|
||||
route.Hops[0].OutgoingTimeLock,
|
||||
startingHeight+MinCLTVDelta)
|
||||
}
|
||||
require.EqualValues(t, startingHeight+MinCLTVDelta,
|
||||
route.Hops[0].OutgoingTimeLock)
|
||||
|
||||
// For B -> C, we assert that the final hop also has the proper
|
||||
// parameters.
|
||||
lastHop := route.Hops[1]
|
||||
if lastHop.AmtToForward != amt {
|
||||
t.Fatalf("wrong forward amount: got %v, expected %v",
|
||||
lastHop.AmtToForward, amt)
|
||||
}
|
||||
if lastHop.OutgoingTimeLock !=
|
||||
startingHeight+MinCLTVDelta {
|
||||
|
||||
t.Fatalf("wrong total time lock: got %v, expecting %v",
|
||||
lastHop.OutgoingTimeLock,
|
||||
startingHeight+MinCLTVDelta)
|
||||
}
|
||||
require.EqualValues(t, amt, lastHop.AmtToForward)
|
||||
require.EqualValues(t, startingHeight+MinCLTVDelta,
|
||||
lastHop.OutgoingTimeLock)
|
||||
}
|
||||
|
||||
func assertExpectedPath(t *testing.T, aliasMap map[string]route.Vertex,
|
||||
path []*unifiedEdge, nodeAliases ...string) {
|
||||
|
||||
if len(path) != len(nodeAliases) {
|
||||
t.Fatalf("number of hops=(%v) and number of aliases=(%v) do "+
|
||||
"not match", len(path), len(nodeAliases))
|
||||
}
|
||||
require.Len(t, path, len(nodeAliases))
|
||||
|
||||
for i, hop := range path {
|
||||
if hop.policy.ToNodePubKey() != aliasMap[nodeAliases[i]] {
|
||||
t.Fatalf("expected %v to be pos #%v in hop, instead "+
|
||||
"%v was", nodeAliases[i], i,
|
||||
hop.policy.ToNodePubKey())
|
||||
}
|
||||
require.Equal(t, aliasMap[nodeAliases[i]],
|
||||
hop.policy.ToNodePubKey())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2396,9 +2310,7 @@ func TestNewRouteFromEmptyHops(t *testing.T) {
|
|||
|
||||
var source route.Vertex
|
||||
_, err := route.NewRouteFromHops(0, 0, source, []*route.Hop{})
|
||||
if err != route.ErrNoRouteHopsProvided {
|
||||
t.Fatalf("expected empty hops error: instead got: %v", err)
|
||||
}
|
||||
require.ErrorIs(t, err, route.ErrNoRouteHopsProvided)
|
||||
}
|
||||
|
||||
// runRestrictOutgoingChannel asserts that a outgoing channel restriction is
|
||||
|
@ -2441,11 +2353,6 @@ func runRestrictOutgoingChannel(t *testing.T, useCache bool) {
|
|||
|
||||
ctx := newPathFindingTestContext(t, useCache, testChannels, "roasbeef")
|
||||
|
||||
const (
|
||||
startingHeight = 100
|
||||
finalHopCLTV = 1
|
||||
)
|
||||
|
||||
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
||||
target := ctx.keyFromAlias("target")
|
||||
outgoingChannelID := uint64(chanSourceB1)
|
||||
|
@ -3201,14 +3108,16 @@ func dbFindPath(graph *channeldb.ChannelGraph,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
routingGraph, err := NewCachedGraph(sourceNode, graph)
|
||||
graphSessFactory := newMockGraphSessionFactoryFromChanDB(graph)
|
||||
|
||||
graphSess, closeGraphSess, err := graphSessFactory.NewGraphSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := routingGraph.Close(); err != nil {
|
||||
log.Errorf("Error closing db tx: %v", err)
|
||||
if err := closeGraphSess(); err != nil {
|
||||
log.Errorf("Error closing graph session: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -3216,9 +3125,10 @@ func dbFindPath(graph *channeldb.ChannelGraph,
|
|||
&graphParams{
|
||||
additionalEdges: additionalEdges,
|
||||
bandwidthHints: bandwidthHints,
|
||||
graph: routingGraph,
|
||||
graph: graphSess,
|
||||
},
|
||||
r, cfg, source, target, amt, timePref, finalHtlcExpiry,
|
||||
r, cfg, sourceNode.PubKeyBytes, source, target, amt, timePref,
|
||||
finalHtlcExpiry,
|
||||
)
|
||||
|
||||
return route, err
|
||||
|
|
|
@ -912,7 +912,7 @@ func (p *paymentLifecycle) handleFailureMessage(rt *route.Route,
|
|||
}
|
||||
|
||||
// Apply channel update to the channel edge policy in our db.
|
||||
if !p.router.applyChannelUpdate(update) {
|
||||
if !p.router.cfg.ApplyChannelUpdate(update) {
|
||||
log.Debugf("Invalid channel update received: node=%v",
|
||||
errVertex)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/build"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
@ -163,9 +164,11 @@ type PaymentSession interface {
|
|||
// loop if payment attempts take long enough. An additional set of edges can
|
||||
// also be provided to assist in reaching the payment's destination.
|
||||
type paymentSession struct {
|
||||
selfNode route.Vertex
|
||||
|
||||
additionalEdges map[route.Vertex][]AdditionalEdge
|
||||
|
||||
getBandwidthHints func(routingGraph) (bandwidthHints, error)
|
||||
getBandwidthHints func(Graph) (bandwidthHints, error)
|
||||
|
||||
payment *LightningPayment
|
||||
|
||||
|
@ -173,7 +176,7 @@ type paymentSession struct {
|
|||
|
||||
pathFinder pathFinder
|
||||
|
||||
getRoutingGraph func() (routingGraph, func(), error)
|
||||
graphSessFactory GraphSessionFactory
|
||||
|
||||
// pathFindingConfig defines global parameters that control the
|
||||
// trade-off in path finding between fees and probability.
|
||||
|
@ -192,11 +195,10 @@ type paymentSession struct {
|
|||
}
|
||||
|
||||
// newPaymentSession instantiates a new payment session.
|
||||
func newPaymentSession(p *LightningPayment,
|
||||
getBandwidthHints func(routingGraph) (bandwidthHints, error),
|
||||
getRoutingGraph func() (routingGraph, func(), error),
|
||||
missionControl MissionController, pathFindingConfig PathFindingConfig) (
|
||||
*paymentSession, error) {
|
||||
func newPaymentSession(p *LightningPayment, selfNode route.Vertex,
|
||||
getBandwidthHints func(Graph) (bandwidthHints, error),
|
||||
graphSessFactory GraphSessionFactory, missionControl MissionController,
|
||||
pathFindingConfig PathFindingConfig) (*paymentSession, error) {
|
||||
|
||||
edges, err := RouteHintsToEdges(p.RouteHints, p.Target)
|
||||
if err != nil {
|
||||
|
@ -206,11 +208,12 @@ func newPaymentSession(p *LightningPayment,
|
|||
logPrefix := fmt.Sprintf("PaymentSession(%x):", p.Identifier())
|
||||
|
||||
return &paymentSession{
|
||||
selfNode: selfNode,
|
||||
additionalEdges: edges,
|
||||
getBandwidthHints: getBandwidthHints,
|
||||
payment: p,
|
||||
pathFinder: findPath,
|
||||
getRoutingGraph: getRoutingGraph,
|
||||
graphSessFactory: graphSessFactory,
|
||||
pathFindingConfig: pathFindingConfig,
|
||||
missionControl: missionControl,
|
||||
minShardAmt: DefaultShardMinAmt,
|
||||
|
@ -277,8 +280,8 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
|||
}
|
||||
|
||||
for {
|
||||
// Get a routing graph.
|
||||
routingGraph, cleanup, err := p.getRoutingGraph()
|
||||
// Get a routing graph session.
|
||||
graph, closeGraph, err := p.graphSessFactory.NewGraphSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -289,29 +292,35 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
|||
// don't have enough bandwidth to carry the payment. New
|
||||
// bandwidth hints are queried for every new path finding
|
||||
// attempt, because concurrent payments may change balances.
|
||||
bandwidthHints, err := p.getBandwidthHints(routingGraph)
|
||||
bandwidthHints, err := p.getBandwidthHints(graph)
|
||||
if err != nil {
|
||||
// Close routing graph session.
|
||||
if graphErr := closeGraph(); graphErr != nil {
|
||||
log.Errorf("could not close graph session: %v",
|
||||
graphErr)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.log.Debugf("pathfinding for amt=%v", maxAmt)
|
||||
|
||||
sourceVertex := routingGraph.sourceNode()
|
||||
|
||||
// Find a route for the current amount.
|
||||
path, _, err := p.pathFinder(
|
||||
&graphParams{
|
||||
additionalEdges: p.additionalEdges,
|
||||
bandwidthHints: bandwidthHints,
|
||||
graph: routingGraph,
|
||||
graph: graph,
|
||||
},
|
||||
restrictions, &p.pathFindingConfig,
|
||||
sourceVertex, p.payment.Target,
|
||||
p.selfNode, p.selfNode, p.payment.Target,
|
||||
maxAmt, p.payment.TimePref, finalHtlcExpiry,
|
||||
)
|
||||
|
||||
// Close routing graph.
|
||||
cleanup()
|
||||
// Close routing graph session.
|
||||
if err := closeGraph(); err != nil {
|
||||
log.Errorf("could not close graph session: %v", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case err == errNoPathFound:
|
||||
|
@ -384,7 +393,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
|||
// this into a route by applying the time-lock and fee
|
||||
// requirements.
|
||||
route, err := newRoute(
|
||||
sourceVertex, path, height,
|
||||
p.selfNode, path, height,
|
||||
finalHopParams{
|
||||
amt: maxAmt,
|
||||
totalAmt: p.payment.Amount,
|
||||
|
@ -410,7 +419,7 @@ func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate,
|
|||
pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool {
|
||||
|
||||
// Validate the message signature.
|
||||
if err := VerifyChannelUpdateSignature(msg, pubKey); err != nil {
|
||||
if err := graph.VerifyChannelUpdateSignature(msg, pubKey); err != nil {
|
||||
log.Errorf(
|
||||
"Unable to validate channel update signature: %v", err,
|
||||
)
|
||||
|
|
|
@ -16,9 +16,10 @@ var _ PaymentSessionSource = (*SessionSource)(nil)
|
|||
// SessionSource defines a source for the router to retrieve new payment
|
||||
// sessions.
|
||||
type SessionSource struct {
|
||||
// Graph is the channel graph that will be used to gather metrics from
|
||||
// and also to carry out path finding queries.
|
||||
Graph *channeldb.ChannelGraph
|
||||
// GraphSessionFactory can be used to gain access to a Graph session.
|
||||
// If the backing DB allows it, this will mean that a read transaction
|
||||
// is being held during the use of the session.
|
||||
GraphSessionFactory GraphSessionFactory
|
||||
|
||||
// SourceNode is the graph's source node.
|
||||
SourceNode *channeldb.LightningNode
|
||||
|
@ -44,21 +45,6 @@ type SessionSource struct {
|
|||
PathFindingConfig PathFindingConfig
|
||||
}
|
||||
|
||||
// getRoutingGraph returns a routing graph and a clean-up function for
|
||||
// pathfinding.
|
||||
func (m *SessionSource) getRoutingGraph() (routingGraph, func(), error) {
|
||||
routingTx, err := NewCachedGraph(m.SourceNode, m.Graph)
|
||||
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
|
||||
}
|
||||
|
||||
// NewPaymentSession creates a new payment session backed by the latest prune
|
||||
// view from Mission Control. An optional set of routing hints can be provided
|
||||
// in order to populate additional edges to explore when finding a path to the
|
||||
|
@ -66,15 +52,15 @@ func (m *SessionSource) getRoutingGraph() (routingGraph, func(), error) {
|
|||
func (m *SessionSource) NewPaymentSession(p *LightningPayment) (
|
||||
PaymentSession, error) {
|
||||
|
||||
getBandwidthHints := func(graph routingGraph) (bandwidthHints, error) {
|
||||
getBandwidthHints := func(graph Graph) (bandwidthHints, error) {
|
||||
return newBandwidthManager(
|
||||
graph, m.SourceNode.PubKeyBytes, m.GetLink,
|
||||
)
|
||||
}
|
||||
|
||||
session, err := newPaymentSession(
|
||||
p, getBandwidthHints, m.getRoutingGraph,
|
||||
m.MissionControl, m.PathFindingConfig,
|
||||
p, m.SourceNode.PubKeyBytes, getBandwidthHints,
|
||||
m.GraphSessionFactory, m.MissionControl, m.PathFindingConfig,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -115,13 +115,11 @@ func TestUpdateAdditionalEdge(t *testing.T) {
|
|||
|
||||
// Create the paymentsession.
|
||||
session, err := newPaymentSession(
|
||||
payment,
|
||||
func(routingGraph) (bandwidthHints, error) {
|
||||
payment, route.Vertex{},
|
||||
func(Graph) (bandwidthHints, error) {
|
||||
return &mockBandwidthHints{}, nil
|
||||
},
|
||||
func() (routingGraph, func(), error) {
|
||||
return &sessionGraph{}, func() {}, nil
|
||||
},
|
||||
newMockGraphSessionFactory(&sessionGraph{}),
|
||||
&MissionControl{},
|
||||
PathFindingConfig{},
|
||||
)
|
||||
|
@ -195,13 +193,11 @@ func TestRequestRoute(t *testing.T) {
|
|||
}
|
||||
|
||||
session, err := newPaymentSession(
|
||||
payment,
|
||||
func(routingGraph) (bandwidthHints, error) {
|
||||
payment, route.Vertex{},
|
||||
func(Graph) (bandwidthHints, error) {
|
||||
return &mockBandwidthHints{}, nil
|
||||
},
|
||||
func() (routingGraph, func(), error) {
|
||||
return &sessionGraph{}, func() {}, nil
|
||||
},
|
||||
newMockGraphSessionFactory(&sessionGraph{}),
|
||||
&MissionControl{},
|
||||
PathFindingConfig{},
|
||||
)
|
||||
|
@ -211,9 +207,9 @@ func TestRequestRoute(t *testing.T) {
|
|||
|
||||
// Override pathfinder with a mock.
|
||||
session.pathFinder = func(_ *graphParams, r *RestrictParams,
|
||||
_ *PathFindingConfig, _, _ route.Vertex, _ lnwire.MilliSatoshi,
|
||||
_ float64, _ int32) ([]*unifiedEdge, float64,
|
||||
error) {
|
||||
_ *PathFindingConfig, _, _, _ route.Vertex,
|
||||
_ lnwire.MilliSatoshi, _ float64, _ int32) ([]*unifiedEdge,
|
||||
float64, error) {
|
||||
|
||||
// We expect find path to receive a cltv limit excluding the
|
||||
// final cltv delta (including the block padding).
|
||||
|
@ -253,7 +249,7 @@ func TestRequestRoute(t *testing.T) {
|
|||
}
|
||||
|
||||
type sessionGraph struct {
|
||||
routingGraph
|
||||
Graph
|
||||
}
|
||||
|
||||
func (g *sessionGraph) sourceNode() route.Vertex {
|
||||
|
|
1867
routing/router.go
1867
routing/router.go
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -94,7 +94,7 @@ func (u *nodeEdgeUnifier) addPolicy(fromNode route.Vertex,
|
|||
|
||||
// addGraphPolicies adds all policies that are known for the toNode in the
|
||||
// graph.
|
||||
func (u *nodeEdgeUnifier) addGraphPolicies(g routingGraph) error {
|
||||
func (u *nodeEdgeUnifier) addGraphPolicies(g Graph) 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
|
||||
|
@ -120,7 +120,7 @@ func (u *nodeEdgeUnifier) addGraphPolicies(g routingGraph) error {
|
|||
}
|
||||
|
||||
// Iterate over all channels of the to node.
|
||||
return g.forEachNodeChannel(u.toNode, cb)
|
||||
return g.ForEachNodeChannel(u.toNode, cb)
|
||||
}
|
||||
|
||||
// unifiedEdge is the individual channel data that is kept inside an edgeUnifier
|
||||
|
|
45
rpcserver.go
45
rpcserver.go
|
@ -41,6 +41,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/chanbackup"
|
||||
"github.com/lightningnetwork/lnd/chanfitness"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/graphsession"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/channelnotifier"
|
||||
"github.com/lightningnetwork/lnd/contractcourt"
|
||||
|
@ -48,6 +49,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/feature"
|
||||
"github.com/lightningnetwork/lnd/fn"
|
||||
"github.com/lightningnetwork/lnd/funding"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
|
@ -691,23 +693,9 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
|
|||
FetchAmountPairCapacity: func(nodeFrom, nodeTo route.Vertex,
|
||||
amount lnwire.MilliSatoshi) (btcutil.Amount, error) {
|
||||
|
||||
routingGraph, err := routing.NewCachedGraph(
|
||||
selfNode, graph,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
closeErr := routingGraph.Close()
|
||||
if closeErr != nil {
|
||||
rpcsLog.Errorf("not able to close "+
|
||||
"routing graph tx: %v",
|
||||
closeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
return routingGraph.FetchAmountPairCapacity(
|
||||
nodeFrom, nodeTo, amount,
|
||||
return routing.FetchAmountPairCapacity(
|
||||
graphsession.NewRoutingGraph(graph),
|
||||
selfNode.PubKeyBytes, nodeFrom, nodeTo, amount,
|
||||
)
|
||||
},
|
||||
FetchChannelEndpoints: func(chanID uint64) (route.Vertex,
|
||||
|
@ -3088,7 +3076,7 @@ func (r *rpcServer) GetInfo(_ context.Context,
|
|||
// date, we add the router's state to it. So the flag will only toggle
|
||||
// to true once the router was also able to catch up.
|
||||
if !r.cfg.Routing.AssumeChannelValid {
|
||||
routerHeight := r.server.chanRouter.SyncedHeight()
|
||||
routerHeight := r.server.graphBuilder.SyncedHeight()
|
||||
isSynced = isSynced && uint32(bestHeight) == routerHeight
|
||||
}
|
||||
|
||||
|
@ -3131,7 +3119,7 @@ func (r *rpcServer) GetInfo(_ context.Context,
|
|||
// TODO(roasbeef): add synced height n stuff
|
||||
|
||||
isTestNet := chainreg.IsTestnet(&r.cfg.ActiveNetParams)
|
||||
nodeColor := routing.EncodeHexColor(nodeAnn.RGBColor)
|
||||
nodeColor := graph.EncodeHexColor(nodeAnn.RGBColor)
|
||||
version := build.Version() + " commit=" + build.Commit
|
||||
|
||||
return &lnrpc.GetInfoResponse{
|
||||
|
@ -6358,7 +6346,7 @@ func (r *rpcServer) GetNodeInfo(ctx context.Context,
|
|||
// With the public key decoded, attempt to fetch the node corresponding
|
||||
// to this public key. If the node cannot be found, then an error will
|
||||
// be returned.
|
||||
node, err := graph.FetchLightningNode(nil, pubKey)
|
||||
node, err := graph.FetchLightningNode(pubKey)
|
||||
switch {
|
||||
case err == channeldb.ErrGraphNodeNotFound:
|
||||
return nil, status.Error(codes.NotFound, err.Error())
|
||||
|
@ -6374,7 +6362,7 @@ func (r *rpcServer) GetNodeInfo(ctx context.Context,
|
|||
channels []*lnrpc.ChannelEdge
|
||||
)
|
||||
|
||||
err = graph.ForEachNodeChannel(nil, node.PubKeyBytes,
|
||||
err = graph.ForEachNodeChannel(node.PubKeyBytes,
|
||||
func(_ kvdb.RTx, edge *models.ChannelEdgeInfo,
|
||||
c1, c2 *models.ChannelEdgePolicy) error {
|
||||
|
||||
|
@ -6431,7 +6419,7 @@ func marshalNode(node *channeldb.LightningNode) *lnrpc.LightningNode {
|
|||
PubKey: hex.EncodeToString(node.PubKeyBytes[:]),
|
||||
Addresses: nodeAddrs,
|
||||
Alias: node.Alias,
|
||||
Color: routing.EncodeHexColor(node.Color),
|
||||
Color: graph.EncodeHexColor(node.Color),
|
||||
Features: features,
|
||||
CustomRecords: customRecords,
|
||||
}
|
||||
|
@ -6626,7 +6614,7 @@ func (r *rpcServer) SubscribeChannelGraph(req *lnrpc.GraphTopologySubscription,
|
|||
|
||||
// First, we start by subscribing to a new intent to receive
|
||||
// notifications from the channel router.
|
||||
client, err := r.server.chanRouter.SubscribeTopology()
|
||||
client, err := r.server.graphBuilder.SubscribeTopology()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -6678,7 +6666,8 @@ func (r *rpcServer) SubscribeChannelGraph(req *lnrpc.GraphTopologySubscription,
|
|||
// marshallTopologyChange performs a mapping from the topology change struct
|
||||
// returned by the router to the form of notifications expected by the current
|
||||
// gRPC service.
|
||||
func marshallTopologyChange(topChange *routing.TopologyChange) *lnrpc.GraphTopologyUpdate {
|
||||
func marshallTopologyChange(
|
||||
topChange *graph.TopologyChange) *lnrpc.GraphTopologyUpdate {
|
||||
|
||||
// encodeKey is a simple helper function that converts a live public
|
||||
// key into a hex-encoded version of the compressed serialization for
|
||||
|
@ -6689,7 +6678,9 @@ func marshallTopologyChange(topChange *routing.TopologyChange) *lnrpc.GraphTopol
|
|||
|
||||
nodeUpdates := make([]*lnrpc.NodeUpdate, len(topChange.NodeUpdates))
|
||||
for i, nodeUpdate := range topChange.NodeUpdates {
|
||||
nodeAddrs := make([]*lnrpc.NodeAddress, 0, len(nodeUpdate.Addresses))
|
||||
nodeAddrs := make(
|
||||
[]*lnrpc.NodeAddress, 0, len(nodeUpdate.Addresses),
|
||||
)
|
||||
for _, addr := range nodeUpdate.Addresses {
|
||||
nodeAddr := &lnrpc.NodeAddress{
|
||||
Network: addr.Network(),
|
||||
|
@ -7027,7 +7018,7 @@ func (r *rpcServer) FeeReport(ctx context.Context,
|
|||
}
|
||||
|
||||
var feeReports []*lnrpc.ChannelFeeReport
|
||||
err = channelGraph.ForEachNodeChannel(nil, selfNode.PubKeyBytes,
|
||||
err = channelGraph.ForEachNodeChannel(selfNode.PubKeyBytes,
|
||||
func(_ kvdb.RTx, chanInfo *models.ChannelEdgeInfo,
|
||||
edgePolicy, _ *models.ChannelEdgePolicy) error {
|
||||
|
||||
|
@ -7406,7 +7397,7 @@ func (r *rpcServer) ForwardingHistory(ctx context.Context,
|
|||
return "", err
|
||||
}
|
||||
|
||||
peer, err := r.server.graphDB.FetchLightningNode(nil, vertex)
|
||||
peer, err := r.server.graphDB.FetchLightningNode(vertex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
67
server.go
67
server.go
|
@ -32,6 +32,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/chanbackup"
|
||||
"github.com/lightningnetwork/lnd/chanfitness"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/graphsession"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/channelnotifier"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
|
@ -40,6 +41,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/feature"
|
||||
"github.com/lightningnetwork/lnd/fn"
|
||||
"github.com/lightningnetwork/lnd/funding"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/healthcheck"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||
|
@ -270,6 +272,8 @@ type server struct {
|
|||
|
||||
missionControl *routing.MissionControl
|
||||
|
||||
graphBuilder *graph.Builder
|
||||
|
||||
chanRouter *routing.ChannelRouter
|
||||
|
||||
controlTower routing.ControlTower
|
||||
|
@ -338,7 +342,7 @@ type server struct {
|
|||
// updatePersistentPeerAddrs subscribes to topology changes and stores
|
||||
// advertised addresses for any NodeAnnouncements from our persisted peers.
|
||||
func (s *server) updatePersistentPeerAddrs() error {
|
||||
graphSub, err := s.chanRouter.SubscribeTopology()
|
||||
graphSub, err := s.graphBuilder.SubscribeTopology()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -956,7 +960,9 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||
return nil, fmt.Errorf("error getting source node: %w", err)
|
||||
}
|
||||
paymentSessionSource := &routing.SessionSource{
|
||||
Graph: chanGraph,
|
||||
GraphSessionFactory: graphsession.NewGraphSessionFactory(
|
||||
chanGraph,
|
||||
),
|
||||
SourceNode: sourceNode,
|
||||
MissionControl: s.missionControl,
|
||||
GetLink: s.htlcSwitch.GetLinkByShortID,
|
||||
|
@ -967,28 +973,40 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||
|
||||
s.controlTower = routing.NewControlTower(paymentControl)
|
||||
|
||||
strictPruning := (cfg.Bitcoin.Node == "neutrino" ||
|
||||
cfg.Routing.StrictZombiePruning)
|
||||
s.chanRouter, err = routing.New(routing.Config{
|
||||
strictPruning := cfg.Bitcoin.Node == "neutrino" ||
|
||||
cfg.Routing.StrictZombiePruning
|
||||
|
||||
s.graphBuilder, err = graph.NewBuilder(&graph.Config{
|
||||
SelfNode: selfNode.PubKeyBytes,
|
||||
Graph: chanGraph,
|
||||
Chain: cc.ChainIO,
|
||||
ChainView: cc.ChainView,
|
||||
Notifier: cc.ChainNotifier,
|
||||
Payer: s.htlcSwitch,
|
||||
Control: s.controlTower,
|
||||
MissionControl: s.missionControl,
|
||||
SessionSource: paymentSessionSource,
|
||||
ChannelPruneExpiry: routing.DefaultChannelPruneExpiry,
|
||||
ChannelPruneExpiry: graph.DefaultChannelPruneExpiry,
|
||||
GraphPruneInterval: time.Hour,
|
||||
FirstTimePruneDelay: routing.DefaultFirstTimePruneDelay,
|
||||
GetLink: s.htlcSwitch.GetLinkByShortID,
|
||||
FirstTimePruneDelay: graph.DefaultFirstTimePruneDelay,
|
||||
AssumeChannelValid: cfg.Routing.AssumeChannelValid,
|
||||
NextPaymentID: sequencer.NextID,
|
||||
PathFindingConfig: pathFindingConfig,
|
||||
Clock: clock.NewDefaultClock(),
|
||||
StrictZombiePruning: strictPruning,
|
||||
IsAlias: aliasmgr.IsAlias,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create graph builder: %w", err)
|
||||
}
|
||||
|
||||
s.chanRouter, err = routing.New(routing.Config{
|
||||
SelfNode: selfNode.PubKeyBytes,
|
||||
RoutingGraph: graphsession.NewRoutingGraph(chanGraph),
|
||||
Chain: cc.ChainIO,
|
||||
Payer: s.htlcSwitch,
|
||||
Control: s.controlTower,
|
||||
MissionControl: s.missionControl,
|
||||
SessionSource: paymentSessionSource,
|
||||
GetLink: s.htlcSwitch.GetLinkByShortID,
|
||||
NextPaymentID: sequencer.NextID,
|
||||
PathFindingConfig: pathFindingConfig,
|
||||
Clock: clock.NewDefaultClock(),
|
||||
ApplyChannelUpdate: s.graphBuilder.ApplyChannelUpdate,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create router: %w", err)
|
||||
}
|
||||
|
@ -1004,7 +1022,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||
}
|
||||
|
||||
s.authGossiper = discovery.New(discovery.Config{
|
||||
Router: s.chanRouter,
|
||||
Graph: s.graphBuilder,
|
||||
Notifier: s.cc.ChainNotifier,
|
||||
ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
|
||||
Broadcast: s.BroadcastMessage,
|
||||
|
@ -1039,11 +1057,12 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||
FindBaseByAlias: s.aliasMgr.FindBaseSCID,
|
||||
GetAlias: s.aliasMgr.GetPeerAlias,
|
||||
FindChannel: s.findChannel,
|
||||
IsStillZombieChannel: s.chanRouter.IsZombieChannel,
|
||||
IsStillZombieChannel: s.graphBuilder.IsZombieChannel,
|
||||
}, nodeKeyDesc)
|
||||
|
||||
//nolint:lll
|
||||
s.localChanMgr = &localchans.Manager{
|
||||
ForAllOutgoingChannels: s.chanRouter.ForAllOutgoingChannels,
|
||||
ForAllOutgoingChannels: s.graphBuilder.ForAllOutgoingChannels,
|
||||
PropagateChanPolicyUpdate: s.authGossiper.PropagateChanPolicyUpdate,
|
||||
UpdateForwardingPolicies: s.htlcSwitch.UpdateForwardingPolicies,
|
||||
FetchChannel: s.chanStateDB.FetchChannel,
|
||||
|
@ -2013,6 +2032,12 @@ func (s *server) Start() error {
|
|||
}
|
||||
cleanup = cleanup.add(s.authGossiper.Stop)
|
||||
|
||||
if err := s.graphBuilder.Start(); err != nil {
|
||||
startErr = err
|
||||
return
|
||||
}
|
||||
cleanup = cleanup.add(s.graphBuilder.Stop)
|
||||
|
||||
if err := s.chanRouter.Start(); err != nil {
|
||||
startErr = err
|
||||
return
|
||||
|
@ -3113,7 +3138,7 @@ func (s *server) establishPersistentConnections() error {
|
|||
// TODO(roasbeef): instead iterate over link nodes and query graph for
|
||||
// each of the nodes.
|
||||
selfPub := s.identityECDH.PubKey().SerializeCompressed()
|
||||
err = s.graphDB.ForEachNodeChannel(nil, sourceNode.PubKeyBytes, func(
|
||||
err = s.graphDB.ForEachNodeChannel(sourceNode.PubKeyBytes, func(
|
||||
tx kvdb.RTx,
|
||||
chanInfo *models.ChannelEdgeInfo,
|
||||
policy, _ *models.ChannelEdgePolicy) error {
|
||||
|
@ -4628,7 +4653,7 @@ func (s *server) fetchNodeAdvertisedAddrs(pub *btcec.PublicKey) ([]net.Addr, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
node, err := s.graphDB.FetchLightningNode(nil, vertex)
|
||||
node, err := s.graphDB.FetchLightningNode(vertex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -4647,7 +4672,7 @@ func (s *server) fetchLastChanUpdate() func(lnwire.ShortChannelID) (
|
|||
|
||||
ourPubKey := s.identityECDH.PubKey().SerializeCompressed()
|
||||
return func(cid lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) {
|
||||
info, edge1, edge2, err := s.chanRouter.GetChannelByID(cid)
|
||||
info, edge1, edge2, err := s.graphBuilder.GetChannelByID(cid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue