mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-25 23:26:52 +02:00
routing: add option to force import MC pair history
This commit adds the `force` flag to the `XImportMissionControl` RPC which allows skipping rules around the pair import except for what is mandatory to make values meaningful. This can be useful for when clients would like to forcibly override MC state in order to manipulate routing results.
This commit is contained in:
@@ -363,7 +363,9 @@ func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
|
||||
// ImportHistory imports the set of mission control results provided to our
|
||||
// in-memory state. These results are not persisted, so will not survive
|
||||
// restarts.
|
||||
func (m *MissionControl) ImportHistory(history *MissionControlSnapshot) error {
|
||||
func (m *MissionControl) ImportHistory(history *MissionControlSnapshot,
|
||||
force bool) error {
|
||||
|
||||
if history == nil {
|
||||
return errors.New("cannot import nil history")
|
||||
}
|
||||
@@ -374,7 +376,7 @@ func (m *MissionControl) ImportHistory(history *MissionControlSnapshot) error {
|
||||
log.Infof("Importing history snapshot with %v pairs to mission control",
|
||||
len(history.Pairs))
|
||||
|
||||
imported := m.state.importSnapshot(history)
|
||||
imported := m.state.importSnapshot(history, force)
|
||||
|
||||
log.Infof("Imported %v results to mission control", imported)
|
||||
|
||||
@@ -520,7 +522,7 @@ func (m *MissionControl) applyPaymentResult(
|
||||
}
|
||||
|
||||
m.state.setLastPairResult(
|
||||
pair.From, pair.To, result.timeReply, &pairResult,
|
||||
pair.From, pair.To, result.timeReply, &pairResult, false,
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -54,7 +54,7 @@ func (m *missionControlState) resetHistory() {
|
||||
|
||||
// setLastPairResult stores a result for a node pair.
|
||||
func (m *missionControlState) setLastPairResult(fromNode, toNode route.Vertex,
|
||||
timestamp time.Time, result *pairResult) {
|
||||
timestamp time.Time, result *pairResult, force bool) {
|
||||
|
||||
nodePairs, ok := m.lastPairResult[fromNode]
|
||||
if !ok {
|
||||
@@ -74,7 +74,7 @@ func (m *missionControlState) setLastPairResult(fromNode, toNode route.Vertex,
|
||||
// prevents the success range from shrinking when there is no
|
||||
// reason to do so. For example: small amount probes shouldn't
|
||||
// affect a previous success for a much larger amount.
|
||||
if successAmt > current.SuccessAmt {
|
||||
if force || successAmt > current.SuccessAmt {
|
||||
current.SuccessAmt = successAmt
|
||||
}
|
||||
|
||||
@@ -83,7 +83,9 @@ func (m *missionControlState) setLastPairResult(fromNode, toNode route.Vertex,
|
||||
// are likely to succeed. We don't want to clear the failure
|
||||
// completely, because we haven't learnt much for amounts above
|
||||
// the current success amount.
|
||||
if !current.FailTime.IsZero() && successAmt >= current.FailAmt {
|
||||
if force || (!current.FailTime.IsZero() &&
|
||||
successAmt >= current.FailAmt) {
|
||||
|
||||
current.FailAmt = successAmt + 1
|
||||
}
|
||||
} else {
|
||||
@@ -100,7 +102,7 @@ func (m *missionControlState) setLastPairResult(fromNode, toNode route.Vertex,
|
||||
// come in out of order. This check makes it easier for payment
|
||||
// processes to converge to a final state.
|
||||
failInterval := timestamp.Sub(current.FailTime)
|
||||
if failAmt > current.FailAmt &&
|
||||
if !force && failAmt > current.FailAmt &&
|
||||
failInterval < m.minFailureRelaxInterval {
|
||||
|
||||
log.Debugf("Ignoring higher amount failure within min "+
|
||||
@@ -213,7 +215,9 @@ func (m *missionControlState) getSnapshot() *MissionControlSnapshot {
|
||||
// importSnapshot takes an existing snapshot and merges it with our current
|
||||
// state if the result provided are fresher than our current results. It returns
|
||||
// the number of pairs that were used.
|
||||
func (m *missionControlState) importSnapshot(snapshot *MissionControlSnapshot) int {
|
||||
func (m *missionControlState) importSnapshot(snapshot *MissionControlSnapshot,
|
||||
force bool) int {
|
||||
|
||||
var imported int
|
||||
|
||||
for _, pair := range snapshot.Pairs {
|
||||
@@ -230,13 +234,13 @@ func (m *missionControlState) importSnapshot(snapshot *MissionControlSnapshot) i
|
||||
failResult := failPairResult(pair.FailAmt)
|
||||
imported += m.importResult(
|
||||
lastResult.FailTime, pair.FailTime, failResult,
|
||||
fromNode, toNode,
|
||||
fromNode, toNode, force,
|
||||
)
|
||||
|
||||
successResult := successPairResult(pair.SuccessAmt)
|
||||
imported += m.importResult(
|
||||
lastResult.SuccessTime, pair.SuccessTime, successResult,
|
||||
fromNode, toNode,
|
||||
fromNode, toNode, force,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -244,9 +248,10 @@ func (m *missionControlState) importSnapshot(snapshot *MissionControlSnapshot) i
|
||||
}
|
||||
|
||||
func (m *missionControlState) importResult(currentTs, importedTs time.Time,
|
||||
importedResult pairResult, fromNode, toNode route.Vertex) int {
|
||||
importedResult pairResult, fromNode, toNode route.Vertex,
|
||||
force bool) int {
|
||||
|
||||
if currentTs.After(importedTs) {
|
||||
if !force && currentTs.After(importedTs) {
|
||||
log.Debugf("Not setting pair result for %v->%v (%v) "+
|
||||
"success=%v, timestamp %v older than last result %v",
|
||||
fromNode, toNode, importedResult.amt,
|
||||
@@ -255,7 +260,9 @@ func (m *missionControlState) importResult(currentTs, importedTs time.Time,
|
||||
return 0
|
||||
}
|
||||
|
||||
m.setLastPairResult(fromNode, toNode, importedTs, &importedResult)
|
||||
m.setLastPairResult(
|
||||
fromNode, toNode, importedTs, &importedResult, force,
|
||||
)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
@@ -5,11 +5,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestMissionControlStateFailureResult tests setting failure results on the
|
||||
// mission control state.
|
||||
func TestMissionControlStateFailureResult(t *testing.T) {
|
||||
// TestMissionControlStateSetLastPairResult tests setting mission control state
|
||||
// pair results.
|
||||
func TestMissionControlStateSetLastPairResult(t *testing.T) {
|
||||
const minFailureRelaxInterval = time.Minute
|
||||
state := newMissionControlState(minFailureRelaxInterval)
|
||||
|
||||
@@ -20,7 +21,9 @@ func TestMissionControlStateFailureResult(t *testing.T) {
|
||||
)
|
||||
|
||||
// Report a 1000 sat failure.
|
||||
state.setLastPairResult(from, to, timestamp, &pairResult{amt: 1000})
|
||||
state.setLastPairResult(
|
||||
from, to, timestamp, &pairResult{amt: 1000}, false,
|
||||
)
|
||||
result, _ := state.getLastPairResult(from)
|
||||
if result[to].FailAmt != 1000 {
|
||||
t.Fatalf("unexpected fail amount %v", result[to].FailAmt)
|
||||
@@ -29,7 +32,9 @@ func TestMissionControlStateFailureResult(t *testing.T) {
|
||||
// Report an 1100 sat failure one hour later. It is expected to
|
||||
// overwrite the previous failure.
|
||||
timestamp = timestamp.Add(time.Hour)
|
||||
state.setLastPairResult(from, to, timestamp, &pairResult{amt: 1100})
|
||||
state.setLastPairResult(
|
||||
from, to, timestamp, &pairResult{amt: 1100}, false,
|
||||
)
|
||||
result, _ = state.getLastPairResult(from)
|
||||
if result[to].FailAmt != 1100 {
|
||||
t.Fatalf("unexpected fail amount %v", result[to].FailAmt)
|
||||
@@ -39,9 +44,51 @@ func TestMissionControlStateFailureResult(t *testing.T) {
|
||||
// the failure amount is too soon after the previous failure, the result
|
||||
// is not applied.
|
||||
timestamp = timestamp.Add(time.Second)
|
||||
state.setLastPairResult(from, to, timestamp, &pairResult{amt: 1200})
|
||||
state.setLastPairResult(
|
||||
from, to, timestamp, &pairResult{amt: 1200}, false,
|
||||
)
|
||||
result, _ = state.getLastPairResult(from)
|
||||
if result[to].FailAmt != 1100 {
|
||||
t.Fatalf("unexpected fail amount %v", result[to].FailAmt)
|
||||
}
|
||||
|
||||
// Roll back time 1 second to test forced import.
|
||||
timestamp = testTime
|
||||
state.setLastPairResult(
|
||||
from, to, timestamp, &pairResult{amt: 999}, true,
|
||||
)
|
||||
result, _ = state.getLastPairResult(from)
|
||||
require.Equal(t, 999, int(result[to].FailAmt))
|
||||
|
||||
// Report an 1500 sat success.
|
||||
timestamp = timestamp.Add(time.Second)
|
||||
state.setLastPairResult(
|
||||
from, to, timestamp, &pairResult{amt: 1500, success: true}, false,
|
||||
)
|
||||
result, _ = state.getLastPairResult(from)
|
||||
// We don't expect the failtime to change, only the fail amount, we
|
||||
// expect however change of both the success time and the success amount.
|
||||
expected := TimedPairResult{
|
||||
FailTime: timestamp.Add(-time.Second),
|
||||
FailAmt: 1501,
|
||||
SuccessTime: timestamp,
|
||||
SuccessAmt: 1500,
|
||||
}
|
||||
require.Equal(t, expected, result[to])
|
||||
|
||||
// Again roll back time to test forced import.
|
||||
state.setLastPairResult(
|
||||
from, to, testTime, &pairResult{amt: 50, success: true}, true,
|
||||
)
|
||||
result, _ = state.getLastPairResult(from)
|
||||
// We don't expect the failtime to change, only the fail amount, we
|
||||
// expect however change of both the success time and the success amount.
|
||||
expected = TimedPairResult{
|
||||
FailTime: timestamp.Add(-time.Second),
|
||||
FailAmt: 51,
|
||||
SuccessTime: testTime,
|
||||
SuccessAmt: 50,
|
||||
}
|
||||
require.Equal(t, expected, result[to])
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user