lnd.xprv/routing/result_interpretation_test.go

229 lines
5.6 KiB
Go
Raw Normal View History

package routing
import (
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"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])
}
func getPolicyFailure(from, to int) *DirectedNodePair {
pair := getTestPair(from, to)
return &pair
}
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): successPairResult(),
getTestPair(1, 2): failPairResult(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): failPairResult(0),
getTestPair(1, 0): failPairResult(0),
getTestPair(1, 2): failPairResult(0),
getTestPair(2, 1): failPairResult(0),
getTestPair(2, 3): failPairResult(0),
getTestPair(3, 2): failPairResult(0),
},
},
},
// 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, 0),
expectedResult: &interpretedResult{
pairResults: map[DirectedNodePair]pairResult{
getTestPair(0, 1): successPairResult(),
getTestPair(1, 2): successPairResult(),
},
finalFailureReason: &reasonIncorrectDetails,
},
},
// Tests a successful direct payment.
{
name: "success direct",
route: &routeOneHop,
success: true,
expectedResult: &interpretedResult{
pairResults: map[DirectedNodePair]pairResult{
getTestPair(0, 1): successPairResult(),
},
},
},
// Tests a successful two hop payment.
{
name: "success",
route: &routeTwoHop,
success: true,
expectedResult: &interpretedResult{
pairResults: map[DirectedNodePair]pairResult{
getTestPair(0, 1): successPairResult(),
getTestPair(1, 2): successPairResult(),
},
},
},
// 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],
pairResults: map[DirectedNodePair]pairResult{
getTestPair(1, 0): failPairResult(0),
getTestPair(1, 2): failPairResult(0),
},
},
},
// 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],
pairResults: map[DirectedNodePair]pairResult{
getTestPair(1, 0): failPairResult(0),
},
},
},
// Tests that a fee insufficient failure to an intermediate hop with
// index 2 results in the first hop marked as success, and then a
// bidirectional failure for the incoming channel. It should also result
// in a policy failure for the outgoing hop.
{
name: "fail fee insufficient intermediate",
route: &routeFourHop,
failureSrcIdx: 2,
failure: lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate{}),
expectedResult: &interpretedResult{
pairResults: map[DirectedNodePair]pairResult{
getTestPair(0, 1): {
success: true,
},
getTestPair(1, 2): {},
getTestPair(2, 1): {},
},
policyFailure: getPolicyFailure(2, 3),
},
},
}
// 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.Fatalf("unexpected result\nwant: %v\ngot: %v",
spew.Sdump(expected), spew.Sdump(i))
}
})
}
}