mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
e9c89ae0ec
In prep for a clean Graph DB interface, we add a version of ForEachNodeChannel that does not take in an existing db transaction.
635 lines
16 KiB
Go
635 lines
16 KiB
Go
package autopilot
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"net"
|
|
"sort"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"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"
|
|
)
|
|
|
|
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 *channeldb.ChannelGraph
|
|
}
|
|
|
|
// 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 live, open channeldb instance.
|
|
func ChannelGraphFromDatabase(db *channeldb.ChannelGraph) 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 {
|
|
db *channeldb.ChannelGraph
|
|
|
|
tx kvdb.RTx
|
|
|
|
node *channeldb.LightningNode
|
|
}
|
|
|
|
// 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.node.PubKeyBytes
|
|
}
|
|
|
|
// 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.node.Addresses
|
|
}
|
|
|
|
// ForEachChannel is a higher-order function that will be used to iterate
|
|
// through all edges emanating from/to the target node. For each active
|
|
// channel, this function should be called with the populated ChannelEdge that
|
|
// describes the active channel.
|
|
//
|
|
// NOTE: Part of the autopilot.Node interface.
|
|
func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
|
|
return d.db.ForEachNodeChannelTx(d.tx, d.node.PubKeyBytes,
|
|
func(tx kvdb.RTx, ei *models.ChannelEdgeInfo, ep,
|
|
_ *models.ChannelEdgePolicy) error {
|
|
|
|
// Skip channels for which no outgoing edge policy is
|
|
// available.
|
|
//
|
|
// TODO(joostjager): Ideally the case where channels
|
|
// have a nil policy should be supported, as autopilot
|
|
// is not looking at the policies. For now, it is not
|
|
// easily possible to get a reference to the other end
|
|
// LightningNode object without retrieving the policy.
|
|
if ep == nil {
|
|
return nil
|
|
}
|
|
|
|
node, err := d.db.FetchLightningNodeTx(
|
|
tx, ep.ToNode,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
edge := ChannelEdge{
|
|
ChanID: lnwire.NewShortChanIDFromInt(
|
|
ep.ChannelID,
|
|
),
|
|
Capacity: ei.Capacity,
|
|
Peer: &dbNode{
|
|
tx: tx,
|
|
db: d.db,
|
|
node: node,
|
|
},
|
|
}
|
|
|
|
return cb(edge)
|
|
})
|
|
}
|
|
|
|
// 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(cb func(Node) error) error {
|
|
return d.db.ForEachNode(func(tx kvdb.RTx, n *channeldb.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{
|
|
db: d.db,
|
|
tx: tx,
|
|
node: n,
|
|
}
|
|
return cb(node)
|
|
})
|
|
}
|
|
|
|
// addRandChannel creates a new channel two target nodes. This function is
|
|
// meant to aide in the generation of random graphs for use within test cases
|
|
// the exercise the autopilot package.
|
|
func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
|
|
capacity btcutil.Amount) (*ChannelEdge, *ChannelEdge, error) {
|
|
|
|
fetchNode := func(pub *btcec.PublicKey) (*channeldb.LightningNode, error) {
|
|
if pub != nil {
|
|
vertex, err := route.NewVertexFromBytes(
|
|
pub.SerializeCompressed(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dbNode, err := d.db.FetchLightningNode(vertex)
|
|
switch {
|
|
case err == channeldb.ErrGraphNodeNotFound:
|
|
fallthrough
|
|
case err == channeldb.ErrGraphNotFound:
|
|
graphNode := &channeldb.LightningNode{
|
|
HaveNodeAnnouncement: true,
|
|
Addresses: []net.Addr{
|
|
&net.TCPAddr{
|
|
IP: bytes.Repeat([]byte("a"), 16),
|
|
},
|
|
},
|
|
Features: lnwire.NewFeatureVector(
|
|
nil, lnwire.Features,
|
|
),
|
|
AuthSigBytes: testSig.Serialize(),
|
|
}
|
|
graphNode.AddPubKey(pub)
|
|
if err := d.db.AddLightningNode(graphNode); err != nil {
|
|
return nil, err
|
|
}
|
|
case err != nil:
|
|
return nil, err
|
|
}
|
|
|
|
return dbNode, nil
|
|
}
|
|
|
|
nodeKey, err := randKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dbNode := &channeldb.LightningNode{
|
|
HaveNodeAnnouncement: true,
|
|
Addresses: []net.Addr{
|
|
&net.TCPAddr{
|
|
IP: bytes.Repeat([]byte("a"), 16),
|
|
},
|
|
},
|
|
Features: lnwire.NewFeatureVector(
|
|
nil, lnwire.Features,
|
|
),
|
|
AuthSigBytes: testSig.Serialize(),
|
|
}
|
|
dbNode.AddPubKey(nodeKey)
|
|
if err := d.db.AddLightningNode(dbNode); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return dbNode, nil
|
|
}
|
|
|
|
vertex1, err := fetchNode(node1)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
vertex2, err := fetchNode(node2)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
var lnNode1, lnNode2 *btcec.PublicKey
|
|
if bytes.Compare(vertex1.PubKeyBytes[:], vertex2.PubKeyBytes[:]) == -1 {
|
|
lnNode1, _ = vertex1.PubKey()
|
|
lnNode2, _ = vertex2.PubKey()
|
|
} else {
|
|
lnNode1, _ = vertex2.PubKey()
|
|
lnNode2, _ = vertex1.PubKey()
|
|
}
|
|
|
|
chanID := randChanID()
|
|
edge := &models.ChannelEdgeInfo{
|
|
ChannelID: chanID.ToUint64(),
|
|
Capacity: capacity,
|
|
}
|
|
edge.AddNodeKeys(lnNode1, lnNode2, lnNode1, lnNode2)
|
|
if err := d.db.AddChannelEdge(edge); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
edgePolicy := &models.ChannelEdgePolicy{
|
|
SigBytes: testSig.Serialize(),
|
|
ChannelID: chanID.ToUint64(),
|
|
LastUpdate: time.Now(),
|
|
TimeLockDelta: 10,
|
|
MinHTLC: 1,
|
|
MaxHTLC: lnwire.NewMSatFromSatoshis(capacity),
|
|
FeeBaseMSat: 10,
|
|
FeeProportionalMillionths: 10000,
|
|
MessageFlags: 1,
|
|
ChannelFlags: 0,
|
|
}
|
|
|
|
if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
edgePolicy = &models.ChannelEdgePolicy{
|
|
SigBytes: testSig.Serialize(),
|
|
ChannelID: chanID.ToUint64(),
|
|
LastUpdate: time.Now(),
|
|
TimeLockDelta: 10,
|
|
MinHTLC: 1,
|
|
MaxHTLC: lnwire.NewMSatFromSatoshis(capacity),
|
|
FeeBaseMSat: 10,
|
|
FeeProportionalMillionths: 10000,
|
|
MessageFlags: 1,
|
|
ChannelFlags: 1,
|
|
}
|
|
if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &ChannelEdge{
|
|
ChanID: chanID,
|
|
Capacity: capacity,
|
|
Peer: &dbNode{
|
|
db: d.db,
|
|
node: vertex1,
|
|
},
|
|
},
|
|
&ChannelEdge{
|
|
ChanID: chanID,
|
|
Capacity: capacity,
|
|
Peer: &dbNode{
|
|
db: d.db,
|
|
node: vertex2,
|
|
},
|
|
},
|
|
nil
|
|
}
|
|
|
|
func (d *databaseChannelGraph) addRandNode() (*btcec.PublicKey, error) {
|
|
nodeKey, err := randKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dbNode := &channeldb.LightningNode{
|
|
HaveNodeAnnouncement: true,
|
|
Addresses: []net.Addr{
|
|
&net.TCPAddr{
|
|
IP: bytes.Repeat([]byte("a"), 16),
|
|
},
|
|
},
|
|
Features: lnwire.NewFeatureVector(
|
|
nil, lnwire.Features,
|
|
),
|
|
AuthSigBytes: testSig.Serialize(),
|
|
}
|
|
dbNode.AddPubKey(nodeKey)
|
|
if err := d.db.AddLightningNode(dbNode); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return nodeKey, nil
|
|
|
|
}
|
|
|
|
// memChannelGraph is an implementation of the autopilot.ChannelGraph backed by
|
|
// an in-memory graph.
|
|
type memChannelGraph struct {
|
|
graph map[NodeID]*memNode
|
|
}
|
|
|
|
// A compile time assertion to ensure memChannelGraph meets the
|
|
// autopilot.ChannelGraph interface.
|
|
var _ ChannelGraph = (*memChannelGraph)(nil)
|
|
|
|
// newMemChannelGraph creates a new blank in-memory channel graph
|
|
// implementation.
|
|
func newMemChannelGraph() *memChannelGraph {
|
|
return &memChannelGraph{
|
|
graph: make(map[NodeID]*memNode),
|
|
}
|
|
}
|
|
|
|
// 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 (m memChannelGraph) ForEachNode(cb func(Node) error) error {
|
|
for _, node := range m.graph {
|
|
if err := cb(node); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// randChanID generates a new random channel ID.
|
|
func randChanID() lnwire.ShortChannelID {
|
|
id := atomic.AddUint64(&chanIDCounter, 1)
|
|
return lnwire.NewShortChanIDFromInt(id)
|
|
}
|
|
|
|
// randKey returns a random public key.
|
|
func randKey() (*btcec.PublicKey, error) {
|
|
priv, err := btcec.NewPrivateKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return priv.PubKey(), nil
|
|
}
|
|
|
|
// addRandChannel creates a new channel two target nodes. This function is
|
|
// meant to aide in the generation of random graphs for use within test cases
|
|
// the exercise the autopilot package.
|
|
func (m *memChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
|
|
capacity btcutil.Amount) (*ChannelEdge, *ChannelEdge, error) {
|
|
|
|
var (
|
|
vertex1, vertex2 *memNode
|
|
ok bool
|
|
)
|
|
|
|
if node1 != nil {
|
|
vertex1, ok = m.graph[NewNodeID(node1)]
|
|
if !ok {
|
|
vertex1 = &memNode{
|
|
pub: node1,
|
|
addrs: []net.Addr{
|
|
&net.TCPAddr{
|
|
IP: bytes.Repeat([]byte("a"), 16),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
} else {
|
|
newPub, err := randKey()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
vertex1 = &memNode{
|
|
pub: newPub,
|
|
addrs: []net.Addr{
|
|
&net.TCPAddr{
|
|
IP: bytes.Repeat([]byte("a"), 16),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
if node2 != nil {
|
|
vertex2, ok = m.graph[NewNodeID(node2)]
|
|
if !ok {
|
|
vertex2 = &memNode{
|
|
pub: node2,
|
|
addrs: []net.Addr{
|
|
&net.TCPAddr{
|
|
IP: bytes.Repeat([]byte("a"), 16),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
} else {
|
|
newPub, err := randKey()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
vertex2 = &memNode{
|
|
pub: newPub,
|
|
addrs: []net.Addr{
|
|
&net.TCPAddr{
|
|
IP: bytes.Repeat([]byte("a"), 16),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
edge1 := ChannelEdge{
|
|
ChanID: randChanID(),
|
|
Capacity: capacity,
|
|
Peer: vertex2,
|
|
}
|
|
vertex1.chans = append(vertex1.chans, edge1)
|
|
|
|
edge2 := ChannelEdge{
|
|
ChanID: randChanID(),
|
|
Capacity: capacity,
|
|
Peer: vertex1,
|
|
}
|
|
vertex2.chans = append(vertex2.chans, edge2)
|
|
|
|
m.graph[NewNodeID(vertex1.pub)] = vertex1
|
|
m.graph[NewNodeID(vertex2.pub)] = vertex2
|
|
|
|
return &edge1, &edge2, nil
|
|
}
|
|
|
|
func (m *memChannelGraph) addRandNode() (*btcec.PublicKey, error) {
|
|
newPub, err := randKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
vertex := &memNode{
|
|
pub: newPub,
|
|
addrs: []net.Addr{
|
|
&net.TCPAddr{
|
|
IP: bytes.Repeat([]byte("a"), 16),
|
|
},
|
|
},
|
|
}
|
|
m.graph[NewNodeID(newPub)] = vertex
|
|
|
|
return newPub, nil
|
|
}
|
|
|
|
// databaseChannelGraphCached wraps a channeldb.ChannelGraph instance with the
|
|
// necessary API to properly implement the autopilot.ChannelGraph interface.
|
|
type databaseChannelGraphCached struct {
|
|
db *channeldb.ChannelGraph
|
|
}
|
|
|
|
// 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 *channeldb.ChannelGraph) 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]*channeldb.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{}
|
|
}
|
|
|
|
// ForEachChannel is a higher-order function that will be used to iterate
|
|
// through all edges emanating from/to the target node. For each active
|
|
// channel, this function should be called with the populated ChannelEdge that
|
|
// describes the active channel.
|
|
//
|
|
// NOTE: Part of the autopilot.Node interface.
|
|
func (nc dbNodeCached) ForEachChannel(cb func(ChannelEdge) error) error {
|
|
for cid, channel := range nc.channels {
|
|
edge := ChannelEdge{
|
|
ChanID: lnwire.NewShortChanIDFromInt(cid),
|
|
Capacity: channel.Capacity,
|
|
Peer: dbNodeCached{
|
|
node: channel.OtherNode,
|
|
},
|
|
}
|
|
|
|
if err := cb(edge); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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(cb func(Node) error) error {
|
|
return dc.db.ForEachNodeCached(func(n route.Vertex,
|
|
channels map[uint64]*channeldb.DirectedChannel) error {
|
|
|
|
if len(channels) > 0 {
|
|
node := dbNodeCached{
|
|
node: n,
|
|
channels: channels,
|
|
}
|
|
return cb(node)
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// ForEachChannel is a higher-order function that will be used to iterate
|
|
// through all edges emanating from/to the target node. For each active
|
|
// channel, this function should be called with the populated ChannelEdge that
|
|
// describes the active channel.
|
|
//
|
|
// NOTE: Part of the autopilot.Node interface.
|
|
func (m memNode) ForEachChannel(cb func(ChannelEdge) error) error {
|
|
for _, channel := range m.chans {
|
|
if err := cb(channel); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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]
|
|
}
|
|
}
|