lnd.xprv/routing/missioncontrol_test.go
Joost Jager dc13da5abb
routing: move second chance logic into mission control
If nodes return a channel policy related failure, they may get a second
chance. Our graph may not be up to date. Previously this logic was
contained in the payment session.

This commit moves that into global mission control and thereby removes
the last mission control state that was kept on the payment level.

Because mission control is not aware of the relation between payment
attempts and payments, the second chance logic is no longer based
tracking second chances given per payment.

Instead a time based approach is used. If a node reports a policy
failure that prevents forwarding to its peer, it will get a second
chance. But it will get it only if the previous second chance was
long enough ago.

Also those second chances are no longer dependent on whether an
associated channel update is valid. It will get the second chance
regardless, to prevent creating a dependency between mission control and
the graph. This would interfer with (future) replay of history, because
the graph may not be the same anymore at that point.
2019-07-13 22:38:23 +02:00

131 lines
2.9 KiB
Go

package routing
import (
"testing"
"time"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
var (
mcTestNode = route.Vertex{}
mcTestEdge = EdgeLocator{
ChannelID: 123,
}
mcTestTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
)
type mcTestContext struct {
t *testing.T
mc *MissionControl
now time.Time
}
func createMcTestContext(t *testing.T) *mcTestContext {
ctx := &mcTestContext{
t: t,
now: mcTestTime,
}
mc := NewMissionControl(
&MissionControlConfig{
PenaltyHalfLife: 30 * time.Minute,
AprioriHopProbability: 0.8,
},
)
mc.now = func() time.Time { return ctx.now }
ctx.mc = mc
return ctx
}
// Assert that mission control returns a probability for an edge.
func (ctx *mcTestContext) expectP(amt lnwire.MilliSatoshi,
expected float64) {
ctx.t.Helper()
p := ctx.mc.GetEdgeProbability(mcTestNode, mcTestEdge, amt)
if p != expected {
ctx.t.Fatalf("unexpected probability %v", p)
}
}
// TestMissionControl tests mission control probability estimation.
func TestMissionControl(t *testing.T) {
ctx := createMcTestContext(t)
ctx.now = testTime
testTime := time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
testNode := route.Vertex{}
testEdge := edge{
channel: 123,
}
// Initial probability is expected to be 1.
ctx.expectP(1000, 0.8)
// Expect probability to be zero after reporting the edge as failed.
ctx.mc.ReportEdgeFailure(testEdge, 1000)
ctx.expectP(1000, 0)
// As we reported with a min penalization amt, a lower amt than reported
// should be unaffected.
ctx.expectP(500, 0.8)
// Edge decay started.
ctx.now = testTime.Add(30 * time.Minute)
ctx.expectP(1000, 0.4)
// Edge fails again, this time without a min penalization amt. The edge
// should be penalized regardless of amount.
ctx.mc.ReportEdgeFailure(testEdge, 0)
ctx.expectP(1000, 0)
ctx.expectP(500, 0)
// Edge decay started.
ctx.now = testTime.Add(60 * time.Minute)
ctx.expectP(1000, 0.4)
// A node level failure should bring probability of every channel back
// to zero.
ctx.mc.ReportVertexFailure(testNode)
ctx.expectP(1000, 0)
// Check whether history snapshot looks sane.
history := ctx.mc.GetHistorySnapshot()
if len(history.Nodes) != 1 {
t.Fatal("unexpected number of nodes")
}
if len(history.Nodes[0].Channels) != 1 {
t.Fatal("unexpected number of channels")
}
}
// TestMissionControlChannelUpdate tests that the first channel update is not
// penalizing the channel yet.
func TestMissionControlChannelUpdate(t *testing.T) {
ctx := createMcTestContext(t)
testEdge := edge{
channel: 123,
}
// Report a policy related failure. Because it is the first, we don't
// expect a penalty.
ctx.mc.ReportEdgePolicyFailure(testEdge)
ctx.expectP(0, 0.8)
// Report another failure for the same channel. We expect it to be
// pruned.
ctx.mc.ReportEdgePolicyFailure(testEdge)
ctx.expectP(0, 0)
}