ff0c5a0d5e
This commit modifies paymentLifecycle so that it not only feeds failures into mission control, but successes as well. This allows for more accurate probability estimates. Previously, the success probability for a successful pair and a pair with no history was equal. There was no force that pushed towards previously successful routes.
207 lines
4.5 KiB
Go
207 lines
4.5 KiB
Go
package routing
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
|
)
|
|
|
|
var (
|
|
hops = []route.Vertex{
|
|
{1, 0}, {1, 1}, {1, 2}, {1, 3}, {1, 4},
|
|
}
|
|
|
|
routeOneHop = route.Route{
|
|
SourcePubKey: hops[0],
|
|
TotalAmount: 100,
|
|
Hops: []*route.Hop{
|
|
{PubKeyBytes: hops[1], AmtToForward: 99},
|
|
},
|
|
}
|
|
|
|
routeTwoHop = route.Route{
|
|
SourcePubKey: hops[0],
|
|
TotalAmount: 100,
|
|
Hops: []*route.Hop{
|
|
{PubKeyBytes: hops[1], AmtToForward: 99},
|
|
{PubKeyBytes: hops[2], AmtToForward: 97},
|
|
},
|
|
}
|
|
|
|
routeFourHop = route.Route{
|
|
SourcePubKey: hops[0],
|
|
TotalAmount: 100,
|
|
Hops: []*route.Hop{
|
|
{PubKeyBytes: hops[1], AmtToForward: 99},
|
|
{PubKeyBytes: hops[2], AmtToForward: 97},
|
|
{PubKeyBytes: hops[3], AmtToForward: 94},
|
|
{PubKeyBytes: hops[4], AmtToForward: 90},
|
|
},
|
|
}
|
|
)
|
|
|
|
func getTestPair(from, to int) DirectedNodePair {
|
|
return NewDirectedNodePair(hops[from], hops[to])
|
|
}
|
|
|
|
type resultTestCase struct {
|
|
name string
|
|
route *route.Route
|
|
success bool
|
|
failureSrcIdx int
|
|
failure lnwire.FailureMessage
|
|
|
|
expectedResult *interpretedResult
|
|
}
|
|
|
|
var resultTestCases = []resultTestCase{
|
|
// Tests that a temporary channel failure result is properly
|
|
// interpreted.
|
|
{
|
|
name: "fail",
|
|
route: &routeTwoHop,
|
|
failureSrcIdx: 1,
|
|
failure: lnwire.NewTemporaryChannelFailure(nil),
|
|
|
|
expectedResult: &interpretedResult{
|
|
pairResults: map[DirectedNodePair]pairResult{
|
|
getTestPair(0, 1): {
|
|
success: true,
|
|
},
|
|
getTestPair(1, 2): {
|
|
minPenalizeAmt: 99,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Tests that a expiry too soon failure result is properly interpreted.
|
|
{
|
|
name: "fail expiry too soon",
|
|
route: &routeFourHop,
|
|
failureSrcIdx: 3,
|
|
failure: lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{}),
|
|
|
|
expectedResult: &interpretedResult{
|
|
pairResults: map[DirectedNodePair]pairResult{
|
|
getTestPair(0, 1): {},
|
|
getTestPair(1, 0): {},
|
|
getTestPair(1, 2): {},
|
|
getTestPair(2, 1): {},
|
|
getTestPair(2, 3): {},
|
|
getTestPair(3, 2): {},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Tests an incorrect payment details result. This should be a final
|
|
// failure, but mark all pairs along the route as successful.
|
|
{
|
|
name: "fail incorrect details",
|
|
route: &routeTwoHop,
|
|
failureSrcIdx: 2,
|
|
failure: lnwire.NewFailIncorrectDetails(97),
|
|
|
|
expectedResult: &interpretedResult{
|
|
pairResults: map[DirectedNodePair]pairResult{
|
|
getTestPair(0, 1): {
|
|
success: true,
|
|
},
|
|
getTestPair(1, 2): {
|
|
success: true,
|
|
},
|
|
},
|
|
finalFailureReason: &reasonIncorrectDetails,
|
|
},
|
|
},
|
|
|
|
// Tests a successful direct payment.
|
|
{
|
|
name: "success direct",
|
|
route: &routeOneHop,
|
|
success: true,
|
|
|
|
expectedResult: &interpretedResult{
|
|
pairResults: map[DirectedNodePair]pairResult{
|
|
getTestPair(0, 1): {
|
|
success: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Tests a successful two hop payment.
|
|
{
|
|
name: "success",
|
|
route: &routeTwoHop,
|
|
success: true,
|
|
|
|
expectedResult: &interpretedResult{
|
|
pairResults: map[DirectedNodePair]pairResult{
|
|
getTestPair(0, 1): {
|
|
success: true,
|
|
},
|
|
getTestPair(1, 2): {
|
|
success: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Tests a malformed htlc from a direct peer.
|
|
{
|
|
name: "fail malformed htlc from direct peer",
|
|
route: &routeTwoHop,
|
|
failureSrcIdx: 0,
|
|
failure: lnwire.NewInvalidOnionKey(nil),
|
|
|
|
expectedResult: &interpretedResult{
|
|
nodeFailure: &hops[1],
|
|
},
|
|
},
|
|
|
|
// Tests a malformed htlc from a direct peer that is also the final
|
|
// destination.
|
|
{
|
|
name: "fail malformed htlc from direct final peer",
|
|
route: &routeOneHop,
|
|
failureSrcIdx: 0,
|
|
failure: lnwire.NewInvalidOnionKey(nil),
|
|
|
|
expectedResult: &interpretedResult{
|
|
finalFailureReason: &reasonError,
|
|
nodeFailure: &hops[1],
|
|
},
|
|
},
|
|
}
|
|
|
|
// TestResultInterpretation executes a list of test cases that test the result
|
|
// interpretation logic.
|
|
func TestResultInterpretation(t *testing.T) {
|
|
emptyResults := make(map[DirectedNodePair]pairResult)
|
|
|
|
for _, testCase := range resultTestCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
i := interpretResult(
|
|
testCase.route, testCase.success,
|
|
&testCase.failureSrcIdx, testCase.failure,
|
|
)
|
|
|
|
expected := testCase.expectedResult
|
|
|
|
// Replace nil pairResults with empty map to satisfy
|
|
// DeepEqual.
|
|
if expected.pairResults == nil {
|
|
expected.pairResults = emptyResults
|
|
}
|
|
|
|
if !reflect.DeepEqual(i, expected) {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
}
|
|
}
|