lnd/autopilot/graph.go
Oli 8047149c6a
multi: upgrade to btcd v2 modules
Migrate all btcd dependencies to the new per-package v2 modules (wire/v2,
txscript/v2, chaincfg/v2, chainhash/v2, btcutil/v2, psbt/v2, btcec/v2)
introduced by btcd v0.26.0, and pin the tagged ecosystem versions:
btcwallet v0.17.0, neutrino v0.18.0 and lightning-onion v1.4.0.

The bulk of the import rewrite was produced by the scripted diff from
https://github.com/btcsuite/btcd/pull/2547 (followed by 'make rpc'). The
address symbols that moved out of btcutil into the new address package
are imported as btcaddr where a local "address" variable would otherwise
shadow them. The go.mod/go.sum updates and the remaining manual
compilation fixes are folded into this single commit so it builds on its
own (the migration was previously split into a reproducible scripted-diff
plus follow-ups, intended to be squashed on merge).
2026-06-24 10:58:36 -07:00

292 lines
8.3 KiB
Go

package autopilot
import (
"context"
"encoding/hex"
"net"
"sort"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcutil/v2"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
var (
testRBytes, _ = hex.DecodeString("8ce2bc69281ce27da07e6683571319d18e949ddfa2965fb6caa1bf0314f882d7")
testSBytes, _ = hex.DecodeString("299105481d63e0f4bc2a88121167221b6700d72a0ead154c03be696a292d24ae")
testRScalar = new(btcec.ModNScalar)
testSScalar = new(btcec.ModNScalar)
_ = testRScalar.SetByteSlice(testRBytes)
_ = testSScalar.SetByteSlice(testSBytes)
testSig = ecdsa.NewSignature(testRScalar, testSScalar)
chanIDCounter uint64 // To be used atomically.
)
// databaseChannelGraph wraps a channeldb.ChannelGraph instance with the
// necessary API to properly implement the autopilot.ChannelGraph interface.
//
// TODO(roasbeef): move inmpl to main package?
type databaseChannelGraph struct {
db GraphSource
}
// A compile time assertion to ensure databaseChannelGraph meets the
// autopilot.ChannelGraph interface.
var _ ChannelGraph = (*databaseChannelGraph)(nil)
// ChannelGraphFromDatabase returns an instance of the autopilot.ChannelGraph
// backed by a GraphSource.
func ChannelGraphFromDatabase(db GraphSource) ChannelGraph {
return &databaseChannelGraph{
db: db,
}
}
// type dbNode is a wrapper struct around a database transaction an
// channeldb.Node. The wrapper method implement the autopilot.Node
// interface.
type dbNode struct {
pub [33]byte
addrs []net.Addr
}
// A compile time assertion to ensure dbNode meets the autopilot.Node
// interface.
var _ Node = (*dbNode)(nil)
// PubKey is the identity public key of the node. This will be used to attempt
// to target a node for channel opening by the main autopilot agent. The key
// will be returned in serialized compressed format.
//
// NOTE: Part of the autopilot.Node interface.
func (d *dbNode) PubKey() [33]byte {
return d.pub
}
// Addrs returns a slice of publicly reachable public TCP addresses that the
// peer is known to be listening on.
//
// NOTE: Part of the autopilot.Node interface.
func (d *dbNode) Addrs() []net.Addr {
return d.addrs
}
// ForEachNode is a higher-order function that should be called once for each
// connected node within the channel graph. If the passed callback returns an
// error, then execution should be terminated.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (d *databaseChannelGraph) ForEachNode(ctx context.Context,
cb func(context.Context, Node) error, reset func()) error {
return d.db.ForEachNode(ctx, func(n *models.Node) error {
// We'll skip over any node that doesn't have any advertised
// addresses. As we won't be able to reach them to actually
// open any channels.
if len(n.Addresses) == 0 {
return nil
}
node := &dbNode{
pub: n.PubKeyBytes,
addrs: n.Addresses,
}
return cb(ctx, node)
}, reset)
}
// ForEachNodesChannels iterates through all connected nodes, and for each
// node, all the channels that connect to it. The passed callback will be
// called with the context, the node's pubkey, and a slice of ChannelEdge
// that connect to the node.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (d *databaseChannelGraph) ForEachNodesChannels(ctx context.Context,
cb func(context.Context, NodeID, []*ChannelEdge) error,
reset func()) error {
// The channel-scoring callers only need topology data here. Address
// filtering happens through ForEachNode before connecting to peers.
return d.db.ForEachNodeCached(
ctx, func(ctx context.Context, node route.Vertex,
chans map[uint64]*graphdb.DirectedChannel) error {
if len(chans) == 0 {
return nil
}
edges := make([]*ChannelEdge, 0, len(chans))
for _, channel := range chans {
edges = append(edges, &ChannelEdge{
ChanID: lnwire.NewShortChanIDFromInt(
channel.ChannelID,
),
Capacity: channel.Capacity,
Peer: channel.OtherNode,
})
}
return cb(ctx, NodeID(node), edges)
}, reset,
)
}
// databaseChannelGraphCached wraps a channeldb.ChannelGraph instance with the
// necessary API to properly implement the autopilot.ChannelGraph interface.
type databaseChannelGraphCached struct {
db GraphSource
}
// A compile time assertion to ensure databaseChannelGraphCached meets the
// autopilot.ChannelGraph interface.
var _ ChannelGraph = (*databaseChannelGraphCached)(nil)
// ChannelGraphFromCachedDatabase returns an instance of the
// autopilot.ChannelGraph backed by a live, open channeldb instance.
func ChannelGraphFromCachedDatabase(db GraphSource) ChannelGraph {
return &databaseChannelGraphCached{
db: db,
}
}
// dbNodeCached is a wrapper struct around a database transaction for a
// channeldb.Node. The wrapper methods implement the autopilot.Node
// interface.
type dbNodeCached struct {
node route.Vertex
channels map[uint64]*graphdb.DirectedChannel
}
// A compile time assertion to ensure dbNodeCached meets the autopilot.Node
// interface.
var _ Node = (*dbNodeCached)(nil)
// PubKey is the identity public key of the node.
//
// NOTE: Part of the autopilot.Node interface.
func (nc dbNodeCached) PubKey() [33]byte {
return nc.node
}
// Addrs returns a slice of publicly reachable public TCP addresses that the
// peer is known to be listening on.
//
// NOTE: Part of the autopilot.Node interface.
func (nc dbNodeCached) Addrs() []net.Addr {
// TODO: Add addresses to be usable by autopilot.
return []net.Addr{}
}
// ForEachNode is a higher-order function that should be called once for each
// connected node within the channel graph. If the passed callback returns an
// error, then execution should be terminated.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (dc *databaseChannelGraphCached) ForEachNode(ctx context.Context,
cb func(context.Context, Node) error, reset func()) error {
return dc.db.ForEachNodeCached(ctx, func(ctx context.Context,
n route.Vertex,
channels map[uint64]*graphdb.DirectedChannel) error {
if len(channels) > 0 {
node := dbNodeCached{
node: n,
channels: channels,
}
return cb(ctx, node)
}
return nil
}, reset)
}
// ForEachNodesChannels iterates through all connected nodes, and for each
// node, all the channels that connect to it. The passed callback will be
// called with the context, the node's pubkey, and a slice of ChannelEdge
// that connect to the node.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (dc *databaseChannelGraphCached) ForEachNodesChannels(ctx context.Context,
cb func(context.Context, NodeID, []*ChannelEdge) error,
reset func()) error {
return dc.db.ForEachNodeCached(ctx, func(ctx context.Context,
n route.Vertex,
channels map[uint64]*graphdb.DirectedChannel) error {
if len(channels) == 0 {
return nil
}
edges := make([]*ChannelEdge, 0, len(channels))
for cid, channel := range channels {
edges = append(edges, &ChannelEdge{
ChanID: lnwire.NewShortChanIDFromInt(cid),
Capacity: channel.Capacity,
Peer: channel.OtherNode,
})
}
return cb(ctx, NodeID(n), edges)
}, reset)
}
// memNode is a purely in-memory implementation of the autopilot.Node
// interface.
type memNode struct {
pub *btcec.PublicKey
chans []ChannelEdge
addrs []net.Addr
}
// A compile time assertion to ensure memNode meets the autopilot.Node
// interface.
var _ Node = (*memNode)(nil)
// PubKey is the identity public key of the node. This will be used to attempt
// to target a node for channel opening by the main autopilot agent.
//
// NOTE: Part of the autopilot.Node interface.
func (m memNode) PubKey() [33]byte {
var n [33]byte
copy(n[:], m.pub.SerializeCompressed())
return n
}
// Addrs returns a slice of publicly reachable public TCP addresses that the
// peer is known to be listening on.
//
// NOTE: Part of the autopilot.Node interface.
func (m memNode) Addrs() []net.Addr {
return m.addrs
}
// Median returns the median value in the slice of Amounts.
func Median(vals []btcutil.Amount) btcutil.Amount {
sort.Slice(vals, func(i, j int) bool {
return vals[i] < vals[j]
})
num := len(vals)
switch {
case num == 0:
return 0
case num%2 == 0:
return (vals[num/2-1] + vals[num/2]) / 2
default:
return vals[num/2]
}
}