Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions cmd/lncli/cmd_query_mission_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,19 @@ func queryMissionControl(ctx *cli.Context) error {
Pubkey string
LastFailTime int64
OtherChanSuccessProb float32
Channels []*routerrpc.ChannelHistory
}

type displayPairHistory struct {
NodeA, NodeB string
Timestamp int64
SuccessProb float32
Amt int64
Result string
}

displayResp := struct {
Nodes []displayNodeHistory
Pairs []displayPairHistory
}{}

for _, n := range snapshot.Nodes {
Expand All @@ -49,7 +57,20 @@ func queryMissionControl(ctx *cli.Context) error {
Pubkey: hex.EncodeToString(n.Pubkey),
LastFailTime: n.LastFailTime,
OtherChanSuccessProb: n.OtherChanSuccessProb,
Channels: n.Channels,
},
)
}

for _, n := range snapshot.Pairs {
displayResp.Pairs = append(
displayResp.Pairs,
displayPairHistory{
NodeA: hex.EncodeToString(n.NodeA),
NodeB: hex.EncodeToString(n.NodeB),
Timestamp: n.Timestamp,
SuccessProb: n.SuccessProb,
Amt: n.Amt,
Result: n.Result.String(),
},
)
}
Expand Down
348 changes: 197 additions & 151 deletions lnrpc/routerrpc/router.pb.go

Large diffs are not rendered by default.

26 changes: 18 additions & 8 deletions lnrpc/routerrpc/router.proto
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,8 @@ message QueryMissionControlRequest {}
/// QueryMissionControlResponse contains mission control state per node.
message QueryMissionControlResponse {
repeated NodeHistory nodes = 1;

repeated PairHistory pairs = 2;
}

/// NodeHistory contains the mission control state for a particular node.
Expand All @@ -338,24 +340,32 @@ message NodeHistory {

/// Estimation of success probability for channels not in the channel array.
float other_chan_success_prob = 3 [json_name = "other_chan_success_prob"];
}

enum PairResult {
SUCCESS = 0;

FAIL = 1;

/// Historical information of particular channels.
repeated ChannelHistory channels = 4 [json_name = "channels"];
FAIL_BALANCE = 2;
}

/// NodeHistory contains the mission control state for a particular channel.
message ChannelHistory {
/// Short channel id
uint64 channel_id = 1 [json_name = "channel_id"];
message PairHistory {
bytes node_a = 1;

bytes node_b = 2;

/// Time stamp of last failure.
int64 last_fail_time = 2 [json_name = "last_fail_time"];
int64 timestamp = 3 [json_name = "timestamp"];

/// Minimum penalization amount.
int64 min_penalize_amt_sat = 3 [json_name = "min_penalize_amt_sat"];
int64 amt = 4 [json_name = "amt"];

/// Estimation of success probability for this channel.
float success_prob = 4 [json_name = "success_prob"];
float success_prob = 5 [json_name = "success_prob"];

PairResult result = 6;
}

service Router {
Expand Down
33 changes: 21 additions & 12 deletions lnrpc/routerrpc/router_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ type RouterBackend struct {
type MissionControl interface {
// GetEdgeProbability is expected to return the success probability of a payment
// from fromNode along edge.
GetEdgeProbability(fromNode route.Vertex,
edge routing.EdgeLocator, amt lnwire.MilliSatoshi) float64
GetEdgeProbability(fromNode, toNode route.Vertex,
amt lnwire.MilliSatoshi) float64

// ResetHistory resets the history of MissionControl returning it to a state as
// if no payment attempts have been made.
Expand Down Expand Up @@ -142,28 +142,37 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
ignoredNodes[ignoreVertex] = struct{}{}
}

ignoredEdges := make(map[routing.EdgeLocator]struct{})
ignoredEdges := make(map[routing.DirectedNodePair]struct{})
for _, ignoredEdge := range in.IgnoredEdges {
locator := routing.EdgeLocator{
ChannelID: ignoredEdge.ChannelId,
a, b, err := r.FetchChannelEndpoints(ignoredEdge.ChannelId)
if err != nil {
log.Warnf("Channel %v unknown", ignoredEdge.ChannelId)
continue
}

var pair routing.DirectedNodePair
if ignoredEdge.DirectionReverse {
locator.Direction = 1
pair.From, pair.To = b, a
} else {
pair.From, pair.To = a, b
}
ignoredEdges[locator] = struct{}{}
ignoredEdges[pair] = struct{}{}
}

restrictions := &routing.RestrictParams{
FeeLimit: feeLimit,
ProbabilitySource: func(node route.Vertex,
edge routing.EdgeLocator,
ProbabilitySource: func(fromNode, toNode route.Vertex,
amt lnwire.MilliSatoshi) float64 {

if _, ok := ignoredNodes[node]; ok {
if _, ok := ignoredNodes[fromNode]; ok {
return 0
}

if _, ok := ignoredEdges[edge]; ok {
pair := routing.DirectedNodePair{
From: fromNode,
To: toNode,
}
if _, ok := ignoredEdges[pair]; ok {
return 0
}

Expand All @@ -172,7 +181,7 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
}

return r.MissionControl.GetEdgeProbability(
node, edge, amt,
fromNode, toNode, amt,
)
},
}
Expand Down
28 changes: 16 additions & 12 deletions lnrpc/routerrpc/router_backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,6 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) {
t.Fatal(err)
}

ignoredEdge := routing.EdgeLocator{
ChannelID: 555,
Direction: 1,
}

request := &lnrpc.QueryRoutesRequest{
PubKey: destKey,
Amt: 100000,
Expand Down Expand Up @@ -92,14 +87,14 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) {
t.Fatal("unexpected fee limit")
}

if restrictions.ProbabilitySource(route.Vertex{},
ignoredEdge, 0,
if restrictions.ProbabilitySource(route.Vertex{2},
route.Vertex{1}, 0,
) != 0 {
t.Fatal("expecting 0% probability for ignored edge")
}

if restrictions.ProbabilitySource(ignoreNodeVertex,
routing.EdgeLocator{}, 0,
route.Vertex{6}, 0,
) != 0 {
t.Fatal("expecting 0% probability for ignored node")
}
Expand All @@ -108,8 +103,8 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) {
if useMissionControl {
expectedProb = testMissionControlProb
}
if restrictions.ProbabilitySource(route.Vertex{},
routing.EdgeLocator{}, 0,
if restrictions.ProbabilitySource(route.Vertex{4},
route.Vertex{5}, 0,
) != expectedProb {
t.Fatal("expecting 100% probability")
}
Expand All @@ -128,6 +123,14 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) {
return 1, nil
},
MissionControl: &mockMissionControl{},
FetchChannelEndpoints: func(chanID uint64) (route.Vertex,
route.Vertex, error) {

if chanID != 555 {
t.Fatal()
}
return route.Vertex{1}, route.Vertex{2}, nil
},
}

resp, err := backend.QueryRoutes(context.Background(), request)
Expand All @@ -142,8 +145,9 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) {
type mockMissionControl struct {
}

func (m *mockMissionControl) GetEdgeProbability(fromNode route.Vertex,
edge routing.EdgeLocator, amt lnwire.MilliSatoshi) float64 {
func (m *mockMissionControl) GetEdgeProbability(fromNode, toNode route.Vertex,
amt lnwire.MilliSatoshi) float64 {

return testMissionControlProb
}

Expand Down
58 changes: 37 additions & 21 deletions lnrpc/routerrpc/router_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,40 +451,56 @@ func (s *Server) QueryMissionControl(ctx context.Context,

snapshot := s.cfg.RouterBackend.MissionControl.GetHistorySnapshot()

rpcNodes := make([]*NodeHistory, len(snapshot.Nodes))
for i, n := range snapshot.Nodes {
rpcNodes := make([]*NodeHistory, 0, len(snapshot.Nodes))
for _, n := range snapshot.Nodes {
// Copy node struct to prevent loop variable binding bugs.
node := n

channels := make([]*ChannelHistory, len(node.Channels))
for j, channel := range node.Channels {
channels[j] = &ChannelHistory{
ChannelId: channel.ChannelID,
LastFailTime: channel.LastFail.Unix(),
MinPenalizeAmtSat: int64(
channel.MinPenalizeAmt.ToSatoshis(),
),
SuccessProb: float32(channel.SuccessProb),
}
rpcNode := NodeHistory{
Pubkey: node.Node[:],
LastFailTime: node.LastFail.Unix(),
OtherChanSuccessProb: float32(
node.OtherChanSuccessProb,
),
}

var lastFail int64
if node.LastFail != nil {
lastFail = node.LastFail.Unix()
rpcNodes = append(rpcNodes, &rpcNode)
}

rpcPairs := make([]*PairHistory, 0, len(snapshot.Pairs))
for _, p := range snapshot.Pairs {
// Prevent binding to loop variable
pair := p

var result PairResult
switch pair.ResultType {
case routing.ChannelResultFail:
result = PairResult_FAIL
case routing.ChannelResultFailBalance:
result = PairResult_FAIL_BALANCE
case routing.ChannelResultSuccess:
result = PairResult_SUCCESS
default:
return nil, errors.New("unknown result")
}

rpcNodes[i] = &NodeHistory{
Pubkey: node.Node[:],
LastFailTime: lastFail,
OtherChanSuccessProb: float32(
node.OtherChanSuccessProb,
rpcPair := PairHistory{
NodeA: pair.NodeA[:],
NodeB: pair.NodeB[:],
Timestamp: pair.Timestamp.Unix(),
Amt: int64(
pair.Amount.ToSatoshis(),
),
Channels: channels,
SuccessProb: float32(pair.SuccessProb),
Result: result,
}

rpcPairs = append(rpcPairs, &rpcPair)
}

response := QueryMissionControlResponse{
Nodes: rpcNodes,
Pairs: rpcPairs,
}

return &response, nil
Expand Down
Loading