mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
refactor: move various duties from ChannelRouter to graph.Builder
This commit is a large refactor that moves over various responsibilities from the ChannelRouter to the graph.Builder. These include all graph related tasks such as: - graph pruning - validation of new network updates & persisting new updates - notifying topology update clients of any changes. This is a large commit but: - many of the files are purely moved from `routing` to `graph` - the business logic put in the graph Builder is copied exactly as is from the ChannelRouter with one exception: - The ChannelRouter just needs to be able to call the Builder's `ApplyChannelUpdate` method. So this is now exported and provided to the ChannelRouter as a config option. - The trickiest part was just moving over the test code since quite a bit had to be duplicated.
This commit is contained in:
parent
0b7364f54b
commit
7f1be39d45
@ -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
|
||||
|
@ -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)
|
||||
|
@ -29,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"
|
||||
@ -1361,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 {
|
||||
@ -1486,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()
|
||||
@ -1502,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 "+
|
||||
@ -1861,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",
|
||||
@ -1910,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)
|
||||
}
|
||||
@ -2064,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)
|
||||
@ -2201,7 +2200,7 @@ 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)
|
||||
@ -2338,11 +2337,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)
|
||||
@ -2457,7 +2456,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)
|
||||
|
||||
@ -2538,7 +2537,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)
|
||||
@ -2862,7 +2861,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",
|
||||
@ -2947,10 +2946,10 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
|
||||
}
|
||||
|
||||
if err := d.cfg.Router.UpdateEdge(update, ops...); err != nil {
|
||||
if routing.IsError(
|
||||
err, routing.ErrOutdated,
|
||||
routing.ErrIgnored,
|
||||
routing.ErrVBarrierShuttingDown,
|
||||
if graph.IsError(
|
||||
err, graph.ErrOutdated,
|
||||
graph.ErrIgnored,
|
||||
graph.ErrVBarrierShuttingDown,
|
||||
) {
|
||||
|
||||
log.Debugf("Update edge for short_chan_id(%v) got: %v",
|
||||
@ -3268,7 +3267,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)
|
||||
|
||||
|
@ -33,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"
|
||||
@ -351,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 {
|
||||
@ -2258,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)
|
||||
|
@ -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"
|
||||
)
|
||||
|
||||
@ -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 "+
|
||||
@ -4354,10 +4354,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 "+
|
||||
"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 "+
|
||||
|
@ -1,4 +1,4 @@
|
||||
package routing
|
||||
package graph
|
||||
|
||||
import (
|
||||
"bytes"
|
1757
graph/builder.go
1757
graph/builder.go
File diff suppressed because it is too large
Load Diff
2051
graph/builder_test.go
Normal file
2051
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
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package routing
|
||||
package graph
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -13,7 +13,6 @@ import (
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/graph"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
@ -57,16 +56,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 := atomic.AddUint64(&b.ntfnClientCounter, 1)
|
||||
|
||||
log.Debugf("New graph topology client subscription, client %v",
|
||||
clientID)
|
||||
@ -74,12 +73,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")
|
||||
}
|
||||
|
||||
@ -87,11 +86,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
|
||||
}
|
||||
},
|
||||
@ -117,7 +116,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.
|
||||
@ -146,7 +145,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)
|
||||
@ -158,7 +157,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.
|
||||
@ -314,7 +313,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 graph.DB, 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,28 @@ var (
|
||||
bitcoinKey2 = priv2.PubKey()
|
||||
|
||||
timeout = time.Second * 5
|
||||
|
||||
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)
|
||||
|
||||
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 +89,7 @@ func createTestNode() (*channeldb.LightningNode, error) {
|
||||
}
|
||||
copy(n.PubKeyBytes[:], pub)
|
||||
|
||||
return n, nil
|
||||
return n
|
||||
}
|
||||
|
||||
func randEdgePolicy(chanID *lnwire.ShortChannelID,
|
||||
@ -271,7 +289,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 +320,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 +432,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 +455,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 +474,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 +493,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 +641,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 +663,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 +770,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 +802,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 +822,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 +844,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 +895,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 +913,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 +1009,200 @@ 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 (c *testCtx) getChannelIDFromAlias(t *testing.T, a, b string) uint64 {
|
||||
vertexA, ok := c.aliases[a]
|
||||
require.True(t, ok, "cannot find aliases for %s", a)
|
||||
|
||||
vertexB, ok := c.aliases[b]
|
||||
require.True(t, ok, "cannot find aliases for %s", b)
|
||||
|
||||
channelIDMap, ok := c.channelIDs[vertexA]
|
||||
require.True(t, ok, "cannot find channelID map %s(%s)", vertexA, a)
|
||||
|
||||
channelID, ok := channelIDMap[vertexB]
|
||||
require.True(t, ok, "cannot find channelID using %s(%s)", vertexB, b)
|
||||
|
||||
return channelID
|
||||
}
|
||||
|
||||
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() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -2232,8 +2214,8 @@ func TestPathFindSpecExample(t *testing.T) {
|
||||
carol := ctx.aliases["C"]
|
||||
const amt lnwire.MilliSatoshi = 4999999
|
||||
req, err := NewRouteRequest(
|
||||
bob, &carol, amt, 0, noRestrictions, nil, nil, nil,
|
||||
MinCLTVDelta,
|
||||
bob, &carol, amt, 0, noRestrictions, nil, nil,
|
||||
nil, MinCLTVDelta,
|
||||
)
|
||||
require.NoError(t, err, "invalid route request")
|
||||
|
||||
@ -2244,33 +2226,18 @@ 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"]
|
||||
ctx.router.cfg.SelfNode = alice
|
||||
|
||||
// We'll now request a route from A -> B -> C.
|
||||
req, err = NewRouteRequest(
|
||||
@ -2283,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:
|
||||
@ -2317,59 +2273,31 @@ 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())
|
||||
}
|
||||
}
|
||||
|
||||
@ -2380,9 +2308,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
|
||||
@ -2425,11 +2351,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)
|
||||
|
@ -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"
|
||||
)
|
||||
@ -412,7 +413,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,
|
||||
)
|
||||
|
1750
routing/router.go
1750
routing/router.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
11
rpcserver.go
11
rpcserver.go
@ -49,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"
|
||||
@ -3075,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
|
||||
}
|
||||
|
||||
@ -3118,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{
|
||||
@ -6418,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,
|
||||
}
|
||||
@ -6613,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
|
||||
}
|
||||
@ -6665,7 +6666,7 @@ 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
|
||||
|
56
server.go
56
server.go
@ -342,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
|
||||
}
|
||||
@ -976,32 +976,36 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
||||
strictPruning := cfg.Bitcoin.Node == "neutrino" ||
|
||||
cfg.Routing.StrictZombiePruning
|
||||
|
||||
s.graphBuilder, err = graph.NewBuilder(&graph.Config{})
|
||||
s.graphBuilder, err = graph.NewBuilder(&graph.Config{
|
||||
SelfNode: selfNode.PubKeyBytes,
|
||||
Graph: chanGraph,
|
||||
Chain: cc.ChainIO,
|
||||
ChainView: cc.ChainView,
|
||||
Notifier: cc.ChainNotifier,
|
||||
ChannelPruneExpiry: graph.DefaultChannelPruneExpiry,
|
||||
GraphPruneInterval: time.Hour,
|
||||
FirstTimePruneDelay: graph.DefaultFirstTimePruneDelay,
|
||||
AssumeChannelValid: cfg.Routing.AssumeChannelValid,
|
||||
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),
|
||||
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,
|
||||
GraphPruneInterval: time.Hour,
|
||||
FirstTimePruneDelay: routing.DefaultFirstTimePruneDelay,
|
||||
GetLink: s.htlcSwitch.GetLinkByShortID,
|
||||
AssumeChannelValid: cfg.Routing.AssumeChannelValid,
|
||||
NextPaymentID: sequencer.NextID,
|
||||
PathFindingConfig: pathFindingConfig,
|
||||
Clock: clock.NewDefaultClock(),
|
||||
StrictZombiePruning: strictPruning,
|
||||
IsAlias: aliasmgr.IsAlias,
|
||||
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)
|
||||
@ -1018,7 +1022,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
||||
}
|
||||
|
||||
s.authGossiper = discovery.New(discovery.Config{
|
||||
Router: s.chanRouter,
|
||||
Router: s.graphBuilder,
|
||||
Notifier: s.cc.ChainNotifier,
|
||||
ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
|
||||
Broadcast: s.BroadcastMessage,
|
||||
@ -1053,11 +1057,11 @@ 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)
|
||||
|
||||
s.localChanMgr = &localchans.Manager{
|
||||
ForAllOutgoingChannels: s.chanRouter.ForAllOutgoingChannels,
|
||||
ForAllOutgoingChannels: s.graphBuilder.ForAllOutgoingChannels,
|
||||
PropagateChanPolicyUpdate: s.authGossiper.PropagateChanPolicyUpdate,
|
||||
UpdateForwardingPolicies: s.htlcSwitch.UpdateForwardingPolicies,
|
||||
FetchChannel: s.chanStateDB.FetchChannel,
|
||||
@ -4667,7 +4671,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…
Reference in New Issue
Block a user