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:
bitromortac 2022-08-23 12:00:54 +02:00
parent 66ffc64776
commit 454c115b6e
No known key found for this signature in database
GPG Key ID: 1965063FC13BEBE2
9 changed files with 117 additions and 34 deletions

View File

@ -45,6 +45,11 @@ type RouterBackend struct {
// capacity of a channel to populate in responses.
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
// given channel id.
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
// can carry `in.Amt` satoshis _including_ the total fee required on
// the route.
route, _, err := r.FindRoute(
route, successProb, err := r.FindRoute(
sourcePubKey, targetPubKey, amt, in.TimePref, restrictions,
customRecords, routeHintEdges, finalCLTVDelta,
)
@ -340,11 +345,6 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
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{
Routes: []*lnrpc.Route{rpcRoute},
SuccessProb: successProb,
@ -353,28 +353,6 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
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
// as a directed pair.
func (r *RouterBackend) rpcEdgeToPair(e *lnrpc.EdgeLocator) (

View File

@ -200,6 +200,11 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool,
return 1, nil
},
FetchAmountPairCapacity: func(nodeFrom, nodeTo route.Vertex,
amount lnwire.MilliSatoshi) (btcutil.Amount, error) {
return 1, nil
},
MissionControl: &mockMissionControl{},
FetchChannelEndpoints: func(chanID uint64) (route.Vertex,
route.Vertex, error) {

View File

@ -709,8 +709,22 @@ func (s *Server) QueryProbability(ctx context.Context,
amt := lnwire.MilliSatoshi(req.AmtMsat)
// Compute the probability.
var prob float64
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)
return &QueryProbabilityResponse{

View File

@ -111,7 +111,7 @@ func testRestAPI(net *lntest.NetworkHarness, ht *harnessTest) {
resp := &routerrpc.QueryProbabilityResponse{}
err := invokeGET(a, url, resp)
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",

View File

@ -1,6 +1,9 @@
package routing
import (
"fmt"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwire"
@ -20,6 +23,11 @@ type routingGraph interface {
// fetchNodeFeatures returns the features of the given node.
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
@ -51,9 +59,9 @@ func NewCachedGraph(sourceNode *channeldb.LightningNode,
}, 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.
func (g *CachedGraph) close() error {
func (g *CachedGraph) Close() error {
if g.tx == nil {
return nil
}
@ -86,3 +94,33 @@ func (g *CachedGraph) fetchNodeFeatures(nodePub route.Vertex) (
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
}

View File

@ -226,6 +226,31 @@ func (m *mockGraph) fetchNodeFeatures(nodePub route.Vertex) (
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
// was settled.
type htlcResult struct {

View File

@ -3112,7 +3112,7 @@ func dbFindPath(graph *channeldb.ChannelGraph,
}
defer func() {
if err := routingGraph.close(); err != nil {
if err := routingGraph.Close(); err != nil {
log.Errorf("Error closing db tx: %v", err)
}
}()

View File

@ -51,7 +51,7 @@ func (m *SessionSource) getRoutingGraph() (routingGraph, func(), error) {
return nil, nil, err
}
return routingTx, func() {
err := routingTx.close()
err := routingTx.Close()
if err != nil {
log.Errorf("Error closing db tx: %v", err)
}

View File

@ -689,6 +689,7 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
return err
}
graph := s.graphDB
routerBackend := &routerrpc.RouterBackend{
SelfNode: selfNode.PubKeyBytes,
FetchChannelCapacity: func(chanID uint64) (btcutil.Amount,
@ -700,6 +701,28 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
}
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,
route.Vertex, error) {