Merge pull request #3556 from joostjager/query-mc-prob
routerrpc: add queryprob rpc
This commit is contained in:
11 files changed
+582
-204
No files matched your search
+57
-29
@@ -53,7 +53,7 @@ const (
|
||||
)
|
||||
|
||||
// NodeResults contains previous results from a node to its peers.
|
||||
type NodeResults map[route.Vertex]timedPairResult
|
||||
type NodeResults map[route.Vertex]TimedPairResult
|
||||
|
||||
// MissionControl contains state which summarizes the past attempts of HTLC
|
||||
// routing by external callers when sending payments throughout the network. It
|
||||
@@ -120,12 +120,29 @@ type MissionControlConfig struct {
|
||||
AprioriWeight float64
|
||||
}
|
||||
|
||||
// timedPairResult describes a timestamped pair result.
|
||||
type timedPairResult struct {
|
||||
// TimedPairResult describes a timestamped pair result.
|
||||
type TimedPairResult struct {
|
||||
// timestamp is the time when this result was obtained.
|
||||
timestamp time.Time
|
||||
Timestamp time.Time
|
||||
|
||||
pairResult
|
||||
// minPenalizeAmt is the minimum amount for which a penalty should be
|
||||
// applied based on this result. Only applies to fail results.
|
||||
MinPenalizeAmt lnwire.MilliSatoshi
|
||||
|
||||
// success indicates whether the payment attempt was successful through
|
||||
// this pair.
|
||||
Success bool
|
||||
}
|
||||
|
||||
// newTimedPairResult wraps a pair result with a timestamp.
|
||||
func newTimedPairResult(timestamp time.Time,
|
||||
result pairResult) TimedPairResult {
|
||||
|
||||
return TimedPairResult{
|
||||
Timestamp: timestamp,
|
||||
MinPenalizeAmt: result.minPenalizeAmt,
|
||||
Success: result.success,
|
||||
}
|
||||
}
|
||||
|
||||
// MissionControlSnapshot contains a snapshot of the current state of mission
|
||||
@@ -142,16 +159,8 @@ type MissionControlPairSnapshot struct {
|
||||
// Pair is the node pair of which the state is described.
|
||||
Pair DirectedNodePair
|
||||
|
||||
// Timestamp is the time of last result.
|
||||
Timestamp time.Time
|
||||
|
||||
// MinPenalizeAmt is the minimum amount for which the channel will be
|
||||
// penalized.
|
||||
MinPenalizeAmt lnwire.MilliSatoshi
|
||||
|
||||
// LastAttemptSuccessful indicates whether the last payment attempt
|
||||
// through this pair was successful.
|
||||
LastAttemptSuccessful bool
|
||||
// TimedPairResult contains the data for this pair.
|
||||
TimedPairResult
|
||||
}
|
||||
|
||||
// paymentResult is the information that becomes available when a payment
|
||||
@@ -257,7 +266,7 @@ func (m *MissionControl) GetProbability(fromNode, toNode route.Vertex,
|
||||
|
||||
// setLastPairResult stores a result for a node pair.
|
||||
func (m *MissionControl) setLastPairResult(fromNode,
|
||||
toNode route.Vertex, result timedPairResult) {
|
||||
toNode route.Vertex, result TimedPairResult) {
|
||||
|
||||
nodePairs, ok := m.lastPairResult[fromNode]
|
||||
if !ok {
|
||||
@@ -278,10 +287,9 @@ func (m *MissionControl) setAllFail(fromNode route.Vertex,
|
||||
}
|
||||
|
||||
for connection := range nodePairs {
|
||||
nodePairs[connection] = timedPairResult{
|
||||
timestamp: timestamp,
|
||||
pairResult: failPairResult(0),
|
||||
}
|
||||
nodePairs[connection] = newTimedPairResult(
|
||||
timestamp, failPairResult(0),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,14 +337,11 @@ func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
|
||||
|
||||
for fromNode, fromPairs := range m.lastPairResult {
|
||||
for toNode, result := range fromPairs {
|
||||
|
||||
pair := NewDirectedNodePair(fromNode, toNode)
|
||||
|
||||
pairSnapshot := MissionControlPairSnapshot{
|
||||
Pair: pair,
|
||||
MinPenalizeAmt: result.minPenalizeAmt,
|
||||
Timestamp: result.timestamp,
|
||||
LastAttemptSuccessful: result.success,
|
||||
Pair: pair,
|
||||
TimedPairResult: result,
|
||||
}
|
||||
|
||||
pairs = append(pairs, pairSnapshot)
|
||||
@@ -350,6 +355,26 @@ func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
|
||||
return &snapshot
|
||||
}
|
||||
|
||||
// GetPairHistorySnapshot returns the stored history for a given node pair.
|
||||
func (m *MissionControl) GetPairHistorySnapshot(
|
||||
fromNode, toNode route.Vertex) TimedPairResult {
|
||||
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
results, ok := m.lastPairResult[fromNode]
|
||||
if !ok {
|
||||
return TimedPairResult{}
|
||||
}
|
||||
|
||||
result, ok := results[toNode]
|
||||
if !ok {
|
||||
return TimedPairResult{}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ReportPaymentFail reports a failed payment to mission control as input for
|
||||
// future probability estimates. The failureSourceIdx argument indicates the
|
||||
// failure source. If it is nil, the failure source is unknown. This function
|
||||
@@ -468,10 +493,13 @@ func (m *MissionControl) applyPaymentResult(
|
||||
pair, pairResult.minPenalizeAmt)
|
||||
}
|
||||
|
||||
m.setLastPairResult(pair.From, pair.To, timedPairResult{
|
||||
timestamp: result.timeReply,
|
||||
pairResult: pairResult,
|
||||
})
|
||||
m.setLastPairResult(
|
||||
pair.From, pair.To,
|
||||
newTimedPairResult(
|
||||
result.timeReply,
|
||||
pairResult,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return i.finalFailureReason
|
||||
|
||||
@@ -80,19 +80,19 @@ func (p *probabilityEstimator) getNodeProbability(now time.Time,
|
||||
totalWeight := aprioriFactor
|
||||
|
||||
for _, result := range results {
|
||||
age := now.Sub(result.timestamp)
|
||||
age := now.Sub(result.Timestamp)
|
||||
|
||||
switch {
|
||||
// Weigh success with a constant high weight of 1. There is no
|
||||
// decay.
|
||||
case result.success:
|
||||
case result.Success:
|
||||
totalWeight++
|
||||
probabilitiesTotal += p.prevSuccessProbability
|
||||
|
||||
// Weigh failures in accordance with their age. The base
|
||||
// probability of a failure is considered zero, so nothing needs
|
||||
// to be added to probabilitiesTotal.
|
||||
case amt >= result.minPenalizeAmt:
|
||||
case amt >= result.MinPenalizeAmt:
|
||||
totalWeight += p.getWeight(age)
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,7 @@ func (p *probabilityEstimator) getPairProbability(
|
||||
|
||||
// For successes, we have a fixed (high) probability. Those pairs
|
||||
// will be assumed good until proven otherwise.
|
||||
if lastPairResult.success {
|
||||
if lastPairResult.Success {
|
||||
return p.prevSuccessProbability
|
||||
}
|
||||
|
||||
@@ -138,11 +138,11 @@ func (p *probabilityEstimator) getPairProbability(
|
||||
// penalization. If the current amount is smaller than the amount that
|
||||
// previously triggered a failure, we act as if this is an untried
|
||||
// channel.
|
||||
if amt < lastPairResult.minPenalizeAmt {
|
||||
if amt < lastPairResult.MinPenalizeAmt {
|
||||
return nodeProbability
|
||||
}
|
||||
|
||||
timeSinceLastFailure := now.Sub(lastPairResult.timestamp)
|
||||
timeSinceLastFailure := now.Sub(lastPairResult.Timestamp)
|
||||
|
||||
// Calculate success probability based on the weight of the last
|
||||
// failure. When the failure is fresh, its weight is 1 and we'll return
|
||||
|
||||
@@ -33,7 +33,7 @@ type estimatorTestContext struct {
|
||||
// corresponds to the last result towards a node. The list index equals
|
||||
// the node id. So the first element in the list is the result towards
|
||||
// node 0.
|
||||
results map[int]timedPairResult
|
||||
results map[int]TimedPairResult
|
||||
}
|
||||
|
||||
func newEstimatorTestContext(t *testing.T) *estimatorTestContext {
|
||||
@@ -83,11 +83,11 @@ func TestProbabilityEstimatorNoResults(t *testing.T) {
|
||||
func TestProbabilityEstimatorOneSuccess(t *testing.T) {
|
||||
ctx := newEstimatorTestContext(t)
|
||||
|
||||
ctx.results = map[int]timedPairResult{
|
||||
node1: {
|
||||
timestamp: testTime.Add(-time.Hour),
|
||||
pairResult: successPairResult(),
|
||||
},
|
||||
ctx.results = map[int]TimedPairResult{
|
||||
node1: newTimedPairResult(
|
||||
testTime.Add(-time.Hour),
|
||||
successPairResult(),
|
||||
),
|
||||
}
|
||||
|
||||
// Because of the previous success, this channel keep reporting a high
|
||||
@@ -107,11 +107,11 @@ func TestProbabilityEstimatorOneSuccess(t *testing.T) {
|
||||
func TestProbabilityEstimatorOneFailure(t *testing.T) {
|
||||
ctx := newEstimatorTestContext(t)
|
||||
|
||||
ctx.results = map[int]timedPairResult{
|
||||
node1: {
|
||||
timestamp: testTime.Add(-time.Hour),
|
||||
pairResult: failPairResult(0),
|
||||
},
|
||||
ctx.results = map[int]TimedPairResult{
|
||||
node1: newTimedPairResult(
|
||||
testTime.Add(-time.Hour),
|
||||
failPairResult(0),
|
||||
),
|
||||
}
|
||||
|
||||
// For an untried node, we expected the node probability. The weight for
|
||||
@@ -130,19 +130,19 @@ func TestProbabilityEstimatorOneFailure(t *testing.T) {
|
||||
func TestProbabilityEstimatorMix(t *testing.T) {
|
||||
ctx := newEstimatorTestContext(t)
|
||||
|
||||
ctx.results = map[int]timedPairResult{
|
||||
node1: {
|
||||
timestamp: testTime.Add(-time.Hour),
|
||||
pairResult: successPairResult(),
|
||||
},
|
||||
node2: {
|
||||
timestamp: testTime.Add(-2 * time.Hour),
|
||||
pairResult: failPairResult(0),
|
||||
},
|
||||
node3: {
|
||||
timestamp: testTime.Add(-3 * time.Hour),
|
||||
pairResult: failPairResult(0),
|
||||
},
|
||||
ctx.results = map[int]TimedPairResult{
|
||||
node1: newTimedPairResult(
|
||||
testTime.Add(-time.Hour),
|
||||
successPairResult(),
|
||||
),
|
||||
node2: newTimedPairResult(
|
||||
testTime.Add(-2*time.Hour),
|
||||
failPairResult(0),
|
||||
),
|
||||
node3: newTimedPairResult(
|
||||
testTime.Add(-3*time.Hour),
|
||||
failPairResult(0),
|
||||
),
|
||||
}
|
||||
|
||||
// We expect the probability for a previously successful channel to
|
||||
|
||||
Reference in new issue
Block a user