Merge pull request #2958 from halseth/route-sub-package

multi: move Route to sub-pkg routing/route
This commit is contained in:
Olaoluwa Osuntokun 2019-04-29 15:36:38 -07:00 committed by GitHub
commit 84dd949132
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 391 additions and 366 deletions

View File

@ -6,7 +6,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route"
) )
// ChannelGraphTimeSeries is an interface that provides time and block based // ChannelGraphTimeSeries is an interface that provides time and block based
@ -247,7 +247,7 @@ func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash,
// We'll use this map to ensure we don't send the same node // We'll use this map to ensure we don't send the same node
// announcement more than one time as one node may have many channel // announcement more than one time as one node may have many channel
// anns we'll need to send. // anns we'll need to send.
nodePubsSent := make(map[routing.Vertex]struct{}) nodePubsSent := make(map[route.Vertex]struct{})
chanAnns := make([]lnwire.Message, 0, len(channels)*3) chanAnns := make([]lnwire.Message, 0, len(channels)*3)
for _, channel := range channels { for _, channel := range channels {

View File

@ -21,6 +21,7 @@ import (
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/multimutex" "github.com/lightningnetwork/lnd/multimutex"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
) )
@ -131,7 +132,7 @@ type Config struct {
// that the daemon is connected to. If supplied, the exclude parameter // that the daemon is connected to. If supplied, the exclude parameter
// indicates that the target peer should be excluded from the // indicates that the target peer should be excluded from the
// broadcast. // broadcast.
Broadcast func(skips map[routing.Vertex]struct{}, Broadcast func(skips map[route.Vertex]struct{},
msg ...lnwire.Message) error msg ...lnwire.Message) error
// NotifyWhenOnline is a function that allows the gossiper to be // NotifyWhenOnline is a function that allows the gossiper to be
@ -342,7 +343,7 @@ func (d *AuthenticatedGossiper) SynchronizeNode(syncPeer lnpeer.Peer) error {
// We'll use this map to ensure we don't send the same node // We'll use this map to ensure we don't send the same node
// announcement more than one time as one node may have many channel // announcement more than one time as one node may have many channel
// anns we'll need to send. // anns we'll need to send.
nodePubsSent := make(map[routing.Vertex]struct{}) nodePubsSent := make(map[route.Vertex]struct{})
// As peers are expecting channel announcements before node // As peers are expecting channel announcements before node
// announcements, we first retrieve the initial announcement, as well as // announcements, we first retrieve the initial announcement, as well as
@ -654,14 +655,14 @@ type msgWithSenders struct {
msg lnwire.Message msg lnwire.Message
// sender is the set of peers that sent us this message. // sender is the set of peers that sent us this message.
senders map[routing.Vertex]struct{} senders map[route.Vertex]struct{}
} }
// mergeSyncerMap is used to merge the set of senders of a particular message // mergeSyncerMap is used to merge the set of senders of a particular message
// with peers that we have an active GossipSyncer with. We do this to ensure // with peers that we have an active GossipSyncer with. We do this to ensure
// that we don't broadcast messages to any peers that we have active gossip // that we don't broadcast messages to any peers that we have active gossip
// syncers for. // syncers for.
func (m *msgWithSenders) mergeSyncerMap(syncers map[routing.Vertex]*GossipSyncer) { func (m *msgWithSenders) mergeSyncerMap(syncers map[route.Vertex]*GossipSyncer) {
for peerPub := range syncers { for peerPub := range syncers {
m.senders[peerPub] = struct{}{} m.senders[peerPub] = struct{}{}
} }
@ -682,7 +683,7 @@ type deDupedAnnouncements struct {
channelUpdates map[channelUpdateID]msgWithSenders channelUpdates map[channelUpdateID]msgWithSenders
// nodeAnnouncements are identified by the Vertex field. // nodeAnnouncements are identified by the Vertex field.
nodeAnnouncements map[routing.Vertex]msgWithSenders nodeAnnouncements map[route.Vertex]msgWithSenders
sync.Mutex sync.Mutex
} }
@ -704,7 +705,7 @@ func (d *deDupedAnnouncements) reset() {
// appropriate key points to the corresponding lnwire.Message. // appropriate key points to the corresponding lnwire.Message.
d.channelAnnouncements = make(map[lnwire.ShortChannelID]msgWithSenders) d.channelAnnouncements = make(map[lnwire.ShortChannelID]msgWithSenders)
d.channelUpdates = make(map[channelUpdateID]msgWithSenders) d.channelUpdates = make(map[channelUpdateID]msgWithSenders)
d.nodeAnnouncements = make(map[routing.Vertex]msgWithSenders) d.nodeAnnouncements = make(map[route.Vertex]msgWithSenders)
} }
// addMsg adds a new message to the current batch. If the message is already // addMsg adds a new message to the current batch. If the message is already
@ -722,13 +723,13 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) {
// Channel announcements are identified by the short channel id field. // Channel announcements are identified by the short channel id field.
case *lnwire.ChannelAnnouncement: case *lnwire.ChannelAnnouncement:
deDupKey := msg.ShortChannelID deDupKey := msg.ShortChannelID
sender := routing.NewVertex(message.source) sender := route.NewVertex(message.source)
mws, ok := d.channelAnnouncements[deDupKey] mws, ok := d.channelAnnouncements[deDupKey]
if !ok { if !ok {
mws = msgWithSenders{ mws = msgWithSenders{
msg: msg, msg: msg,
senders: make(map[routing.Vertex]struct{}), senders: make(map[route.Vertex]struct{}),
} }
mws.senders[sender] = struct{}{} mws.senders[sender] = struct{}{}
@ -744,7 +745,7 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) {
// Channel updates are identified by the (short channel id, // Channel updates are identified by the (short channel id,
// channelflags) tuple. // channelflags) tuple.
case *lnwire.ChannelUpdate: case *lnwire.ChannelUpdate:
sender := routing.NewVertex(message.source) sender := route.NewVertex(message.source)
deDupKey := channelUpdateID{ deDupKey := channelUpdateID{
msg.ShortChannelID, msg.ShortChannelID,
msg.ChannelFlags, msg.ChannelFlags,
@ -770,7 +771,7 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) {
if oldTimestamp < msg.Timestamp { if oldTimestamp < msg.Timestamp {
mws = msgWithSenders{ mws = msgWithSenders{
msg: msg, msg: msg,
senders: make(map[routing.Vertex]struct{}), senders: make(map[route.Vertex]struct{}),
} }
// We'll mark the sender of the message in the // We'll mark the sender of the message in the
@ -793,8 +794,8 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) {
// Node announcements are identified by the Vertex field. Use the // Node announcements are identified by the Vertex field. Use the
// NodeID to create the corresponding Vertex. // NodeID to create the corresponding Vertex.
case *lnwire.NodeAnnouncement: case *lnwire.NodeAnnouncement:
sender := routing.NewVertex(message.source) sender := route.NewVertex(message.source)
deDupKey := routing.Vertex(msg.NodeID) deDupKey := route.Vertex(msg.NodeID)
// We do the same for node announcements as we did for channel // We do the same for node announcements as we did for channel
// updates, as they also carry a timestamp. // updates, as they also carry a timestamp.
@ -813,7 +814,7 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) {
if oldTimestamp < msg.Timestamp { if oldTimestamp < msg.Timestamp {
mws = msgWithSenders{ mws = msgWithSenders{
msg: msg, msg: msg,
senders: make(map[routing.Vertex]struct{}), senders: make(map[route.Vertex]struct{}),
} }
mws.senders[sender] = struct{}{} mws.senders[sender] = struct{}{}
@ -1137,7 +1138,7 @@ func (d *AuthenticatedGossiper) InitSyncState(syncPeer lnpeer.Peer) {
// PruneSyncState is called by outside sub-systems once a peer that we were // PruneSyncState is called by outside sub-systems once a peer that we were
// previously connected to has been disconnected. In this case we can stop the // previously connected to has been disconnected. In this case we can stop the
// existing GossipSyncer assigned to the peer and free up resources. // existing GossipSyncer assigned to the peer and free up resources.
func (d *AuthenticatedGossiper) PruneSyncState(peer routing.Vertex) { func (d *AuthenticatedGossiper) PruneSyncState(peer route.Vertex) {
d.syncMgr.PruneSyncState(peer) d.syncMgr.PruneSyncState(peer)
} }

View File

@ -27,6 +27,7 @@ import (
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
) )
@ -272,7 +273,7 @@ func (r *mockGraphSource) GetChannelByID(chanID lnwire.ShortChannelID) (
} }
func (r *mockGraphSource) FetchLightningNode( func (r *mockGraphSource) FetchLightningNode(
nodePub routing.Vertex) (*channeldb.LightningNode, error) { nodePub route.Vertex) (*channeldb.LightningNode, error) {
for _, node := range r.nodes { for _, node := range r.nodes {
if bytes.Equal(nodePub[:], node.PubKeyBytes[:]) { if bytes.Equal(nodePub[:], node.PubKeyBytes[:]) {
@ -285,7 +286,7 @@ func (r *mockGraphSource) FetchLightningNode(
// IsStaleNode returns true if the graph source has a node announcement for the // IsStaleNode returns true if the graph source has a node announcement for the
// target node with a more recent timestamp. // target node with a more recent timestamp.
func (r *mockGraphSource) IsStaleNode(nodePub routing.Vertex, timestamp time.Time) bool { func (r *mockGraphSource) IsStaleNode(nodePub route.Vertex, timestamp time.Time) bool {
r.mu.Lock() r.mu.Lock()
defer r.mu.Unlock() defer r.mu.Unlock()
@ -312,7 +313,7 @@ func (r *mockGraphSource) IsStaleNode(nodePub routing.Vertex, timestamp time.Tim
// IsPublicNode determines whether the given vertex is seen as a public node in // IsPublicNode determines whether the given vertex is seen as a public node in
// the graph from the graph's source node's point of view. // the graph from the graph's source node's point of view.
func (r *mockGraphSource) IsPublicNode(node routing.Vertex) (bool, error) { func (r *mockGraphSource) IsPublicNode(node route.Vertex) (bool, error) {
for _, info := range r.infos { for _, info := range r.infos {
if !bytes.Equal(node[:], info.NodeKey1Bytes[:]) && if !bytes.Equal(node[:], info.NodeKey1Bytes[:]) &&
!bytes.Equal(node[:], info.NodeKey2Bytes[:]) { !bytes.Equal(node[:], info.NodeKey2Bytes[:]) {
@ -721,7 +722,7 @@ func createTestCtx(startHeight uint32) (*testCtx, func(), error) {
broadcastedMessage := make(chan msgWithSenders, 10) broadcastedMessage := make(chan msgWithSenders, 10)
gossiper := New(Config{ gossiper := New(Config{
Notifier: notifier, Notifier: notifier,
Broadcast: func(senders map[routing.Vertex]struct{}, Broadcast: func(senders map[route.Vertex]struct{},
msgs ...lnwire.Message) error { msgs ...lnwire.Message) error {
for _, msg := range msgs { for _, msg := range msgs {
@ -785,7 +786,7 @@ func TestProcessAnnouncement(t *testing.T) {
defer cleanup() defer cleanup()
assertSenderExistence := func(sender *btcec.PublicKey, msg msgWithSenders) { assertSenderExistence := func(sender *btcec.PublicKey, msg msgWithSenders) {
if _, ok := msg.senders[routing.NewVertex(sender)]; !ok { if _, ok := msg.senders[route.NewVertex(sender)]; !ok {
t.Fatalf("sender=%x not present in %v", t.Fatalf("sender=%x not present in %v",
sender.SerializeCompressed(), spew.Sdump(msg)) sender.SerializeCompressed(), spew.Sdump(msg))
} }
@ -1987,7 +1988,7 @@ func TestDeDuplicatedAnnouncements(t *testing.T) {
if len(announcements.nodeAnnouncements) != 2 { if len(announcements.nodeAnnouncements) != 2 {
t.Fatal("node announcement not replaced in batch") t.Fatal("node announcement not replaced in batch")
} }
nodeID := routing.NewVertex(nodeKeyPriv2.PubKey()) nodeID := route.NewVertex(nodeKeyPriv2.PubKey())
stored, ok := announcements.nodeAnnouncements[nodeID] stored, ok := announcements.nodeAnnouncements[nodeID]
if !ok { if !ok {
t.Fatalf("node announcement not found in batch") t.Fatalf("node announcement not found in batch")

View File

@ -8,7 +8,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
) )
@ -45,7 +45,7 @@ type newSyncer struct {
// that a peer has disconnected and its GossipSyncer should be removed. // that a peer has disconnected and its GossipSyncer should be removed.
type staleSyncer struct { type staleSyncer struct {
// peer is the peer that has disconnected. // peer is the peer that has disconnected.
peer routing.Vertex peer route.Vertex
// doneChan serves as a signal to the caller that the SyncManager's // doneChan serves as a signal to the caller that the SyncManager's
// internal state correctly reflects the stale active syncer. This is // internal state correctly reflects the stale active syncer. This is
@ -118,11 +118,11 @@ type SyncManager struct {
// activeSyncers is the set of all syncers for which we are currently // activeSyncers is the set of all syncers for which we are currently
// receiving graph updates from. The number of possible active syncers // receiving graph updates from. The number of possible active syncers
// is bounded by NumActiveSyncers. // is bounded by NumActiveSyncers.
activeSyncers map[routing.Vertex]*GossipSyncer activeSyncers map[route.Vertex]*GossipSyncer
// inactiveSyncers is the set of all syncers for which we are not // inactiveSyncers is the set of all syncers for which we are not
// currently receiving new graph updates from. // currently receiving new graph updates from.
inactiveSyncers map[routing.Vertex]*GossipSyncer inactiveSyncers map[route.Vertex]*GossipSyncer
wg sync.WaitGroup wg sync.WaitGroup
quit chan struct{} quit chan struct{}
@ -135,9 +135,9 @@ func newSyncManager(cfg *SyncManagerCfg) *SyncManager {
newSyncers: make(chan *newSyncer), newSyncers: make(chan *newSyncer),
staleSyncers: make(chan *staleSyncer), staleSyncers: make(chan *staleSyncer),
activeSyncers: make( activeSyncers: make(
map[routing.Vertex]*GossipSyncer, cfg.NumActiveSyncers, map[route.Vertex]*GossipSyncer, cfg.NumActiveSyncers,
), ),
inactiveSyncers: make(map[routing.Vertex]*GossipSyncer), inactiveSyncers: make(map[route.Vertex]*GossipSyncer),
quit: make(chan struct{}), quit: make(chan struct{}),
} }
} }
@ -372,7 +372,7 @@ func (m *SyncManager) syncerHandler() {
// createGossipSyncer creates the GossipSyncer for a newly connected peer. // createGossipSyncer creates the GossipSyncer for a newly connected peer.
func (m *SyncManager) createGossipSyncer(peer lnpeer.Peer) *GossipSyncer { func (m *SyncManager) createGossipSyncer(peer lnpeer.Peer) *GossipSyncer {
nodeID := routing.Vertex(peer.PubKey()) nodeID := route.Vertex(peer.PubKey())
log.Infof("Creating new GossipSyncer for peer=%x", nodeID[:]) log.Infof("Creating new GossipSyncer for peer=%x", nodeID[:])
encoding := lnwire.EncodingSortedPlain encoding := lnwire.EncodingSortedPlain
@ -399,7 +399,7 @@ func (m *SyncManager) createGossipSyncer(peer lnpeer.Peer) *GossipSyncer {
// removeGossipSyncer removes all internal references to the disconnected peer's // removeGossipSyncer removes all internal references to the disconnected peer's
// GossipSyncer and stops it. In the event of an active GossipSyncer being // GossipSyncer and stops it. In the event of an active GossipSyncer being
// disconnected, a passive GossipSyncer, if any, will take its place. // disconnected, a passive GossipSyncer, if any, will take its place.
func (m *SyncManager) removeGossipSyncer(peer routing.Vertex) { func (m *SyncManager) removeGossipSyncer(peer route.Vertex) {
m.syncersMu.Lock() m.syncersMu.Lock()
defer m.syncersMu.Unlock() defer m.syncersMu.Unlock()
@ -527,7 +527,7 @@ func (m *SyncManager) forceHistoricalSync() *GossipSyncer {
// //
// NOTE: It's possible for a nil value to be returned if there are no eligible // NOTE: It's possible for a nil value to be returned if there are no eligible
// candidate syncers. // candidate syncers.
func chooseRandomSyncer(syncers map[routing.Vertex]*GossipSyncer, func chooseRandomSyncer(syncers map[route.Vertex]*GossipSyncer,
action func(*GossipSyncer) error) *GossipSyncer { action func(*GossipSyncer) error) *GossipSyncer {
for _, s := range syncers { for _, s := range syncers {
@ -583,7 +583,7 @@ func (m *SyncManager) InitSyncState(peer lnpeer.Peer) error {
// PruneSyncState is called by outside sub-systems once a peer that we were // PruneSyncState is called by outside sub-systems once a peer that we were
// previously connected to has been disconnected. In this case we can stop the // previously connected to has been disconnected. In this case we can stop the
// existing GossipSyncer assigned to the peer and free up resources. // existing GossipSyncer assigned to the peer and free up resources.
func (m *SyncManager) PruneSyncState(peer routing.Vertex) { func (m *SyncManager) PruneSyncState(peer route.Vertex) {
done := make(chan struct{}) done := make(chan struct{})
// We avoid returning an error when the SyncManager is stopped since the // We avoid returning an error when the SyncManager is stopped since the
@ -605,7 +605,7 @@ func (m *SyncManager) PruneSyncState(peer routing.Vertex) {
// GossipSyncer returns the associated gossip syncer of a peer. The boolean // GossipSyncer returns the associated gossip syncer of a peer. The boolean
// returned signals whether there exists a gossip syncer for the peer. // returned signals whether there exists a gossip syncer for the peer.
func (m *SyncManager) GossipSyncer(peer routing.Vertex) (*GossipSyncer, bool) { func (m *SyncManager) GossipSyncer(peer route.Vertex) (*GossipSyncer, bool) {
m.syncersMu.Lock() m.syncersMu.Lock()
defer m.syncersMu.Unlock() defer m.syncersMu.Unlock()
return m.gossipSyncer(peer) return m.gossipSyncer(peer)
@ -613,7 +613,7 @@ func (m *SyncManager) GossipSyncer(peer routing.Vertex) (*GossipSyncer, bool) {
// gossipSyncer returns the associated gossip syncer of a peer. The boolean // gossipSyncer returns the associated gossip syncer of a peer. The boolean
// returned signals whether there exists a gossip syncer for the peer. // returned signals whether there exists a gossip syncer for the peer.
func (m *SyncManager) gossipSyncer(peer routing.Vertex) (*GossipSyncer, bool) { func (m *SyncManager) gossipSyncer(peer route.Vertex) (*GossipSyncer, bool) {
syncer, ok := m.inactiveSyncers[peer] syncer, ok := m.inactiveSyncers[peer]
if ok { if ok {
return syncer, true return syncer, true
@ -626,16 +626,16 @@ func (m *SyncManager) gossipSyncer(peer routing.Vertex) (*GossipSyncer, bool) {
} }
// GossipSyncers returns all of the currently initialized gossip syncers. // GossipSyncers returns all of the currently initialized gossip syncers.
func (m *SyncManager) GossipSyncers() map[routing.Vertex]*GossipSyncer { func (m *SyncManager) GossipSyncers() map[route.Vertex]*GossipSyncer {
m.syncersMu.Lock() m.syncersMu.Lock()
defer m.syncersMu.Unlock() defer m.syncersMu.Unlock()
return m.gossipSyncers() return m.gossipSyncers()
} }
// gossipSyncers returns all of the currently initialized gossip syncers. // gossipSyncers returns all of the currently initialized gossip syncers.
func (m *SyncManager) gossipSyncers() map[routing.Vertex]*GossipSyncer { func (m *SyncManager) gossipSyncers() map[route.Vertex]*GossipSyncer {
numSyncers := len(m.inactiveSyncers) + len(m.activeSyncers) numSyncers := len(m.inactiveSyncers) + len(m.activeSyncers)
syncers := make(map[routing.Vertex]*GossipSyncer, numSyncers) syncers := make(map[route.Vertex]*GossipSyncer, numSyncers)
for _, syncer := range m.inactiveSyncers { for _, syncer := range m.inactiveSyncers {
syncers[syncer.cfg.peerPub] = syncer syncers[syncer.cfg.peerPub] = syncer

View File

@ -9,6 +9,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route"
context "golang.org/x/net/context" context "golang.org/x/net/context"
) )
@ -19,7 +20,7 @@ type RouterBackend struct {
MaxPaymentMSat lnwire.MilliSatoshi MaxPaymentMSat lnwire.MilliSatoshi
// SelfNode is the vertex of the node sending the payment. // SelfNode is the vertex of the node sending the payment.
SelfNode routing.Vertex SelfNode route.Vertex
// FetchChannelCapacity is a closure that we'll use the fetch the total // FetchChannelCapacity is a closure that we'll use the fetch the total
// capacity of a channel to populate in responses. // capacity of a channel to populate in responses.
@ -27,10 +28,10 @@ type RouterBackend struct {
// FindRoutes is a closure that abstracts away how we locate/query for // FindRoutes is a closure that abstracts away how we locate/query for
// routes. // routes.
FindRoutes func(source, target routing.Vertex, FindRoutes func(source, target route.Vertex,
amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams, amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams,
numPaths uint32, finalExpiry ...uint16) ( numPaths uint32, finalExpiry ...uint16) (
[]*routing.Route, error) []*route.Route, error)
} }
// QueryRoutes attempts to query the daemons' Channel Router for a possible // QueryRoutes attempts to query the daemons' Channel Router for a possible
@ -45,18 +46,18 @@ type RouterBackend struct {
func (r *RouterBackend) QueryRoutes(ctx context.Context, func (r *RouterBackend) QueryRoutes(ctx context.Context,
in *lnrpc.QueryRoutesRequest) (*lnrpc.QueryRoutesResponse, error) { in *lnrpc.QueryRoutesRequest) (*lnrpc.QueryRoutesResponse, error) {
parsePubKey := func(key string) (routing.Vertex, error) { parsePubKey := func(key string) (route.Vertex, error) {
pubKeyBytes, err := hex.DecodeString(key) pubKeyBytes, err := hex.DecodeString(key)
if err != nil { if err != nil {
return routing.Vertex{}, err return route.Vertex{}, err
} }
if len(pubKeyBytes) != 33 { if len(pubKeyBytes) != 33 {
return routing.Vertex{}, return route.Vertex{},
errors.New("invalid key length") errors.New("invalid key length")
} }
var v routing.Vertex var v route.Vertex
copy(v[:], pubKeyBytes) copy(v[:], pubKeyBytes)
return v, nil return v, nil
@ -69,7 +70,7 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
return nil, err return nil, err
} }
var sourcePubKey routing.Vertex var sourcePubKey route.Vertex
if in.SourcePubKey != "" { if in.SourcePubKey != "" {
var err error var err error
sourcePubKey, err = parsePubKey(in.SourcePubKey) sourcePubKey, err = parsePubKey(in.SourcePubKey)
@ -94,12 +95,12 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
// Unmarshall restrictions from request. // Unmarshall restrictions from request.
feeLimit := calculateFeeLimit(in.FeeLimit, amtMSat) feeLimit := calculateFeeLimit(in.FeeLimit, amtMSat)
ignoredNodes := make(map[routing.Vertex]struct{}) ignoredNodes := make(map[route.Vertex]struct{})
for _, ignorePubKey := range in.IgnoredNodes { for _, ignorePubKey := range in.IgnoredNodes {
if len(ignorePubKey) != 33 { if len(ignorePubKey) != 33 {
return nil, fmt.Errorf("invalid ignore node pubkey") return nil, fmt.Errorf("invalid ignore node pubkey")
} }
var ignoreVertex routing.Vertex var ignoreVertex route.Vertex
copy(ignoreVertex[:], ignorePubKey) copy(ignoreVertex[:], ignorePubKey)
ignoredNodes[ignoreVertex] = struct{}{} ignoredNodes[ignoreVertex] = struct{}{}
} }
@ -131,7 +132,7 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
// can carry `in.Amt` satoshis _including_ the total fee required on // can carry `in.Amt` satoshis _including_ the total fee required on
// the route. // the route.
var ( var (
routes []*routing.Route routes []*route.Route
findErr error findErr error
) )
@ -195,7 +196,7 @@ func calculateFeeLimit(feeLimit *lnrpc.FeeLimit,
} }
// MarshallRoute marshalls an internal route to an rpc route struct. // MarshallRoute marshalls an internal route to an rpc route struct.
func (r *RouterBackend) MarshallRoute(route *routing.Route) *lnrpc.Route { func (r *RouterBackend) MarshallRoute(route *route.Route) *lnrpc.Route {
resp := &lnrpc.Route{ resp := &lnrpc.Route{
TotalTimeLock: route.TotalTimeLock, TotalTimeLock: route.TotalTimeLock,
TotalFees: int64(route.TotalFees.ToSatoshis()), TotalFees: int64(route.TotalFees.ToSatoshis()),

View File

@ -9,6 +9,7 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
) )
@ -19,7 +20,7 @@ const (
) )
var ( var (
sourceKey = routing.Vertex{1, 2, 3} sourceKey = route.Vertex{1, 2, 3}
) )
// TestQueryRoutes asserts that query routes rpc parameters are properly parsed // TestQueryRoutes asserts that query routes rpc parameters are properly parsed
@ -30,7 +31,7 @@ func TestQueryRoutes(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
var ignoreNodeVertex routing.Vertex var ignoreNodeVertex route.Vertex
copy(ignoreNodeVertex[:], ignoreNodeBytes) copy(ignoreNodeVertex[:], ignoreNodeBytes)
destNodeBytes, err := hex.DecodeString(destKey) destNodeBytes, err := hex.DecodeString(destKey)
@ -55,12 +56,12 @@ func TestQueryRoutes(t *testing.T) {
}}, }},
} }
route := &routing.Route{} rt := &route.Route{}
findRoutes := func(source, target routing.Vertex, findRoutes := func(source, target route.Vertex,
amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams, amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams,
numPaths uint32, finalExpiry ...uint16) ( numPaths uint32, finalExpiry ...uint16) (
[]*routing.Route, error) { []*route.Route, error) {
if int64(amt) != request.Amt*1000 { if int64(amt) != request.Amt*1000 {
t.Fatal("unexpected amount") t.Fatal("unexpected amount")
@ -100,15 +101,15 @@ func TestQueryRoutes(t *testing.T) {
t.Fatal("unexpected ignored node") t.Fatal("unexpected ignored node")
} }
return []*routing.Route{ return []*route.Route{
route, rt,
}, nil }, nil
} }
backend := &RouterBackend{ backend := &RouterBackend{
MaxPaymentMSat: lnwire.NewMSatFromSatoshis(1000000), MaxPaymentMSat: lnwire.NewMSatFromSatoshis(1000000),
FindRoutes: findRoutes, FindRoutes: findRoutes,
SelfNode: routing.Vertex{1, 2, 3}, SelfNode: route.Vertex{1, 2, 3},
FetchChannelCapacity: func(chanID uint64) ( FetchChannelCapacity: func(chanID uint64) (
btcutil.Amount, error) { btcutil.Amount, error) {

View File

@ -15,6 +15,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32" "github.com/lightningnetwork/lnd/zpay32"
"google.golang.org/grpc" "google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery" "gopkg.in/macaroon-bakery.v2/bakery"
@ -190,7 +191,7 @@ func (s *Server) SendPayment(ctx context.Context,
return nil, fmt.Errorf("zero value invoices are not supported") return nil, fmt.Errorf("zero value invoices are not supported")
} }
var destination routing.Vertex var destination route.Vertex
copy(destination[:], payReq.Destination.SerializeCompressed()) copy(destination[:], payReq.Destination.SerializeCompressed())
// Now that all the information we need has been parsed, we'll map this // Now that all the information we need has been parsed, we'll map this
@ -232,7 +233,7 @@ func (s *Server) EstimateRouteFee(ctx context.Context,
if len(req.Dest) != 33 { if len(req.Dest) != 33 {
return nil, errors.New("invalid length destination key") return nil, errors.New("invalid length destination key")
} }
var destNode routing.Vertex var destNode route.Vertex
copy(destNode[:], req.Dest) copy(destNode[:], req.Dest)
// Next, we'll convert the amount in satoshis to mSAT, which are the // Next, we'll convert the amount in satoshis to mSAT, which are the

View File

@ -8,6 +8,7 @@ import (
"github.com/coreos/bbolt" "github.com/coreos/bbolt"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32" "github.com/lightningnetwork/lnd/zpay32"
) )
@ -53,7 +54,7 @@ type missionControl struct {
// the time that it was added to the prune view. Vertexes are added to // the time that it was added to the prune view. Vertexes are added to
// this map if a caller reports to missionControl a failure localized // this map if a caller reports to missionControl a failure localized
// to that particular vertex. // to that particular vertex.
failedVertexes map[Vertex]time.Time failedVertexes map[route.Vertex]time.Time
graph *channeldb.ChannelGraph graph *channeldb.ChannelGraph
@ -77,7 +78,7 @@ func newMissionControl(g *channeldb.ChannelGraph, selfNode *channeldb.LightningN
return &missionControl{ return &missionControl{
failedEdges: make(map[EdgeLocator]time.Time), failedEdges: make(map[EdgeLocator]time.Time),
failedVertexes: make(map[Vertex]time.Time), failedVertexes: make(map[route.Vertex]time.Time),
selfNode: selfNode, selfNode: selfNode,
queryBandwidth: qb, queryBandwidth: qb,
graph: g, graph: g,
@ -92,7 +93,7 @@ func newMissionControl(g *channeldb.ChannelGraph, selfNode *channeldb.LightningN
type graphPruneView struct { type graphPruneView struct {
edges map[EdgeLocator]struct{} edges map[EdgeLocator]struct{}
vertexes map[Vertex]struct{} vertexes map[route.Vertex]struct{}
} }
// GraphPruneView returns a new graphPruneView instance which is to be // GraphPruneView returns a new graphPruneView instance which is to be
@ -110,7 +111,7 @@ func (m *missionControl) GraphPruneView() graphPruneView {
// For each of the vertexes that have been added to the prune view, if // For each of the vertexes that have been added to the prune view, if
// it is now "stale", then we'll ignore it and avoid adding it to the // it is now "stale", then we'll ignore it and avoid adding it to the
// view we'll return. // view we'll return.
vertexes := make(map[Vertex]struct{}) vertexes := make(map[route.Vertex]struct{})
for vertex, pruneTime := range m.failedVertexes { for vertex, pruneTime := range m.failedVertexes {
if now.Sub(pruneTime) >= vertexDecay { if now.Sub(pruneTime) >= vertexDecay {
log.Tracef("Pruning decayed failure report for vertex %v "+ log.Tracef("Pruning decayed failure report for vertex %v "+
@ -154,11 +155,11 @@ func (m *missionControl) GraphPruneView() graphPruneView {
// in order to populate additional edges to explore when finding a path to the // in order to populate additional edges to explore when finding a path to the
// payment's destination. // payment's destination.
func (m *missionControl) NewPaymentSession(routeHints [][]zpay32.HopHint, func (m *missionControl) NewPaymentSession(routeHints [][]zpay32.HopHint,
target Vertex) (*paymentSession, error) { target route.Vertex) (*paymentSession, error) {
viewSnapshot := m.GraphPruneView() viewSnapshot := m.GraphPruneView()
edges := make(map[Vertex][]*channeldb.ChannelEdgePolicy) edges := make(map[route.Vertex][]*channeldb.ChannelEdgePolicy)
// Traverse through all of the available hop hints and include them in // Traverse through all of the available hop hints and include them in
// our edges map, indexed by the public key of the channel's starting // our edges map, indexed by the public key of the channel's starting
@ -200,7 +201,7 @@ func (m *missionControl) NewPaymentSession(routeHints [][]zpay32.HopHint,
TimeLockDelta: hopHint.CLTVExpiryDelta, TimeLockDelta: hopHint.CLTVExpiryDelta,
} }
v := NewVertex(hopHint.NodeID) v := route.NewVertex(hopHint.NodeID)
edges[v] = append(edges[v], edge) edges[v] = append(edges[v], edge)
} }
} }
@ -234,7 +235,7 @@ func (m *missionControl) NewPaymentSession(routeHints [][]zpay32.HopHint,
// skip all path finding, and will instead utilize a set of pre-built routes. // skip all path finding, and will instead utilize a set of pre-built routes.
// This constructor allows callers to specify their own routes which can be // This constructor allows callers to specify their own routes which can be
// used for things like channel rebalancing, and swaps. // used for things like channel rebalancing, and swaps.
func (m *missionControl) NewPaymentSessionFromRoutes(routes []*Route) *paymentSession { func (m *missionControl) NewPaymentSessionFromRoutes(routes []*route.Route) *paymentSession {
return &paymentSession{ return &paymentSession{
pruneViewSnapshot: m.GraphPruneView(), pruneViewSnapshot: m.GraphPruneView(),
haveRoutes: true, haveRoutes: true,
@ -285,6 +286,6 @@ func generateBandwidthHints(sourceNode *channeldb.LightningNode,
func (m *missionControl) ResetHistory() { func (m *missionControl) ResetHistory() {
m.Lock() m.Lock()
m.failedEdges = make(map[EdgeLocator]time.Time) m.failedEdges = make(map[EdgeLocator]time.Time)
m.failedVertexes = make(map[Vertex]time.Time) m.failedVertexes = make(map[route.Vertex]time.Time)
m.Unlock() m.Unlock()
} }

View File

@ -20,6 +20,7 @@ import (
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/chainview" "github.com/lightningnetwork/lnd/routing/chainview"
"github.com/lightningnetwork/lnd/routing/route"
) )
var ( var (
@ -460,9 +461,9 @@ func TestEdgeUpdateNotification(t *testing.T) {
// Create lookup map for notifications we are intending to receive. Entries // Create lookup map for notifications we are intending to receive. Entries
// are removed from the map when the anticipated notification is received. // are removed from the map when the anticipated notification is received.
var waitingFor = map[Vertex]int{ var waitingFor = map[route.Vertex]int{
Vertex(node1.PubKeyBytes): 1, route.Vertex(node1.PubKeyBytes): 1,
Vertex(node2.PubKeyBytes): 2, route.Vertex(node2.PubKeyBytes): 2,
} }
node1Pub, err := node1.PubKey() node1Pub, err := node1.PubKey()
@ -486,7 +487,7 @@ func TestEdgeUpdateNotification(t *testing.T) {
} }
edgeUpdate := ntfn.ChannelEdgeUpdates[0] edgeUpdate := ntfn.ChannelEdgeUpdates[0]
nodeVertex := NewVertex(edgeUpdate.AdvertisingNode) nodeVertex := route.NewVertex(edgeUpdate.AdvertisingNode)
if idx, ok := waitingFor[nodeVertex]; ok { if idx, ok := waitingFor[nodeVertex]; ok {
switch idx { switch idx {
@ -630,9 +631,9 @@ func TestNodeUpdateNotification(t *testing.T) {
// Create lookup map for notifications we are intending to receive. Entries // Create lookup map for notifications we are intending to receive. Entries
// are removed from the map when the anticipated notification is received. // are removed from the map when the anticipated notification is received.
var waitingFor = map[Vertex]int{ var waitingFor = map[route.Vertex]int{
Vertex(node1.PubKeyBytes): 1, route.Vertex(node1.PubKeyBytes): 1,
Vertex(node2.PubKeyBytes): 2, route.Vertex(node2.PubKeyBytes): 2,
} }
// Exactly two notifications should be sent, each corresponding to the // Exactly two notifications should be sent, each corresponding to the
@ -649,7 +650,7 @@ func TestNodeUpdateNotification(t *testing.T) {
} }
nodeUpdate := ntfn.NodeUpdates[0] nodeUpdate := ntfn.NodeUpdates[0]
nodeVertex := NewVertex(nodeUpdate.IdentityKey) nodeVertex := route.NewVertex(nodeUpdate.IdentityKey)
if idx, ok := waitingFor[nodeVertex]; ok { if idx, ok := waitingFor[nodeVertex]; ok {
switch idx { switch idx {
case 1: case 1:

View File

@ -1,18 +1,14 @@
package routing package routing
import ( import (
"encoding/binary" "container/heap"
"fmt"
"math" "math"
"container/heap"
"github.com/btcsuite/btcd/btcec"
"github.com/coreos/bbolt" "github.com/coreos/bbolt"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
) )
const ( const (
@ -41,34 +37,9 @@ const (
// pathFinder defines the interface of a path finding algorithm. // pathFinder defines the interface of a path finding algorithm.
type pathFinder = func(g *graphParams, r *RestrictParams, type pathFinder = func(g *graphParams, r *RestrictParams,
source, target Vertex, amt lnwire.MilliSatoshi) ( source, target route.Vertex, amt lnwire.MilliSatoshi) (
[]*channeldb.ChannelEdgePolicy, error) []*channeldb.ChannelEdgePolicy, error)
// Hop represents an intermediate or final node of the route. This naming
// is in line with the definition given in BOLT #4: Onion Routing Protocol.
// The struct houses the channel along which this hop can be reached and
// the values necessary to create the HTLC that needs to be sent to the
// next hop. It is also used to encode the per-hop payload included within
// the Sphinx packet.
type Hop struct {
// PubKeyBytes is the raw bytes of the public key of the target node.
PubKeyBytes Vertex
// ChannelID is the unique channel ID for the channel. The first 3
// bytes are the block height, the next 3 the index within the block,
// and the last 2 bytes are the output index for the channel.
ChannelID uint64
// OutgoingTimeLock is the timelock value that should be used when
// crafting the _outgoing_ HTLC from this hop.
OutgoingTimeLock uint32
// AmtToForward is the amount that this hop will forward to the next
// hop. This value is less than the value that the incoming HTLC
// carries as a fee will be subtracted by the hop.
AmtToForward lnwire.MilliSatoshi
}
// edgePolicyWithSource is a helper struct to keep track of the source node // edgePolicyWithSource is a helper struct to keep track of the source node
// of a channel edge. ChannelEdgePolicy only contains to destination node // of a channel edge. ChannelEdgePolicy only contains to destination node
// of the edge. // of the edge.
@ -102,89 +73,6 @@ func isSamePath(path1, path2 []*channeldb.ChannelEdgePolicy) bool {
return true return true
} }
// Route represents a path through the channel graph which runs over one or
// more channels in succession. This struct carries all the information
// required to craft the Sphinx onion packet, and send the payment along the
// first hop in the path. A route is only selected as valid if all the channels
// have sufficient capacity to carry the initial payment amount after fees are
// accounted for.
type Route struct {
// TotalTimeLock is the cumulative (final) time lock across the entire
// route. This is the CLTV value that should be extended to the first
// hop in the route. All other hops will decrement the time-lock as
// advertised, leaving enough time for all hops to wait for or present
// the payment preimage to complete the payment.
TotalTimeLock uint32
// TotalFees is the sum of the fees paid at each hop within the final
// route. In the case of a one-hop payment, this value will be zero as
// we don't need to pay a fee to ourself.
TotalFees lnwire.MilliSatoshi
// TotalAmount is the total amount of funds required to complete a
// payment over this route. This value includes the cumulative fees at
// each hop. As a result, the HTLC extended to the first-hop in the
// route will need to have at least this many satoshis, otherwise the
// route will fail at an intermediate node due to an insufficient
// amount of fees.
TotalAmount lnwire.MilliSatoshi
// SourcePubKey is the pubkey of the node where this route originates
// from.
SourcePubKey Vertex
// Hops contains details concerning the specific forwarding details at
// each hop.
Hops []*Hop
}
// HopFee returns the fee charged by the route hop indicated by hopIndex.
func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
var incomingAmt lnwire.MilliSatoshi
if hopIndex == 0 {
incomingAmt = r.TotalAmount
} else {
incomingAmt = r.Hops[hopIndex-1].AmtToForward
}
// Fee is calculated as difference between incoming and outgoing amount.
return incomingAmt - r.Hops[hopIndex].AmtToForward
}
// ToHopPayloads converts a complete route into the series of per-hop payloads
// that is to be encoded within each HTLC using an opaque Sphinx packet.
func (r *Route) ToHopPayloads() []sphinx.HopData {
hopPayloads := make([]sphinx.HopData, len(r.Hops))
// For each hop encoded within the route, we'll convert the hop struct
// to the matching per-hop payload struct as used by the sphinx
// package.
for i, hop := range r.Hops {
hopPayloads[i] = sphinx.HopData{
// TODO(roasbeef): properly set realm, make sphinx type
// an enum actually?
Realm: 0,
ForwardAmount: uint64(hop.AmtToForward),
OutgoingCltv: hop.OutgoingTimeLock,
}
// As a base case, the next hop is set to all zeroes in order
// to indicate that the "last hop" as no further hops after it.
nextHop := uint64(0)
// If we aren't on the last hop, then we set the "next address"
// field to be the channel that directly follows it.
if i != len(r.Hops)-1 {
nextHop = r.Hops[i+1].ChannelID
}
binary.BigEndian.PutUint64(hopPayloads[i].NextAddress[:],
nextHop)
}
return hopPayloads
}
// newRoute returns a fully valid route between the source and target that's // newRoute returns a fully valid route between the source and target that's
// capable of supporting a payment of `amtToSend` after fees are fully // capable of supporting a payment of `amtToSend` after fees are fully
// computed. If the route is too long, or the selected path cannot support the // computed. If the route is too long, or the selected path cannot support the
@ -192,12 +80,12 @@ func (r *Route) ToHopPayloads() []sphinx.HopData {
// //
// NOTE: The passed slice of ChannelHops MUST be sorted in forward order: from // NOTE: The passed slice of ChannelHops MUST be sorted in forward order: from
// the source to the target node of the path finding attempt. // the source to the target node of the path finding attempt.
func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex Vertex, func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex,
pathEdges []*channeldb.ChannelEdgePolicy, currentHeight uint32, pathEdges []*channeldb.ChannelEdgePolicy, currentHeight uint32,
finalCLTVDelta uint16) (*Route, error) { finalCLTVDelta uint16) (*route.Route, error) {
var ( var (
hops []*Hop hops []*route.Hop
// totalTimeLock will accumulate the cumulative time lock // totalTimeLock will accumulate the cumulative time lock
// across the entire route. This value represents how long the // across the entire route. This value represents how long the
@ -270,13 +158,13 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex Vertex,
// Since we're traversing the path backwards atm, we prepend // Since we're traversing the path backwards atm, we prepend
// each new hop such that, the final slice of hops will be in // each new hop such that, the final slice of hops will be in
// the forwards order. // the forwards order.
currentHop := &Hop{ currentHop := &route.Hop{
PubKeyBytes: Vertex(edge.Node.PubKeyBytes), PubKeyBytes: edge.Node.PubKeyBytes,
ChannelID: edge.ChannelID, ChannelID: edge.ChannelID,
AmtToForward: amtToForward, AmtToForward: amtToForward,
OutgoingTimeLock: outgoingTimeLock, OutgoingTimeLock: outgoingTimeLock,
} }
hops = append([]*Hop{currentHop}, hops...) hops = append([]*route.Hop{currentHop}, hops...)
// Finally, we update the amount that needs to flow into the // Finally, we update the amount that needs to flow into the
// *next* hop, which is the amount this hop needs to forward, // *next* hop, which is the amount this hop needs to forward,
@ -285,8 +173,8 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex Vertex,
} }
// With the base routing data expressed as hops, build the full route // With the base routing data expressed as hops, build the full route
newRoute, err := NewRouteFromHops( newRoute, err := route.NewRouteFromHops(
nextIncomingAmount, totalTimeLock, sourceVertex, hops, nextIncomingAmount, totalTimeLock, route.Vertex(sourceVertex), hops,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -295,49 +183,6 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex Vertex,
return newRoute, nil return newRoute, nil
} }
// NewRouteFromHops creates a new Route structure from the minimally required
// information to perform the payment. It infers fee amounts and populates the
// node, chan and prev/next hop maps.
func NewRouteFromHops(amtToSend lnwire.MilliSatoshi, timeLock uint32,
sourceVertex Vertex, hops []*Hop) (*Route, error) {
if len(hops) == 0 {
return nil, ErrNoRouteHopsProvided
}
// First, we'll create a route struct and populate it with the fields
// for which the values are provided as arguments of this function.
// TotalFees is determined based on the difference between the amount
// that is send from the source and the final amount that is received
// by the destination.
route := &Route{
SourcePubKey: sourceVertex,
Hops: hops,
TotalTimeLock: timeLock,
TotalAmount: amtToSend,
TotalFees: amtToSend - hops[len(hops)-1].AmtToForward,
}
return route, nil
}
// Vertex is a simple alias for the serialization of a compressed Bitcoin
// public key.
type Vertex [33]byte
// NewVertex returns a new Vertex given a public key.
func NewVertex(pub *btcec.PublicKey) Vertex {
var v Vertex
copy(v[:], pub.SerializeCompressed())
return v
}
// String returns a human readable version of the Vertex which is the
// hex-encoding of the serialized compressed public key.
func (v Vertex) String() string {
return fmt.Sprintf("%x", v[:])
}
// edgeWeight computes the weight of an edge. This value is used when searching // edgeWeight computes the weight of an edge. This value is used when searching
// for the shortest path within the channel graph between two nodes. Weight is // for the shortest path within the channel graph between two nodes. Weight is
// is the fee itself plus a time lock penalty added to it. This benefits // is the fee itself plus a time lock penalty added to it. This benefits
@ -368,7 +213,7 @@ type graphParams struct {
// additionalEdges is an optional set of edges that should be // additionalEdges is an optional set of edges that should be
// considered during path finding, that is not already found in the // considered during path finding, that is not already found in the
// channel graph. // channel graph.
additionalEdges map[Vertex][]*channeldb.ChannelEdgePolicy additionalEdges map[route.Vertex][]*channeldb.ChannelEdgePolicy
// bandwidthHints is an optional map from channels to bandwidths that // bandwidthHints is an optional map from channels to bandwidths that
// can be populated if the caller has a better estimate of the current // can be populated if the caller has a better estimate of the current
@ -385,7 +230,7 @@ type graphParams struct {
type RestrictParams struct { type RestrictParams struct {
// IgnoredNodes is an optional set of nodes that should be ignored if // IgnoredNodes is an optional set of nodes that should be ignored if
// encountered during path finding. // encountered during path finding.
IgnoredNodes map[Vertex]struct{} IgnoredNodes map[route.Vertex]struct{}
// IgnoredEdges is an optional set of edges that should be ignored if // IgnoredEdges is an optional set of edges that should be ignored if
// encountered during path finding. // encountered during path finding.
@ -416,7 +261,7 @@ type RestrictParams struct {
// destination node back to source. This is to properly accumulate fees // destination node back to source. This is to properly accumulate fees
// that need to be paid along the path and accurately check the amount // that need to be paid along the path and accurately check the amount
// to forward at every node against the available bandwidth. // to forward at every node against the available bandwidth.
func findPath(g *graphParams, r *RestrictParams, source, target Vertex, func findPath(g *graphParams, r *RestrictParams, source, target route.Vertex,
amt lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, error) { amt lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, error) {
var err error var err error
@ -438,12 +283,12 @@ func findPath(g *graphParams, r *RestrictParams, source, target Vertex,
// for the node set with a distance of "infinity". graph.ForEachNode // for the node set with a distance of "infinity". graph.ForEachNode
// also returns the source node, so there is no need to add the source // also returns the source node, so there is no need to add the source
// node explicitly. // node explicitly.
distance := make(map[Vertex]nodeWithDist) distance := make(map[route.Vertex]nodeWithDist)
if err := g.graph.ForEachNode(tx, func(_ *bbolt.Tx, if err := g.graph.ForEachNode(tx, func(_ *bbolt.Tx,
node *channeldb.LightningNode) error { node *channeldb.LightningNode) error {
// TODO(roasbeef): with larger graph can just use disk seeks // TODO(roasbeef): with larger graph can just use disk seeks
// with a visited map // with a visited map
distance[Vertex(node.PubKeyBytes)] = nodeWithDist{ distance[route.Vertex(node.PubKeyBytes)] = nodeWithDist{
dist: infinity, dist: infinity,
node: node, node: node,
} }
@ -452,7 +297,7 @@ func findPath(g *graphParams, r *RestrictParams, source, target Vertex,
return nil, err return nil, err
} }
additionalEdgesWithSrc := make(map[Vertex][]*edgePolicyWithSource) additionalEdgesWithSrc := make(map[route.Vertex][]*edgePolicyWithSource)
for vertex, outgoingEdgePolicies := range g.additionalEdges { for vertex, outgoingEdgePolicies := range g.additionalEdges {
// We'll also include all the nodes found within the additional // We'll also include all the nodes found within the additional
// edges that are not known to us yet in the distance map. // edges that are not known to us yet in the distance map.
@ -495,7 +340,7 @@ func findPath(g *graphParams, r *RestrictParams, source, target Vertex,
// We'll use this map as a series of "next" hop pointers. So to get // We'll use this map as a series of "next" hop pointers. So to get
// from `Vertex` to the target node, we'll take the edge that it's // from `Vertex` to the target node, we'll take the edge that it's
// mapped to within `next`. // mapped to within `next`.
next := make(map[Vertex]*channeldb.ChannelEdgePolicy) next := make(map[route.Vertex]*channeldb.ChannelEdgePolicy)
ignoredEdges := r.IgnoredEdges ignoredEdges := r.IgnoredEdges
if ignoredEdges == nil { if ignoredEdges == nil {
@ -503,16 +348,16 @@ func findPath(g *graphParams, r *RestrictParams, source, target Vertex,
} }
ignoredNodes := r.IgnoredNodes ignoredNodes := r.IgnoredNodes
if ignoredNodes == nil { if ignoredNodes == nil {
ignoredNodes = make(map[Vertex]struct{}) ignoredNodes = make(map[route.Vertex]struct{})
} }
// processEdge is a helper closure that will be used to make sure edges // processEdge is a helper closure that will be used to make sure edges
// satisfy our specific requirements. // satisfy our specific requirements.
processEdge := func(fromNode *channeldb.LightningNode, processEdge := func(fromNode *channeldb.LightningNode,
edge *channeldb.ChannelEdgePolicy, edge *channeldb.ChannelEdgePolicy,
bandwidth lnwire.MilliSatoshi, toNode Vertex) { bandwidth lnwire.MilliSatoshi, toNode route.Vertex) {
fromVertex := Vertex(fromNode.PubKeyBytes) fromVertex := route.Vertex(fromNode.PubKeyBytes)
// If this is not a local channel and it is disabled, we will // If this is not a local channel and it is disabled, we will
// skip it. // skip it.
@ -674,7 +519,7 @@ func findPath(g *graphParams, r *RestrictParams, source, target Vertex,
// Now that we've found the next potential step to take we'll // Now that we've found the next potential step to take we'll
// examine all the incoming edges (channels) from this node to // examine all the incoming edges (channels) from this node to
// further our graph traversal. // further our graph traversal.
pivot := Vertex(bestNode.PubKeyBytes) pivot := route.Vertex(bestNode.PubKeyBytes)
err := bestNode.ForEachChannel(tx, func(tx *bbolt.Tx, err := bestNode.ForEachChannel(tx, func(tx *bbolt.Tx,
edgeInfo *channeldb.ChannelEdgeInfo, edgeInfo *channeldb.ChannelEdgeInfo,
_, inEdge *channeldb.ChannelEdgePolicy) error { _, inEdge *channeldb.ChannelEdgePolicy) error {
@ -756,7 +601,7 @@ func findPath(g *graphParams, r *RestrictParams, source, target Vertex,
pathEdges = append(pathEdges, nextNode) pathEdges = append(pathEdges, nextNode)
// Advance current node. // Advance current node.
currentNode = Vertex(nextNode.Node.PubKeyBytes) currentNode = route.Vertex(nextNode.Node.PubKeyBytes)
} }
// The route is invalid if it spans more than 20 hops. The current // The route is invalid if it spans more than 20 hops. The current
@ -784,7 +629,7 @@ func findPath(g *graphParams, r *RestrictParams, source, target Vertex,
// algorithm, rather than attempting to use an unmodified path finding // algorithm, rather than attempting to use an unmodified path finding
// algorithm in a block box manner. // algorithm in a block box manner.
func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph, func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
source, target Vertex, amt lnwire.MilliSatoshi, source, target route.Vertex, amt lnwire.MilliSatoshi,
restrictions *RestrictParams, numPaths uint32, restrictions *RestrictParams, numPaths uint32,
bandwidthHints map[uint64]lnwire.MilliSatoshi) ( bandwidthHints map[uint64]lnwire.MilliSatoshi) (
[][]*channeldb.ChannelEdgePolicy, error) { [][]*channeldb.ChannelEdgePolicy, error) {
@ -837,7 +682,7 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
// These are required to ensure the paths are unique // These are required to ensure the paths are unique
// and loopless. // and loopless.
ignoredEdges := make(map[EdgeLocator]struct{}) ignoredEdges := make(map[EdgeLocator]struct{})
ignoredVertexes := make(map[Vertex]struct{}) ignoredVertexes := make(map[route.Vertex]struct{})
for e := range restrictions.IgnoredEdges { for e := range restrictions.IgnoredEdges {
ignoredEdges[e] = struct{}{} ignoredEdges[e] = struct{}{}
@ -878,7 +723,7 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
continue continue
} }
ignoredVertexes[Vertex(node)] = struct{}{} ignoredVertexes[route.Vertex(node)] = struct{}{}
} }
// With the edges that are part of our root path, and // With the edges that are part of our root path, and

View File

@ -23,6 +23,7 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32" "github.com/lightningnetwork/lnd/zpay32"
) )
@ -164,7 +165,7 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
return nil, err return nil, err
} }
aliasMap := make(map[string]Vertex) aliasMap := make(map[string]route.Vertex)
var source *channeldb.LightningNode var source *channeldb.LightningNode
// First we insert all the nodes within the graph as vertexes. // First we insert all the nodes within the graph as vertexes.
@ -366,7 +367,7 @@ type testGraphInstance struct {
// aliasMap is a map from a node's alias to its public key. This type is // 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 // provided in order to allow easily look up from the human memorable alias
// to an exact node's public key. // to an exact node's public key.
aliasMap map[string]Vertex aliasMap map[string]route.Vertex
// privKeyMap maps a node alias to its private key. This is used to be // privKeyMap maps a node alias to its private key. This is used to be
// able to mock a remote node's signing behaviour. // able to mock a remote node's signing behaviour.
@ -395,7 +396,7 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
return nil, err return nil, err
} }
aliasMap := make(map[string]Vertex) aliasMap := make(map[string]route.Vertex)
privKeyMap := make(map[string]*btcec.PrivateKey) privKeyMap := make(map[string]*btcec.PrivateKey)
nodeIndex := byte(0) nodeIndex := byte(0)
@ -611,7 +612,7 @@ func TestFindLowestFeePath(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to fetch source node: %v", err) t.Fatalf("unable to fetch source node: %v", err)
} }
sourceVertex := Vertex(sourceNode.PubKeyBytes) sourceVertex := route.Vertex(sourceNode.PubKeyBytes)
const ( const (
startingHeight = 100 startingHeight = 100
@ -648,8 +649,8 @@ func TestFindLowestFeePath(t *testing.T) {
} }
} }
func getAliasFromPubKey(pubKey Vertex, func getAliasFromPubKey(pubKey route.Vertex,
aliases map[string]Vertex) string { aliases map[string]route.Vertex) string {
for alias, key := range aliases { for alias, key := range aliases {
if key == pubKey { if key == pubKey {
@ -751,7 +752,7 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
if err != nil { if err != nil {
t.Fatalf("unable to fetch source node: %v", err) t.Fatalf("unable to fetch source node: %v", err)
} }
sourceVertex := Vertex(sourceNode.PubKeyBytes) sourceVertex := route.Vertex(sourceNode.PubKeyBytes)
const ( const (
startingHeight = 100 startingHeight = 100
@ -920,7 +921,7 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
TimeLockDelta: 9, TimeLockDelta: 9,
} }
additionalEdges := map[Vertex][]*channeldb.ChannelEdgePolicy{ additionalEdges := map[route.Vertex][]*channeldb.ChannelEdgePolicy{
graph.aliasMap["songoku"]: {songokuToDoge}, graph.aliasMap["songoku"]: {songokuToDoge},
} }
@ -1006,7 +1007,7 @@ func TestKShortestPathFinding(t *testing.T) {
func TestNewRoute(t *testing.T) { func TestNewRoute(t *testing.T) {
var sourceKey [33]byte var sourceKey [33]byte
sourceVertex := Vertex(sourceKey) sourceVertex := route.Vertex(sourceKey)
const ( const (
startingHeight = 100 startingHeight = 100
@ -1148,7 +1149,7 @@ func TestNewRoute(t *testing.T) {
}} }}
for _, testCase := range testCases { for _, testCase := range testCases {
assertRoute := func(t *testing.T, route *Route) { assertRoute := func(t *testing.T, route *route.Route) {
if route.TotalAmount != testCase.expectedTotalAmount { if route.TotalAmount != testCase.expectedTotalAmount {
t.Errorf("Expected total amount is be %v"+ t.Errorf("Expected total amount is be %v"+
", but got %v instead", ", but got %v instead",
@ -1294,7 +1295,7 @@ func TestPathNotAvailable(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to parse bytes: %v", err) t.Fatalf("unable to parse bytes: %v", err)
} }
var unknownNode Vertex var unknownNode route.Vertex
copy(unknownNode[:], unknownNodeBytes) copy(unknownNode[:], unknownNodeBytes)
_, err = findPath( _, err = findPath(
@ -1923,7 +1924,7 @@ func TestPathFindSpecExample(t *testing.T) {
} }
} }
func assertExpectedPath(t *testing.T, aliasMap map[string]Vertex, func assertExpectedPath(t *testing.T, aliasMap map[string]route.Vertex,
path []*channeldb.ChannelEdgePolicy, nodeAliases ...string) { path []*channeldb.ChannelEdgePolicy, nodeAliases ...string) {
if len(path) != len(nodeAliases) { if len(path) != len(nodeAliases) {
@ -1943,9 +1944,9 @@ func assertExpectedPath(t *testing.T, aliasMap map[string]Vertex,
func TestNewRouteFromEmptyHops(t *testing.T) { func TestNewRouteFromEmptyHops(t *testing.T) {
t.Parallel() t.Parallel()
var source Vertex var source route.Vertex
_, err := NewRouteFromHops(0, 0, source, []*Hop{}) _, err := route.NewRouteFromHops(0, 0, source, []*route.Hop{})
if err != ErrNoRouteHopsProvided { if err != route.ErrNoRouteHopsProvided {
t.Fatalf("expected empty hops error: instead got: %v", err) t.Fatalf("expected empty hops error: instead got: %v", err)
} }
} }
@ -1995,7 +1996,7 @@ func TestRestrictOutgoingChannel(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to fetch source node: %v", err) t.Fatalf("unable to fetch source node: %v", err)
} }
sourceVertex := Vertex(sourceNode.PubKeyBytes) sourceVertex := route.Vertex(sourceNode.PubKeyBytes)
const ( const (
startingHeight = 100 startingHeight = 100
@ -2089,10 +2090,10 @@ func testCltvLimit(t *testing.T, limit uint32, expectedChannel uint64) {
if err != nil { if err != nil {
t.Fatalf("unable to fetch source node: %v", err) t.Fatalf("unable to fetch source node: %v", err)
} }
sourceVertex := Vertex(sourceNode.PubKeyBytes) sourceVertex := route.Vertex(sourceNode.PubKeyBytes)
ignoredEdges := make(map[EdgeLocator]struct{}) ignoredEdges := make(map[EdgeLocator]struct{})
ignoredVertexes := make(map[Vertex]struct{}) ignoredVertexes := make(map[route.Vertex]struct{})
paymentAmt := lnwire.NewMSatFromSatoshis(100) paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := testGraphInstance.aliasMap["target"] target := testGraphInstance.aliasMap["target"]

View File

@ -6,6 +6,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
) )
// paymentSession is used during an HTLC routings session to prune the local // paymentSession is used during an HTLC routings session to prune the local
@ -19,7 +20,7 @@ import (
type paymentSession struct { type paymentSession struct {
pruneViewSnapshot graphPruneView pruneViewSnapshot graphPruneView
additionalEdges map[Vertex][]*channeldb.ChannelEdgePolicy additionalEdges map[route.Vertex][]*channeldb.ChannelEdgePolicy
bandwidthHints map[uint64]lnwire.MilliSatoshi bandwidthHints map[uint64]lnwire.MilliSatoshi
@ -32,7 +33,7 @@ type paymentSession struct {
mc *missionControl mc *missionControl
haveRoutes bool haveRoutes bool
preBuiltRoutes []*Route preBuiltRoutes []*route.Route
pathFinder pathFinder pathFinder pathFinder
} }
@ -42,7 +43,7 @@ type paymentSession struct {
// added is noted, as it'll be pruned from the shared view after a period of // added is noted, as it'll be pruned from the shared view after a period of
// vertexDecay. However, the vertex will remain pruned for the *local* session. // vertexDecay. However, the vertex will remain pruned for the *local* session.
// This ensures we don't retry this vertex during the payment attempt. // This ensures we don't retry this vertex during the payment attempt.
func (p *paymentSession) ReportVertexFailure(v Vertex) { func (p *paymentSession) ReportVertexFailure(v route.Vertex) {
log.Debugf("Reporting vertex %v failure to Mission Control", v) log.Debugf("Reporting vertex %v failure to Mission Control", v)
// First, we'll add the failed vertex to our local prune view snapshot. // First, we'll add the failed vertex to our local prune view snapshot.
@ -84,7 +85,7 @@ func (p *paymentSession) ReportEdgeFailure(e *EdgeLocator) {
// pruned. This is to prevent nodes from keeping us busy by continuously sending // pruned. This is to prevent nodes from keeping us busy by continuously sending
// new channel updates. // new channel updates.
func (p *paymentSession) ReportEdgePolicyFailure( func (p *paymentSession) ReportEdgePolicyFailure(
errSource Vertex, failedEdge *EdgeLocator) { errSource route.Vertex, failedEdge *EdgeLocator) {
// Check to see if we've already reported a policy related failure for // Check to see if we've already reported a policy related failure for
// this channel. If so, then we'll prune out the vertex. // this channel. If so, then we'll prune out the vertex.
@ -111,7 +112,7 @@ func (p *paymentSession) ReportEdgePolicyFailure(
// //
// NOTE: This function is safe for concurrent access. // NOTE: This function is safe for concurrent access.
func (p *paymentSession) RequestRoute(payment *LightningPayment, func (p *paymentSession) RequestRoute(payment *LightningPayment,
height uint32, finalCltvDelta uint16) (*Route, error) { height uint32, finalCltvDelta uint16) (*route.Route, error) {
switch { switch {
// If we have a set of pre-built routes, then we'll just pop off the // If we have a set of pre-built routes, then we'll just pop off the
@ -175,7 +176,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
// With the next candidate path found, we'll attempt to turn this into // With the next candidate path found, we'll attempt to turn this into
// a route by applying the time-lock and fee requirements. // a route by applying the time-lock and fee requirements.
sourceVertex := Vertex(p.mc.selfNode.PubKeyBytes) sourceVertex := route.Vertex(p.mc.selfNode.PubKeyBytes)
route, err := newRoute( route, err := newRoute(
payment.Amount, sourceVertex, path, height, finalCltvDelta, payment.Amount, sourceVertex, path, height, finalCltvDelta,
) )

View File

@ -5,6 +5,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
) )
func TestRequestRoute(t *testing.T) { func TestRequestRoute(t *testing.T) {
@ -13,7 +14,7 @@ func TestRequestRoute(t *testing.T) {
) )
findPath := func(g *graphParams, r *RestrictParams, findPath := func(g *graphParams, r *RestrictParams,
source, target Vertex, amt lnwire.MilliSatoshi) ( source, target route.Vertex, amt lnwire.MilliSatoshi) (
[]*channeldb.ChannelEdgePolicy, error) { []*channeldb.ChannelEdgePolicy, error) {
// We expect find path to receive a cltv limit excluding the // We expect find path to receive a cltv limit excluding the

165
routing/route/route.go Normal file
View File

@ -0,0 +1,165 @@
package route
import (
"encoding/binary"
"fmt"
"github.com/btcsuite/btcd/btcec"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/lnwire"
)
// ErrNoRouteHopsProvided is returned when a caller attempts to construct a new
// sphinx packet, but provides an empty set of hops for each route.
var ErrNoRouteHopsProvided = fmt.Errorf("empty route hops provided")
// Vertex is a simple alias for the serialization of a compressed Bitcoin
// public key.
type Vertex [33]byte
// NewVertex returns a new Vertex given a public key.
func NewVertex(pub *btcec.PublicKey) Vertex {
var v Vertex
copy(v[:], pub.SerializeCompressed())
return v
}
// String returns a human readable version of the Vertex which is the
// hex-encoding of the serialized compressed public key.
func (v Vertex) String() string {
return fmt.Sprintf("%x", v[:])
}
// Hop represents an intermediate or final node of the route. This naming
// is in line with the definition given in BOLT #4: Onion Routing Protocol.
// The struct houses the channel along which this hop can be reached and
// the values necessary to create the HTLC that needs to be sent to the
// next hop. It is also used to encode the per-hop payload included within
// the Sphinx packet.
type Hop struct {
// PubKeyBytes is the raw bytes of the public key of the target node.
PubKeyBytes Vertex
// ChannelID is the unique channel ID for the channel. The first 3
// bytes are the block height, the next 3 the index within the block,
// and the last 2 bytes are the output index for the channel.
ChannelID uint64
// OutgoingTimeLock is the timelock value that should be used when
// crafting the _outgoing_ HTLC from this hop.
OutgoingTimeLock uint32
// AmtToForward is the amount that this hop will forward to the next
// hop. This value is less than the value that the incoming HTLC
// carries as a fee will be subtracted by the hop.
AmtToForward lnwire.MilliSatoshi
}
// Route represents a path through the channel graph which runs over one or
// more channels in succession. This struct carries all the information
// required to craft the Sphinx onion packet, and send the payment along the
// first hop in the path. A route is only selected as valid if all the channels
// have sufficient capacity to carry the initial payment amount after fees are
// accounted for.
type Route struct {
// TotalTimeLock is the cumulative (final) time lock across the entire
// route. This is the CLTV value that should be extended to the first
// hop in the route. All other hops will decrement the time-lock as
// advertised, leaving enough time for all hops to wait for or present
// the payment preimage to complete the payment.
TotalTimeLock uint32
// TotalFees is the sum of the fees paid at each hop within the final
// route. In the case of a one-hop payment, this value will be zero as
// we don't need to pay a fee to ourself.
TotalFees lnwire.MilliSatoshi
// TotalAmount is the total amount of funds required to complete a
// payment over this route. This value includes the cumulative fees at
// each hop. As a result, the HTLC extended to the first-hop in the
// route will need to have at least this many satoshis, otherwise the
// route will fail at an intermediate node due to an insufficient
// amount of fees.
TotalAmount lnwire.MilliSatoshi
// SourcePubKey is the pubkey of the node where this route originates
// from.
SourcePubKey Vertex
// Hops contains details concerning the specific forwarding details at
// each hop.
Hops []*Hop
}
// HopFee returns the fee charged by the route hop indicated by hopIndex.
func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
var incomingAmt lnwire.MilliSatoshi
if hopIndex == 0 {
incomingAmt = r.TotalAmount
} else {
incomingAmt = r.Hops[hopIndex-1].AmtToForward
}
// Fee is calculated as difference between incoming and outgoing amount.
return incomingAmt - r.Hops[hopIndex].AmtToForward
}
// ToHopPayloads converts a complete route into the series of per-hop payloads
// that is to be encoded within each HTLC using an opaque Sphinx packet.
func (r *Route) ToHopPayloads() []sphinx.HopData {
hopPayloads := make([]sphinx.HopData, len(r.Hops))
// For each hop encoded within the route, we'll convert the hop struct
// to the matching per-hop payload struct as used by the sphinx
// package.
for i, hop := range r.Hops {
hopPayloads[i] = sphinx.HopData{
// TODO(roasbeef): properly set realm, make sphinx type
// an enum actually?
Realm: 0,
ForwardAmount: uint64(hop.AmtToForward),
OutgoingCltv: hop.OutgoingTimeLock,
}
// As a base case, the next hop is set to all zeroes in order
// to indicate that the "last hop" as no further hops after it.
nextHop := uint64(0)
// If we aren't on the last hop, then we set the "next address"
// field to be the channel that directly follows it.
if i != len(r.Hops)-1 {
nextHop = r.Hops[i+1].ChannelID
}
binary.BigEndian.PutUint64(hopPayloads[i].NextAddress[:],
nextHop)
}
return hopPayloads
}
// NewRouteFromHops creates a new Route structure from the minimally required
// information to perform the payment. It infers fee amounts and populates the
// node, chan and prev/next hop maps.
func NewRouteFromHops(amtToSend lnwire.MilliSatoshi, timeLock uint32,
sourceVertex Vertex, hops []*Hop) (*Route, error) {
if len(hops) == 0 {
return nil, ErrNoRouteHopsProvided
}
// First, we'll create a route struct and populate it with the fields
// for which the values are provided as arguments of this function.
// TotalFees is determined based on the difference between the amount
// that is send from the source and the final amount that is received
// by the destination.
route := &Route{
SourcePubKey: sourceVertex,
Hops: hops,
TotalTimeLock: timeLock,
TotalAmount: amtToSend,
TotalFees: amtToSend - hops[len(hops)-1].AmtToForward,
}
return route, nil
}

View File

@ -25,6 +25,7 @@ import (
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/multimutex" "github.com/lightningnetwork/lnd/multimutex"
"github.com/lightningnetwork/lnd/routing/chainview" "github.com/lightningnetwork/lnd/routing/chainview"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32" "github.com/lightningnetwork/lnd/zpay32"
) )
@ -77,11 +78,11 @@ type ChannelGraphSource interface {
// for the target node with a more recent timestamp. This method will // for the target node with a more recent timestamp. This method will
// also return true if we don't have an active channel announcement for // also return true if we don't have an active channel announcement for
// the target node. // the target node.
IsStaleNode(node Vertex, timestamp time.Time) bool IsStaleNode(node route.Vertex, timestamp time.Time) bool
// IsPublicNode determines whether the given vertex is seen as a public // IsPublicNode determines whether the given vertex is seen as a public
// node in the graph from the graph's source node's point of view. // node in the graph from the graph's source node's point of view.
IsPublicNode(node Vertex) (bool, error) IsPublicNode(node route.Vertex) (bool, error)
// IsKnownEdge returns true if the graph source already knows of the // IsKnownEdge returns true if the graph source already knows of the
// passed channel ID either as a live or zombie edge. // passed channel ID either as a live or zombie edge.
@ -114,7 +115,7 @@ type ChannelGraphSource interface {
// FetchLightningNode attempts to look up a target node by its identity // FetchLightningNode attempts to look up a target node by its identity
// public key. channeldb.ErrGraphNodeNotFound is returned if the node // public key. channeldb.ErrGraphNodeNotFound is returned if the node
// doesn't exist within the graph. // doesn't exist within the graph.
FetchLightningNode(Vertex) (*channeldb.LightningNode, error) FetchLightningNode(route.Vertex) (*channeldb.LightningNode, error)
// ForEachNode is used to iterate over every node in the known graph. // ForEachNode is used to iterate over every node in the known graph.
ForEachNode(func(node *channeldb.LightningNode) error) error ForEachNode(func(node *channeldb.LightningNode) error) error
@ -236,7 +237,7 @@ type EdgeLocator struct {
// newEdgeLocatorByPubkeys returns an edgeLocator based on its end point // newEdgeLocatorByPubkeys returns an edgeLocator based on its end point
// pubkeys. // pubkeys.
func newEdgeLocatorByPubkeys(channelID uint64, fromNode, toNode *Vertex) *EdgeLocator { func newEdgeLocatorByPubkeys(channelID uint64, fromNode, toNode *route.Vertex) *EdgeLocator {
// Determine direction based on lexicographical ordering of both // Determine direction based on lexicographical ordering of both
// pubkeys. // pubkeys.
var direction uint8 var direction uint8
@ -973,7 +974,7 @@ func (r *ChannelRouter) networkHandler() {
// timestamp. ErrIgnored will be returned if we already have the node, and // timestamp. ErrIgnored will be returned if we already have the node, and
// ErrOutdated will be returned if we have a timestamp that's after the new // ErrOutdated will be returned if we have a timestamp that's after the new
// timestamp. // timestamp.
func (r *ChannelRouter) assertNodeAnnFreshness(node Vertex, func (r *ChannelRouter) assertNodeAnnFreshness(node route.Vertex,
msgTimestamp time.Time) error { msgTimestamp time.Time) error {
// If we are not already aware of this node, it means that we don't // If we are not already aware of this node, it means that we don't
@ -1307,11 +1308,11 @@ type routingMsg struct {
// fee information attached. The set of routes returned may be less than the // fee information attached. The set of routes returned may be less than the
// initial set of paths as it's possible we drop a route if it can't handle the // initial set of paths as it's possible we drop a route if it can't handle the
// total payment flow after fees are calculated. // total payment flow after fees are calculated.
func pathsToFeeSortedRoutes(source Vertex, paths [][]*channeldb.ChannelEdgePolicy, func pathsToFeeSortedRoutes(source route.Vertex, paths [][]*channeldb.ChannelEdgePolicy,
finalCLTVDelta uint16, amt lnwire.MilliSatoshi, finalCLTVDelta uint16, amt lnwire.MilliSatoshi,
currentHeight uint32) ([]*Route, error) { currentHeight uint32) ([]*route.Route, error) {
validRoutes := make([]*Route, 0, len(paths)) validRoutes := make([]*route.Route, 0, len(paths))
for _, path := range paths { for _, path := range paths {
// Attempt to make the path into a route. We snip off the first // Attempt to make the path into a route. We snip off the first
// hop in the path as it contains a "self-hop" that is inserted // hop in the path as it contains a "self-hop" that is inserted
@ -1365,9 +1366,9 @@ func pathsToFeeSortedRoutes(source Vertex, paths [][]*channeldb.ChannelEdgePolic
// the required fee and time lock values running backwards along the route. The // the required fee and time lock values running backwards along the route. The
// route that will be ranked the highest is the one with the lowest cumulative // route that will be ranked the highest is the one with the lowest cumulative
// fee along the route. // fee along the route.
func (r *ChannelRouter) FindRoutes(source, target Vertex, func (r *ChannelRouter) FindRoutes(source, target route.Vertex,
amt lnwire.MilliSatoshi, restrictions *RestrictParams, numPaths uint32, amt lnwire.MilliSatoshi, restrictions *RestrictParams, numPaths uint32,
finalExpiry ...uint16) ([]*Route, error) { finalExpiry ...uint16) ([]*route.Route, error) {
var finalCLTVDelta uint16 var finalCLTVDelta uint16
if len(finalExpiry) == 0 { if len(finalExpiry) == 0 {
@ -1434,7 +1435,7 @@ func (r *ChannelRouter) FindRoutes(source, target Vertex,
// each path. During this process, some paths may be discarded if they // each path. During this process, some paths may be discarded if they
// aren't able to support the total satoshis flow once fees have been // aren't able to support the total satoshis flow once fees have been
// factored in. // factored in.
sourceVertex := Vertex(r.selfNode.PubKeyBytes) sourceVertex := route.Vertex(r.selfNode.PubKeyBytes)
validRoutes, err := pathsToFeeSortedRoutes( validRoutes, err := pathsToFeeSortedRoutes(
sourceVertex, shortestPaths, finalCLTVDelta, amt, sourceVertex, shortestPaths, finalCLTVDelta, amt,
uint32(currentHeight), uint32(currentHeight),
@ -1456,20 +1457,20 @@ func (r *ChannelRouter) FindRoutes(source, target Vertex,
// the onion route specified by the passed layer 3 route. The blob returned // the onion route specified by the passed layer 3 route. The blob returned
// from this function can immediately be included within an HTLC add packet to // from this function can immediately be included within an HTLC add packet to
// be sent to the first hop within the route. // be sent to the first hop within the route.
func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte, func generateSphinxPacket(rt *route.Route, paymentHash []byte) ([]byte,
*sphinx.Circuit, error) { *sphinx.Circuit, error) {
// As a sanity check, we'll ensure that the set of hops has been // As a sanity check, we'll ensure that the set of hops has been
// properly filled in, otherwise, we won't actually be able to // properly filled in, otherwise, we won't actually be able to
// construct a route. // construct a route.
if len(route.Hops) == 0 { if len(rt.Hops) == 0 {
return nil, nil, ErrNoRouteHopsProvided return nil, nil, route.ErrNoRouteHopsProvided
} }
// First obtain all the public keys along the route which are contained // First obtain all the public keys along the route which are contained
// in each hop. // in each hop.
nodes := make([]*btcec.PublicKey, len(route.Hops)) nodes := make([]*btcec.PublicKey, len(rt.Hops))
for i, hop := range route.Hops { for i, hop := range rt.Hops {
pub, err := btcec.ParsePubKey(hop.PubKeyBytes[:], pub, err := btcec.ParsePubKey(hop.PubKeyBytes[:],
btcec.S256()) btcec.S256())
if err != nil { if err != nil {
@ -1482,7 +1483,7 @@ func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte,
// Next we generate the per-hop payload which gives each node within // Next we generate the per-hop payload which gives each node within
// the route the necessary information (fees, CLTV value, etc) to // the route the necessary information (fees, CLTV value, etc) to
// properly forward the payment. // properly forward the payment.
hopPayloads := route.ToHopPayloads() hopPayloads := rt.ToHopPayloads()
log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v", log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
paymentHash[:], newLogClosure(func() string { paymentHash[:], newLogClosure(func() string {
@ -1530,7 +1531,7 @@ func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte,
// final destination. // final destination.
type LightningPayment struct { type LightningPayment struct {
// Target is the node in which the payment should be routed towards. // Target is the node in which the payment should be routed towards.
Target Vertex Target route.Vertex
// Amount is the value of the payment to send through the network in // Amount is the value of the payment to send through the network in
// milli-satoshis. // milli-satoshis.
@ -1586,7 +1587,7 @@ type LightningPayment struct {
// will be returned which describes the path the successful payment traversed // will be returned which describes the path the successful payment traversed
// within the network to reach the destination. Additionally, the payment // within the network to reach the destination. Additionally, the payment
// preimage will also be returned. // preimage will also be returned.
func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route, error) { func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *route.Route, error) {
// Before starting the HTLC routing attempt, we'll create a fresh // Before starting the HTLC routing attempt, we'll create a fresh
// payment session which will report our errors back to mission // payment session which will report our errors back to mission
// control. // control.
@ -1607,8 +1608,8 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route
// succeeds, then a non-nil Route will be returned which describes the // succeeds, then a non-nil Route will be returned which describes the
// path the successful payment traversed within the network to reach the // path the successful payment traversed within the network to reach the
// destination. Additionally, the payment preimage will also be returned. // destination. Additionally, the payment preimage will also be returned.
func (r *ChannelRouter) SendToRoute(routes []*Route, func (r *ChannelRouter) SendToRoute(routes []*route.Route,
payment *LightningPayment) ([32]byte, *Route, error) { payment *LightningPayment) ([32]byte, *route.Route, error) {
paySession := r.missionControl.NewPaymentSessionFromRoutes( paySession := r.missionControl.NewPaymentSessionFromRoutes(
routes, routes,
@ -1625,7 +1626,7 @@ func (r *ChannelRouter) SendToRoute(routes []*Route,
// within the network to reach the destination. Additionally, the payment // within the network to reach the destination. Additionally, the payment
// preimage will also be returned. // preimage will also be returned.
func (r *ChannelRouter) sendPayment(payment *LightningPayment, func (r *ChannelRouter) sendPayment(payment *LightningPayment,
paySession *paymentSession) ([32]byte, *Route, error) { paySession *paymentSession) ([32]byte, *route.Route, error) {
log.Tracef("Dispatching route for lightning payment: %v", log.Tracef("Dispatching route for lightning payment: %v",
newLogClosure(func() string { newLogClosure(func() string {
@ -1719,7 +1720,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
// bool parameter indicates whether this is a final outcome or more attempts // bool parameter indicates whether this is a final outcome or more attempts
// should be made. // should be made.
func (r *ChannelRouter) sendPaymentAttempt(paySession *paymentSession, func (r *ChannelRouter) sendPaymentAttempt(paySession *paymentSession,
route *Route, paymentHash [32]byte) ([32]byte, bool, error) { route *route.Route, paymentHash [32]byte) ([32]byte, bool, error) {
log.Tracef("Attempting to send payment %x, using route: %v", log.Tracef("Attempting to send payment %x, using route: %v",
paymentHash, newLogClosure(func() string { paymentHash, newLogClosure(func() string {
@ -1742,7 +1743,7 @@ func (r *ChannelRouter) sendPaymentAttempt(paySession *paymentSession,
// sendToSwitch sends a payment along the specified route and returns the // sendToSwitch sends a payment along the specified route and returns the
// obtained preimage. // obtained preimage.
func (r *ChannelRouter) sendToSwitch(route *Route, paymentHash [32]byte) ( func (r *ChannelRouter) sendToSwitch(route *route.Route, paymentHash [32]byte) (
[32]byte, error) { [32]byte, error) {
// Generate the raw encoded sphinx packet to be included along // Generate the raw encoded sphinx packet to be included along
@ -1782,7 +1783,7 @@ func (r *ChannelRouter) sendToSwitch(route *Route, paymentHash [32]byte) (
// to continue with an alternative route. This is indicated by the boolean // to continue with an alternative route. This is indicated by the boolean
// return value. // return value.
func (r *ChannelRouter) processSendError(paySession *paymentSession, func (r *ChannelRouter) processSendError(paySession *paymentSession,
route *Route, err error) bool { rt *route.Route, err error) bool {
fErr, ok := err.(*htlcswitch.ForwardingError) fErr, ok := err.(*htlcswitch.ForwardingError)
if !ok { if !ok {
@ -1790,13 +1791,13 @@ func (r *ChannelRouter) processSendError(paySession *paymentSession,
} }
errSource := fErr.ErrorSource errSource := fErr.ErrorSource
errVertex := NewVertex(errSource) errVertex := route.NewVertex(errSource)
log.Tracef("node=%x reported failure when sending htlc", errVertex) log.Tracef("node=%x reported failure when sending htlc", errVertex)
// Always determine chan id ourselves, because a channel // Always determine chan id ourselves, because a channel
// update with id may not be available. // update with id may not be available.
failedEdge, err := getFailedEdge(route, errVertex) failedEdge, err := getFailedEdge(rt, route.Vertex(errVertex))
if err != nil { if err != nil {
return true return true
} }
@ -1833,7 +1834,7 @@ func (r *ChannelRouter) processSendError(paySession *paymentSession,
} }
paySession.ReportEdgePolicyFailure( paySession.ReportEdgePolicyFailure(
NewVertex(errSource), failedEdge, route.NewVertex(errSource), failedEdge,
) )
} }
@ -2007,7 +2008,7 @@ func (r *ChannelRouter) processSendError(paySession *paymentSession,
// getFailedEdge tries to locate the failing channel given a route and the // getFailedEdge tries to locate the failing channel given a route and the
// pubkey of the node that sent the error. It will assume that the error is // pubkey of the node that sent the error. It will assume that the error is
// associated with the outgoing channel of the error node. // associated with the outgoing channel of the error node.
func getFailedEdge(route *Route, errSource Vertex) ( func getFailedEdge(route *route.Route, errSource route.Vertex) (
*EdgeLocator, error) { *EdgeLocator, error) {
hopCount := len(route.Hops) hopCount := len(route.Hops)
@ -2179,7 +2180,7 @@ func (r *ChannelRouter) GetChannelByID(chanID lnwire.ShortChannelID) (
// within the graph. // within the graph.
// //
// NOTE: This method is part of the ChannelGraphSource interface. // NOTE: This method is part of the ChannelGraphSource interface.
func (r *ChannelRouter) FetchLightningNode(node Vertex) (*channeldb.LightningNode, error) { func (r *ChannelRouter) FetchLightningNode(node route.Vertex) (*channeldb.LightningNode, error) {
pubKey, err := btcec.ParsePubKey(node[:], btcec.S256()) pubKey, err := btcec.ParsePubKey(node[:], btcec.S256())
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse raw public key: %v", err) return nil, fmt.Errorf("unable to parse raw public key: %v", err)
@ -2244,7 +2245,7 @@ func (r *ChannelRouter) AddProof(chanID lnwire.ShortChannelID,
// target node with a more recent timestamp. // target node with a more recent timestamp.
// //
// NOTE: This method is part of the ChannelGraphSource interface. // NOTE: This method is part of the ChannelGraphSource interface.
func (r *ChannelRouter) IsStaleNode(node Vertex, timestamp time.Time) bool { func (r *ChannelRouter) IsStaleNode(node route.Vertex, timestamp time.Time) bool {
// If our attempt to assert that the node announcement is fresh fails, // If our attempt to assert that the node announcement is fresh fails,
// then we know that this is actually a stale announcement. // then we know that this is actually a stale announcement.
return r.assertNodeAnnFreshness(node, timestamp) != nil return r.assertNodeAnnFreshness(node, timestamp) != nil
@ -2254,7 +2255,7 @@ func (r *ChannelRouter) IsStaleNode(node Vertex, timestamp time.Time) bool {
// the graph from the graph's source node's point of view. // the graph from the graph's source node's point of view.
// //
// NOTE: This method is part of the ChannelGraphSource interface. // NOTE: This method is part of the ChannelGraphSource interface.
func (r *ChannelRouter) IsPublicNode(node Vertex) (bool, error) { func (r *ChannelRouter) IsPublicNode(node route.Vertex) (bool, error) {
return r.cfg.Graph.IsPublicNode(node) return r.cfg.Graph.IsPublicNode(node)
} }

View File

@ -19,6 +19,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32" "github.com/lightningnetwork/lnd/zpay32"
) )
@ -31,7 +32,7 @@ type testCtx struct {
graph *channeldb.ChannelGraph graph *channeldb.ChannelGraph
aliases map[string]Vertex aliases map[string]route.Vertex
chain *mockChain chain *mockChain
@ -415,7 +416,7 @@ func TestChannelUpdateValidation(t *testing.T) {
hop2 := ctx.aliases["c"] hop2 := ctx.aliases["c"]
hops := []*Hop{ hops := []*route.Hop{
{ {
ChannelID: 1, ChannelID: 1,
PubKeyBytes: hop1, PubKeyBytes: hop1,
@ -426,7 +427,7 @@ func TestChannelUpdateValidation(t *testing.T) {
}, },
} }
route, err := NewRouteFromHops( rt, err := route.NewRouteFromHops(
lnwire.MilliSatoshi(10000), 100, lnwire.MilliSatoshi(10000), 100,
ctx.aliases["a"], hops, ctx.aliases["a"], hops,
) )
@ -473,7 +474,7 @@ func TestChannelUpdateValidation(t *testing.T) {
// Send off the payment request to the router. The specified route // Send off the payment request to the router. The specified route
// should be attempted and the channel update should be received by // should be attempted and the channel update should be received by
// router and ignored because it is missing a valid signature. // router and ignored because it is missing a valid signature.
_, _, err = ctx.router.SendToRoute([]*Route{route}, payment) _, _, err = ctx.router.SendToRoute([]*route.Route{rt}, payment)
if err == nil { if err == nil {
t.Fatalf("expected route to fail with channel update") t.Fatalf("expected route to fail with channel update")
} }
@ -506,7 +507,7 @@ func TestChannelUpdateValidation(t *testing.T) {
} }
// Retry the payment using the same route as before. // Retry the payment using the same route as before.
_, _, err = ctx.router.SendToRoute([]*Route{route}, payment) _, _, err = ctx.router.SendToRoute([]*route.Route{rt}, payment)
if err == nil { if err == nil {
t.Fatalf("expected route to fail with channel update") t.Fatalf("expected route to fail with channel update")
} }
@ -718,7 +719,7 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
// assertExpectedPath is a helper function that asserts the returned // assertExpectedPath is a helper function that asserts the returned
// route properly routes around the failure we've introduced in the // route properly routes around the failure we've introduced in the
// graph. // graph.
assertExpectedPath := func(retPreImage [32]byte, route *Route) { assertExpectedPath := func(retPreImage [32]byte, route *route.Route) {
// The route selected should have two hops // The route selected should have two hops
if len(route.Hops) != 2 { if len(route.Hops) != 2 {
t.Fatalf("incorrect route length: expected %v got %v", 2, t.Fatalf("incorrect route length: expected %v got %v", 2,
@ -744,12 +745,12 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
// Send off the payment request to the router, this payment should // Send off the payment request to the router, this payment should
// succeed as we should actually go through Pham Nuwen in order to get // succeed as we should actually go through Pham Nuwen in order to get
// to Sophon, even though he has higher fees. // to Sophon, even though he has higher fees.
paymentPreImage, route, err := ctx.router.SendPayment(&payment) paymentPreImage, rt, err := ctx.router.SendPayment(&payment)
if err != nil { if err != nil {
t.Fatalf("unable to send payment: %v", err) t.Fatalf("unable to send payment: %v", err)
} }
assertExpectedPath(paymentPreImage, route) assertExpectedPath(paymentPreImage, rt)
// We'll now modify the error return an IncorrectCltvExpiry error // We'll now modify the error return an IncorrectCltvExpiry error
// instead, this should result in the same behavior of roasbeef routing // instead, this should result in the same behavior of roasbeef routing
@ -778,12 +779,12 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
// Once again, Roasbeef should route around Goku since they disagree // Once again, Roasbeef should route around Goku since they disagree
// w.r.t to the block height, and instead go through Pham Nuwen. // w.r.t to the block height, and instead go through Pham Nuwen.
paymentPreImage, route, err = ctx.router.SendPayment(&payment) paymentPreImage, rt, err = ctx.router.SendPayment(&payment)
if err != nil { if err != nil {
t.Fatalf("unable to send payment: %v", err) t.Fatalf("unable to send payment: %v", err)
} }
assertExpectedPath(paymentPreImage, route) assertExpectedPath(paymentPreImage, rt)
} }
// TestSendPaymentErrorPathPruning tests that the send of candidate routes // TestSendPaymentErrorPathPruning tests that the send of candidate routes
@ -902,25 +903,25 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
// This shouldn't return an error, as we'll make a payment attempt via // This shouldn't return an error, as we'll make a payment attempt via
// the satoshi channel based on the assumption that there might be an // the satoshi channel based on the assumption that there might be an
// intermittent issue with the roasbeef <-> lioji channel. // intermittent issue with the roasbeef <-> lioji channel.
paymentPreImage, route, err := ctx.router.SendPayment(&payment) paymentPreImage, rt, err := ctx.router.SendPayment(&payment)
if err != nil { if err != nil {
t.Fatalf("unable send payment: %v", err) t.Fatalf("unable send payment: %v", err)
} }
// This path should go: roasbeef -> satoshi -> luoji // This path should go: roasbeef -> satoshi -> luoji
if len(route.Hops) != 2 { if len(rt.Hops) != 2 {
t.Fatalf("incorrect route length: expected %v got %v", 2, t.Fatalf("incorrect route length: expected %v got %v", 2,
len(route.Hops)) len(rt.Hops))
} }
if !bytes.Equal(paymentPreImage[:], preImage[:]) { if !bytes.Equal(paymentPreImage[:], preImage[:]) {
t.Fatalf("incorrect preimage used: expected %x got %x", t.Fatalf("incorrect preimage used: expected %x got %x",
preImage[:], paymentPreImage[:]) preImage[:], paymentPreImage[:])
} }
if route.Hops[0].PubKeyBytes != ctx.aliases["satoshi"] { if rt.Hops[0].PubKeyBytes != ctx.aliases["satoshi"] {
t.Fatalf("route should go through satoshi as first hop, "+ t.Fatalf("route should go through satoshi as first hop, "+
"instead passes through: %v", "instead passes through: %v",
getAliasFromPubKey(route.Hops[0].PubKeyBytes, getAliasFromPubKey(rt.Hops[0].PubKeyBytes,
ctx.aliases)) ctx.aliases))
} }
@ -944,16 +945,16 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
return preImage, nil return preImage, nil
} }
paymentPreImage, route, err = ctx.router.SendPayment(&payment) paymentPreImage, rt, err = ctx.router.SendPayment(&payment)
if err != nil { if err != nil {
t.Fatalf("unable to send payment: %v", err) t.Fatalf("unable to send payment: %v", err)
} }
// This should succeed finally. The route selected should have two // This should succeed finally. The route selected should have two
// hops. // hops.
if len(route.Hops) != 2 { if len(rt.Hops) != 2 {
t.Fatalf("incorrect route length: expected %v got %v", 2, t.Fatalf("incorrect route length: expected %v got %v", 2,
len(route.Hops)) len(rt.Hops))
} }
// The preimage should match up with the once created above. // The preimage should match up with the once created above.
@ -963,11 +964,11 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
} }
// The route should have satoshi as the first hop. // The route should have satoshi as the first hop.
if route.Hops[0].PubKeyBytes != ctx.aliases["satoshi"] { if rt.Hops[0].PubKeyBytes != ctx.aliases["satoshi"] {
t.Fatalf("route should go through satoshi as first hop, "+ t.Fatalf("route should go through satoshi as first hop, "+
"instead passes through: %v", "instead passes through: %v",
getAliasFromPubKey(route.Hops[0].PubKeyBytes, getAliasFromPubKey(rt.Hops[0].PubKeyBytes,
ctx.aliases)) ctx.aliases))
} }
} }
@ -1341,7 +1342,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
// We should now be able to find two routes to node 2. // We should now be able to find two routes to node 2.
paymentAmt := lnwire.NewMSatFromSatoshis(100) paymentAmt := lnwire.NewMSatFromSatoshis(100)
targetNode := priv2.PubKey() targetNode := priv2.PubKey()
var targetPubKeyBytes Vertex var targetPubKeyBytes route.Vertex
copy(targetPubKeyBytes[:], targetNode.SerializeCompressed()) copy(targetPubKeyBytes[:], targetNode.SerializeCompressed())
routes, err := ctx.router.FindRoutes( routes, err := ctx.router.FindRoutes(
ctx.router.selfNode.PubKeyBytes, ctx.router.selfNode.PubKeyBytes,
@ -2512,9 +2513,9 @@ func TestIsStaleEdgePolicy(t *testing.T) {
func TestEmptyRoutesGenerateSphinxPacket(t *testing.T) { func TestEmptyRoutesGenerateSphinxPacket(t *testing.T) {
t.Parallel() t.Parallel()
emptyRoute := &Route{} emptyRoute := &route.Route{}
_, _, err := generateSphinxPacket(emptyRoute, testHash[:]) _, _, err := generateSphinxPacket(emptyRoute, testHash[:])
if err != ErrNoRouteHopsProvided { if err != route.ErrNoRouteHopsProvided {
t.Fatalf("expected empty hops error: instead got: %v", err) t.Fatalf("expected empty hops error: instead got: %v", err)
} }
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
) )
// ErrVBarrierShuttingDown signals that the barrier has been requested to // ErrVBarrierShuttingDown signals that the barrier has been requested to
@ -42,7 +43,7 @@ type ValidationBarrier struct {
// nodeAnnDependencies tracks any pending NodeAnnouncement validation // nodeAnnDependencies tracks any pending NodeAnnouncement validation
// jobs which should wait until the completion of the // jobs which should wait until the completion of the
// ChannelAnnouncement before proceeding. // ChannelAnnouncement before proceeding.
nodeAnnDependencies map[Vertex]chan struct{} nodeAnnDependencies map[route.Vertex]chan struct{}
quit chan struct{} quit chan struct{}
sync.Mutex sync.Mutex
@ -57,7 +58,7 @@ func NewValidationBarrier(numActiveReqs int,
v := &ValidationBarrier{ v := &ValidationBarrier{
chanAnnFinSignal: make(map[lnwire.ShortChannelID]chan struct{}), chanAnnFinSignal: make(map[lnwire.ShortChannelID]chan struct{}),
chanEdgeDependencies: make(map[lnwire.ShortChannelID]chan struct{}), chanEdgeDependencies: make(map[lnwire.ShortChannelID]chan struct{}),
nodeAnnDependencies: make(map[Vertex]chan struct{}), nodeAnnDependencies: make(map[route.Vertex]chan struct{}),
quit: quitChan, quit: quitChan,
} }
@ -110,8 +111,8 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) {
v.chanAnnFinSignal[msg.ShortChannelID] = annFinCond v.chanAnnFinSignal[msg.ShortChannelID] = annFinCond
v.chanEdgeDependencies[msg.ShortChannelID] = annFinCond v.chanEdgeDependencies[msg.ShortChannelID] = annFinCond
v.nodeAnnDependencies[Vertex(msg.NodeID1)] = annFinCond v.nodeAnnDependencies[route.Vertex(msg.NodeID1)] = annFinCond
v.nodeAnnDependencies[Vertex(msg.NodeID2)] = annFinCond v.nodeAnnDependencies[route.Vertex(msg.NodeID2)] = annFinCond
} }
case *channeldb.ChannelEdgeInfo: case *channeldb.ChannelEdgeInfo:
@ -122,8 +123,8 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) {
v.chanAnnFinSignal[shortID] = annFinCond v.chanAnnFinSignal[shortID] = annFinCond
v.chanEdgeDependencies[shortID] = annFinCond v.chanEdgeDependencies[shortID] = annFinCond
v.nodeAnnDependencies[Vertex(msg.NodeKey1Bytes)] = annFinCond v.nodeAnnDependencies[route.Vertex(msg.NodeKey1Bytes)] = annFinCond
v.nodeAnnDependencies[Vertex(msg.NodeKey2Bytes)] = annFinCond v.nodeAnnDependencies[route.Vertex(msg.NodeKey2Bytes)] = annFinCond
} }
// These other types don't have any dependants, so no further // These other types don't have any dependants, so no further
@ -174,12 +175,12 @@ func (v *ValidationBarrier) WaitForDependants(job interface{}) error {
shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID) shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
signal, ok = v.chanEdgeDependencies[shortID] signal, ok = v.chanEdgeDependencies[shortID]
case *channeldb.LightningNode: case *channeldb.LightningNode:
vertex := Vertex(msg.PubKeyBytes) vertex := route.Vertex(msg.PubKeyBytes)
signal, ok = v.nodeAnnDependencies[vertex] signal, ok = v.nodeAnnDependencies[vertex]
case *lnwire.ChannelUpdate: case *lnwire.ChannelUpdate:
signal, ok = v.chanEdgeDependencies[msg.ShortChannelID] signal, ok = v.chanEdgeDependencies[msg.ShortChannelID]
case *lnwire.NodeAnnouncement: case *lnwire.NodeAnnouncement:
vertex := Vertex(msg.NodeID) vertex := route.Vertex(msg.NodeID)
signal, ok = v.nodeAnnDependencies[vertex] signal, ok = v.nodeAnnDependencies[vertex]
// Other types of jobs can be executed immediately, so we'll just // Other types of jobs can be executed immediately, so we'll just
@ -243,9 +244,9 @@ func (v *ValidationBarrier) SignalDependants(job interface{}) {
// map, as if we reach this point, then all dependants have already // map, as if we reach this point, then all dependants have already
// finished executing and we can proceed. // finished executing and we can proceed.
case *channeldb.LightningNode: case *channeldb.LightningNode:
delete(v.nodeAnnDependencies, Vertex(msg.PubKeyBytes)) delete(v.nodeAnnDependencies, route.Vertex(msg.PubKeyBytes))
case *lnwire.NodeAnnouncement: case *lnwire.NodeAnnouncement:
delete(v.nodeAnnDependencies, Vertex(msg.NodeID)) delete(v.nodeAnnDependencies, route.Vertex(msg.NodeID))
case *lnwire.ChannelUpdate: case *lnwire.ChannelUpdate:
delete(v.chanEdgeDependencies, msg.ShortChannelID) delete(v.chanEdgeDependencies, msg.ShortChannelID)
case *channeldb.ChannelEdgePolicy: case *channeldb.ChannelEdgePolicy:

View File

@ -17,6 +17,7 @@ import (
"time" "time"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
@ -2720,7 +2721,7 @@ func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription,
// savePayment saves a successfully completed payment to the database for // savePayment saves a successfully completed payment to the database for
// historical record keeping. // historical record keeping.
func (r *rpcServer) savePayment(route *routing.Route, func (r *rpcServer) savePayment(route *route.Route,
amount lnwire.MilliSatoshi, preImage []byte) error { amount lnwire.MilliSatoshi, preImage []byte) error {
paymentPath := make([][33]byte, len(route.Hops)) paymentPath := make([][33]byte, len(route.Hops))
@ -2770,7 +2771,7 @@ type paymentStream struct {
// lnrpc.SendToRouteRequest can be passed to sendPayment. // lnrpc.SendToRouteRequest can be passed to sendPayment.
type rpcPaymentRequest struct { type rpcPaymentRequest struct {
*lnrpc.SendRequest *lnrpc.SendRequest
routes []*routing.Route routes []*route.Route
} }
// calculateFeeLimit returns the fee limit in millisatoshis. If a percentage // calculateFeeLimit returns the fee limit in millisatoshis. If a percentage
@ -2860,9 +2861,9 @@ func unmarshallSendToRouteRequest(req *lnrpc.SendToRouteRequest,
return nil, fmt.Errorf("cannot use both route and routes field") return nil, fmt.Errorf("cannot use both route and routes field")
} }
var routes []*routing.Route var routes []*route.Route
if len(req.Routes) > 0 { if len(req.Routes) > 0 {
routes = make([]*routing.Route, len(req.Routes)) routes = make([]*route.Route, len(req.Routes))
for i, rpcroute := range req.Routes { for i, rpcroute := range req.Routes {
route, err := unmarshallRoute(rpcroute, graph) route, err := unmarshallRoute(rpcroute, graph)
if err != nil { if err != nil {
@ -2871,11 +2872,11 @@ func unmarshallSendToRouteRequest(req *lnrpc.SendToRouteRequest,
routes[i] = route routes[i] = route
} }
} else { } else {
route, err := unmarshallRoute(req.Route, graph) rt, err := unmarshallRoute(req.Route, graph)
if err != nil { if err != nil {
return nil, err return nil, err
} }
routes = []*routing.Route{route} routes = []*route.Route{rt}
} }
return &rpcPaymentRequest{ return &rpcPaymentRequest{
@ -2896,13 +2897,13 @@ type rpcPaymentIntent struct {
msat lnwire.MilliSatoshi msat lnwire.MilliSatoshi
feeLimit lnwire.MilliSatoshi feeLimit lnwire.MilliSatoshi
cltvLimit *uint32 cltvLimit *uint32
dest routing.Vertex dest route.Vertex
rHash [32]byte rHash [32]byte
cltvDelta uint16 cltvDelta uint16
routeHints [][]zpay32.HopHint routeHints [][]zpay32.HopHint
outgoingChannelID *uint64 outgoingChannelID *uint64
routes []*routing.Route routes []*route.Route
} }
// extractPaymentIntent attempts to parse the complete details required to // extractPaymentIntent attempts to parse the complete details required to
@ -3064,7 +3065,7 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error
} }
type paymentIntentResponse struct { type paymentIntentResponse struct {
Route *routing.Route Route *route.Route
Preimage [32]byte Preimage [32]byte
Err error Err error
} }
@ -3082,7 +3083,7 @@ func (r *rpcServer) dispatchPaymentIntent(
// we'll get a non-nil error. // we'll get a non-nil error.
var ( var (
preImage [32]byte preImage [32]byte
route *routing.Route route *route.Route
routerErr error routerErr error
) )
@ -3887,7 +3888,7 @@ func (r *rpcServer) QueryRoutes(ctx context.Context,
// retrieve both endpoints and determine the hop pubkey using the previous hop // retrieve both endpoints and determine the hop pubkey using the previous hop
// pubkey. If the channel is unknown, an error is returned. // pubkey. If the channel is unknown, an error is returned.
func unmarshallHopByChannelLookup(graph *channeldb.ChannelGraph, hop *lnrpc.Hop, func unmarshallHopByChannelLookup(graph *channeldb.ChannelGraph, hop *lnrpc.Hop,
prevPubKeyBytes [33]byte) (*routing.Hop, error) { prevPubKeyBytes [33]byte) (*route.Hop, error) {
// Discard edge policies, because they may be nil. // Discard edge policies, because they may be nil.
edgeInfo, _, _, err := graph.FetchChannelEdgesByID(hop.ChanId) edgeInfo, _, _, err := graph.FetchChannelEdgesByID(hop.ChanId)
@ -3906,7 +3907,7 @@ func unmarshallHopByChannelLookup(graph *channeldb.ChannelGraph, hop *lnrpc.Hop,
return nil, fmt.Errorf("channel edge does not match expected node") return nil, fmt.Errorf("channel edge does not match expected node")
} }
return &routing.Hop{ return &route.Hop{
OutgoingTimeLock: hop.Expiry, OutgoingTimeLock: hop.Expiry,
AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat), AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat),
PubKeyBytes: pubKeyBytes, PubKeyBytes: pubKeyBytes,
@ -3917,7 +3918,7 @@ func unmarshallHopByChannelLookup(graph *channeldb.ChannelGraph, hop *lnrpc.Hop,
// unmarshallKnownPubkeyHop unmarshalls an rpc hop that contains the hop pubkey. // unmarshallKnownPubkeyHop unmarshalls an rpc hop that contains the hop pubkey.
// The channel graph doesn't need to be queried because all information required // The channel graph doesn't need to be queried because all information required
// for sending the payment is present. // for sending the payment is present.
func unmarshallKnownPubkeyHop(hop *lnrpc.Hop) (*routing.Hop, error) { func unmarshallKnownPubkeyHop(hop *lnrpc.Hop) (*route.Hop, error) {
pubKey, err := hex.DecodeString(hop.PubKey) pubKey, err := hex.DecodeString(hop.PubKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot decode pubkey %s", hop.PubKey) return nil, fmt.Errorf("cannot decode pubkey %s", hop.PubKey)
@ -3926,7 +3927,7 @@ func unmarshallKnownPubkeyHop(hop *lnrpc.Hop) (*routing.Hop, error) {
var pubKeyBytes [33]byte var pubKeyBytes [33]byte
copy(pubKeyBytes[:], pubKey) copy(pubKeyBytes[:], pubKey)
return &routing.Hop{ return &route.Hop{
OutgoingTimeLock: hop.Expiry, OutgoingTimeLock: hop.Expiry,
AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat), AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat),
PubKeyBytes: pubKeyBytes, PubKeyBytes: pubKeyBytes,
@ -3937,7 +3938,7 @@ func unmarshallKnownPubkeyHop(hop *lnrpc.Hop) (*routing.Hop, error) {
// unmarshallHop unmarshalls an rpc hop that may or may not contain a node // unmarshallHop unmarshalls an rpc hop that may or may not contain a node
// pubkey. // pubkey.
func unmarshallHop(graph *channeldb.ChannelGraph, hop *lnrpc.Hop, func unmarshallHop(graph *channeldb.ChannelGraph, hop *lnrpc.Hop,
prevNodePubKey [33]byte) (*routing.Hop, error) { prevNodePubKey [33]byte) (*route.Hop, error) {
if hop.PubKey == "" { if hop.PubKey == "" {
// If no pub key is given of the hop, the local channel // If no pub key is given of the hop, the local channel
@ -3952,7 +3953,7 @@ func unmarshallHop(graph *channeldb.ChannelGraph, hop *lnrpc.Hop,
// unmarshallRoute unmarshalls an rpc route. For hops that don't specify a // unmarshallRoute unmarshalls an rpc route. For hops that don't specify a
// pubkey, the channel graph is queried. // pubkey, the channel graph is queried.
func unmarshallRoute(rpcroute *lnrpc.Route, func unmarshallRoute(rpcroute *lnrpc.Route,
graph *channeldb.ChannelGraph) (*routing.Route, error) { graph *channeldb.ChannelGraph) (*route.Route, error) {
sourceNode, err := graph.SourceNode() sourceNode, err := graph.SourceNode()
if err != nil { if err != nil {
@ -3962,7 +3963,7 @@ func unmarshallRoute(rpcroute *lnrpc.Route,
prevNodePubKey := sourceNode.PubKeyBytes prevNodePubKey := sourceNode.PubKeyBytes
hops := make([]*routing.Hop, len(rpcroute.Hops)) hops := make([]*route.Hop, len(rpcroute.Hops))
for i, hop := range rpcroute.Hops { for i, hop := range rpcroute.Hops {
routeHop, err := unmarshallHop(graph, routeHop, err := unmarshallHop(graph,
hop, prevNodePubKey) hop, prevNodePubKey)
@ -3975,7 +3976,7 @@ func unmarshallRoute(rpcroute *lnrpc.Route,
prevNodePubKey = routeHop.PubKeyBytes prevNodePubKey = routeHop.PubKeyBytes
} }
route, err := routing.NewRouteFromHops( route, err := route.NewRouteFromHops(
lnwire.MilliSatoshi(rpcroute.TotalAmtMsat), lnwire.MilliSatoshi(rpcroute.TotalAmtMsat),
rpcroute.TotalTimeLock, rpcroute.TotalTimeLock,
sourceNode.PubKeyBytes, sourceNode.PubKeyBytes,

View File

@ -44,6 +44,7 @@ import (
"github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/netann"
"github.com/lightningnetwork/lnd/pool" "github.com/lightningnetwork/lnd/pool"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/sweep"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
"github.com/lightningnetwork/lnd/tor" "github.com/lightningnetwork/lnd/tor"
@ -2063,7 +2064,7 @@ func (s *server) prunePersistentPeerConnection(compressedPubKey [33]byte) {
// the target peers. // the target peers.
// //
// NOTE: This function is safe for concurrent access. // NOTE: This function is safe for concurrent access.
func (s *server) BroadcastMessage(skips map[routing.Vertex]struct{}, func (s *server) BroadcastMessage(skips map[route.Vertex]struct{},
msgs ...lnwire.Message) error { msgs ...lnwire.Message) error {
srvrLog.Debugf("Broadcasting %v messages", len(msgs)) srvrLog.Debugf("Broadcasting %v messages", len(msgs))