mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-07-01 19:10:59 +02:00
routing+routerrpc: add capacity in rpcs
FetchPairCapacity is used by the following endpoints to introduce the capacity for probability calculations: * QueryProbability * QueryRoutes
This commit is contained in:
@ -45,6 +45,11 @@ type RouterBackend struct {
|
|||||||
// capacity of a channel to populate in responses.
|
// capacity of a channel to populate in responses.
|
||||||
FetchChannelCapacity func(chanID uint64) (btcutil.Amount, error)
|
FetchChannelCapacity func(chanID uint64) (btcutil.Amount, error)
|
||||||
|
|
||||||
|
// FetchAmountPairCapacity determines the maximal channel capacity
|
||||||
|
// between two nodes given a certain amount.
|
||||||
|
FetchAmountPairCapacity func(nodeFrom, nodeTo route.Vertex,
|
||||||
|
amount lnwire.MilliSatoshi) (btcutil.Amount, error)
|
||||||
|
|
||||||
// FetchChannelEndpoints returns the pubkeys of both endpoints of the
|
// FetchChannelEndpoints returns the pubkeys of both endpoints of the
|
||||||
// given channel id.
|
// given channel id.
|
||||||
FetchChannelEndpoints func(chanID uint64) (route.Vertex,
|
FetchChannelEndpoints func(chanID uint64) (route.Vertex,
|
||||||
@ -325,7 +330,7 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
|
|||||||
// Query the channel router for a possible path to the destination that
|
// Query the channel router for a possible path to the destination that
|
||||||
// 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.
|
||||||
route, _, err := r.FindRoute(
|
route, successProb, err := r.FindRoute(
|
||||||
sourcePubKey, targetPubKey, amt, in.TimePref, restrictions,
|
sourcePubKey, targetPubKey, amt, in.TimePref, restrictions,
|
||||||
customRecords, routeHintEdges, finalCLTVDelta,
|
customRecords, routeHintEdges, finalCLTVDelta,
|
||||||
)
|
)
|
||||||
@ -340,11 +345,6 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate route success probability. Do not rely on a probability
|
|
||||||
// that could have been returned from path finding, because mission
|
|
||||||
// control may have been disabled in the provided ProbabilitySource.
|
|
||||||
successProb := r.getSuccessProbability(route)
|
|
||||||
|
|
||||||
routeResp := &lnrpc.QueryRoutesResponse{
|
routeResp := &lnrpc.QueryRoutesResponse{
|
||||||
Routes: []*lnrpc.Route{rpcRoute},
|
Routes: []*lnrpc.Route{rpcRoute},
|
||||||
SuccessProb: successProb,
|
SuccessProb: successProb,
|
||||||
@ -353,28 +353,6 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
|
|||||||
return routeResp, nil
|
return routeResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSuccessProbability returns the success probability for the given route
|
|
||||||
// based on the current state of mission control.
|
|
||||||
func (r *RouterBackend) getSuccessProbability(rt *route.Route) float64 {
|
|
||||||
fromNode := rt.SourcePubKey
|
|
||||||
amtToFwd := rt.TotalAmount
|
|
||||||
successProb := 1.0
|
|
||||||
for _, hop := range rt.Hops {
|
|
||||||
toNode := hop.PubKeyBytes
|
|
||||||
|
|
||||||
probability := r.MissionControl.GetProbability(
|
|
||||||
fromNode, toNode, amtToFwd, 0,
|
|
||||||
)
|
|
||||||
|
|
||||||
successProb *= probability
|
|
||||||
|
|
||||||
amtToFwd = hop.AmtToForward
|
|
||||||
fromNode = toNode
|
|
||||||
}
|
|
||||||
|
|
||||||
return successProb
|
|
||||||
}
|
|
||||||
|
|
||||||
// rpcEdgeToPair looks up the provided channel and returns the channel endpoints
|
// rpcEdgeToPair looks up the provided channel and returns the channel endpoints
|
||||||
// as a directed pair.
|
// as a directed pair.
|
||||||
func (r *RouterBackend) rpcEdgeToPair(e *lnrpc.EdgeLocator) (
|
func (r *RouterBackend) rpcEdgeToPair(e *lnrpc.EdgeLocator) (
|
||||||
|
@ -200,6 +200,11 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool,
|
|||||||
|
|
||||||
return 1, nil
|
return 1, nil
|
||||||
},
|
},
|
||||||
|
FetchAmountPairCapacity: func(nodeFrom, nodeTo route.Vertex,
|
||||||
|
amount lnwire.MilliSatoshi) (btcutil.Amount, error) {
|
||||||
|
|
||||||
|
return 1, nil
|
||||||
|
},
|
||||||
MissionControl: &mockMissionControl{},
|
MissionControl: &mockMissionControl{},
|
||||||
FetchChannelEndpoints: func(chanID uint64) (route.Vertex,
|
FetchChannelEndpoints: func(chanID uint64) (route.Vertex,
|
||||||
route.Vertex, error) {
|
route.Vertex, error) {
|
||||||
|
@ -709,8 +709,22 @@ func (s *Server) QueryProbability(ctx context.Context,
|
|||||||
|
|
||||||
amt := lnwire.MilliSatoshi(req.AmtMsat)
|
amt := lnwire.MilliSatoshi(req.AmtMsat)
|
||||||
|
|
||||||
|
// Compute the probability.
|
||||||
|
var prob float64
|
||||||
mc := s.cfg.RouterBackend.MissionControl
|
mc := s.cfg.RouterBackend.MissionControl
|
||||||
prob := mc.GetProbability(fromNode, toNode, amt, 0)
|
capacity, err := s.cfg.RouterBackend.FetchAmountPairCapacity(
|
||||||
|
fromNode, toNode, amt,
|
||||||
|
)
|
||||||
|
|
||||||
|
// If we cannot query the capacity this means that either we don't have
|
||||||
|
// information available or that the channel fails min/maxHtlc
|
||||||
|
// constraints, so we return a zero probability.
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Cannot fetch capacity: %v", err)
|
||||||
|
} else {
|
||||||
|
prob = mc.GetProbability(fromNode, toNode, amt, capacity)
|
||||||
|
}
|
||||||
|
|
||||||
history := mc.GetPairHistorySnapshot(fromNode, toNode)
|
history := mc.GetPairHistorySnapshot(fromNode, toNode)
|
||||||
|
|
||||||
return &QueryProbabilityResponse{
|
return &QueryProbabilityResponse{
|
||||||
|
@ -111,7 +111,7 @@ func testRestAPI(net *lntest.NetworkHarness, ht *harnessTest) {
|
|||||||
resp := &routerrpc.QueryProbabilityResponse{}
|
resp := &routerrpc.QueryProbabilityResponse{}
|
||||||
err := invokeGET(a, url, resp)
|
err := invokeGET(a, url, resp)
|
||||||
require.Nil(t, err, "query probability")
|
require.Nil(t, err, "query probability")
|
||||||
assert.Greater(t, resp.Probability, 0.5, "probability")
|
require.Zero(t, resp.Probability)
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
name: "GET with map type query param",
|
name: "GET with map type query param",
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@ -20,6 +23,11 @@ type routingGraph interface {
|
|||||||
|
|
||||||
// fetchNodeFeatures returns the features of the given node.
|
// fetchNodeFeatures returns the features of the given node.
|
||||||
fetchNodeFeatures(nodePub route.Vertex) (*lnwire.FeatureVector, error)
|
fetchNodeFeatures(nodePub route.Vertex) (*lnwire.FeatureVector, error)
|
||||||
|
|
||||||
|
// FetchAmountPairCapacity determines the maximal capacity between two
|
||||||
|
// pairs of nodes.
|
||||||
|
FetchAmountPairCapacity(nodeFrom, nodeTo route.Vertex,
|
||||||
|
amount lnwire.MilliSatoshi) (btcutil.Amount, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CachedGraph is a routingGraph implementation that retrieves from the
|
// CachedGraph is a routingGraph implementation that retrieves from the
|
||||||
@ -51,9 +59,9 @@ func NewCachedGraph(sourceNode *channeldb.LightningNode,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// close attempts to close the underlying db transaction. This is a no-op in
|
// Close attempts to close the underlying db transaction. This is a no-op in
|
||||||
// case the underlying graph uses an in-memory cache.
|
// case the underlying graph uses an in-memory cache.
|
||||||
func (g *CachedGraph) close() error {
|
func (g *CachedGraph) Close() error {
|
||||||
if g.tx == nil {
|
if g.tx == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -86,3 +94,33 @@ func (g *CachedGraph) fetchNodeFeatures(nodePub route.Vertex) (
|
|||||||
|
|
||||||
return g.graph.FetchNodeFeatures(nodePub)
|
return g.graph.FetchNodeFeatures(nodePub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FetchAmountPairCapacity determines the maximal public capacity between two
|
||||||
|
// nodes depending on the amount we try to send.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the routingGraph interface.
|
||||||
|
func (g *CachedGraph) FetchAmountPairCapacity(nodeFrom, nodeTo route.Vertex,
|
||||||
|
amount lnwire.MilliSatoshi) (btcutil.Amount, error) {
|
||||||
|
|
||||||
|
// Create unified edges for all incoming connections.
|
||||||
|
u := newNodeEdgeUnifier(g.sourceNode(), nodeTo, nil)
|
||||||
|
|
||||||
|
err := u.addGraphPolicies(g)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeUnifier, ok := u.edgeUnifiers[nodeFrom]
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("no edge info for node pair %v -> %v",
|
||||||
|
nodeFrom, nodeTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
edge := edgeUnifier.getEdgeNetwork(amount)
|
||||||
|
if edge == nil {
|
||||||
|
return 0, fmt.Errorf("no edge for node pair %v -> %v "+
|
||||||
|
"(amount %v)", nodeFrom, nodeTo, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return edge.capacity, nil
|
||||||
|
}
|
||||||
|
@ -226,6 +226,31 @@ func (m *mockGraph) fetchNodeFeatures(nodePub route.Vertex) (
|
|||||||
return lnwire.EmptyFeatureVector(), nil
|
return lnwire.EmptyFeatureVector(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FetchAmountPairCapacity returns the maximal capacity between nodes in the
|
||||||
|
// graph.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the routingGraph interface.
|
||||||
|
func (m *mockGraph) FetchAmountPairCapacity(nodeFrom, nodeTo route.Vertex,
|
||||||
|
amount lnwire.MilliSatoshi) (btcutil.Amount, error) {
|
||||||
|
|
||||||
|
var capacity btcutil.Amount
|
||||||
|
|
||||||
|
cb := func(channel *channeldb.DirectedChannel) error {
|
||||||
|
if channel.OtherNode == nodeTo {
|
||||||
|
capacity = channel.Capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.forEachNodeChannel(nodeFrom, cb)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return capacity, nil
|
||||||
|
}
|
||||||
|
|
||||||
// htlcResult describes the resolution of an htlc. If failure is nil, the htlc
|
// htlcResult describes the resolution of an htlc. If failure is nil, the htlc
|
||||||
// was settled.
|
// was settled.
|
||||||
type htlcResult struct {
|
type htlcResult struct {
|
||||||
|
@ -3112,7 +3112,7 @@ func dbFindPath(graph *channeldb.ChannelGraph,
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := routingGraph.close(); err != nil {
|
if err := routingGraph.Close(); err != nil {
|
||||||
log.Errorf("Error closing db tx: %v", err)
|
log.Errorf("Error closing db tx: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -51,7 +51,7 @@ func (m *SessionSource) getRoutingGraph() (routingGraph, func(), error) {
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return routingTx, func() {
|
return routingTx, func() {
|
||||||
err := routingTx.close()
|
err := routingTx.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error closing db tx: %v", err)
|
log.Errorf("Error closing db tx: %v", err)
|
||||||
}
|
}
|
||||||
|
23
rpcserver.go
23
rpcserver.go
@ -689,6 +689,7 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
graph := s.graphDB
|
graph := s.graphDB
|
||||||
|
|
||||||
routerBackend := &routerrpc.RouterBackend{
|
routerBackend := &routerrpc.RouterBackend{
|
||||||
SelfNode: selfNode.PubKeyBytes,
|
SelfNode: selfNode.PubKeyBytes,
|
||||||
FetchChannelCapacity: func(chanID uint64) (btcutil.Amount,
|
FetchChannelCapacity: func(chanID uint64) (btcutil.Amount,
|
||||||
@ -700,6 +701,28 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
|
|||||||
}
|
}
|
||||||
return info.Capacity, nil
|
return info.Capacity, nil
|
||||||
},
|
},
|
||||||
|
FetchAmountPairCapacity: func(nodeFrom, nodeTo route.Vertex,
|
||||||
|
amount lnwire.MilliSatoshi) (btcutil.Amount, error) {
|
||||||
|
|
||||||
|
routingGraph, err := routing.NewCachedGraph(
|
||||||
|
selfNode, graph,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
closeErr := routingGraph.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
rpcsLog.Errorf("not able to close "+
|
||||||
|
"routing graph tx: %v",
|
||||||
|
closeErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return routingGraph.FetchAmountPairCapacity(
|
||||||
|
nodeFrom, nodeTo, amount,
|
||||||
|
)
|
||||||
|
},
|
||||||
FetchChannelEndpoints: func(chanID uint64) (route.Vertex,
|
FetchChannelEndpoints: func(chanID uint64) (route.Vertex,
|
||||||
route.Vertex, error) {
|
route.Vertex, error) {
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user