mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-29 07:00:55 +02:00
305 lines
8.5 KiB
Go
305 lines
8.5 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"
|
|
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.LightningNode. 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.LightningNode) 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 itself, 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, Node, []*ChannelEdge) error,
|
|
reset func()) error {
|
|
|
|
return d.db.ForEachNodeCached(
|
|
ctx, true, func(ctx context.Context, node route.Vertex,
|
|
addrs []net.Addr,
|
|
chans map[uint64]*graphdb.DirectedChannel) 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(addrs) == 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, &dbNode{
|
|
pub: node,
|
|
addrs: addrs,
|
|
}, 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.LightningNode. 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, false, func(ctx context.Context,
|
|
n route.Vertex, _ []net.Addr,
|
|
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 itself, 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, Node, []*ChannelEdge) error,
|
|
reset func()) error {
|
|
|
|
return dc.db.ForEachNodeCached(ctx, false, func(ctx context.Context,
|
|
n route.Vertex, _ []net.Addr,
|
|
channels map[uint64]*graphdb.DirectedChannel) error {
|
|
|
|
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,
|
|
})
|
|
}
|
|
|
|
if len(channels) > 0 {
|
|
node := dbNodeCached{
|
|
node: n,
|
|
channels: channels,
|
|
}
|
|
|
|
if err := cb(ctx, node, edges); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}, 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]
|
|
}
|
|
}
|