lntemp+itest: refactor testHtlcErrorPropagation

This commit is contained in:
yyforyongyu 2022-08-05 18:36:15 +08:00
parent 2a7830d442
commit 130c4e325a
No known key found for this signature in database
GPG Key ID: 9BCD95C4FF296868
5 changed files with 336 additions and 237 deletions

View File

@ -19,6 +19,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntemp/node" "github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntemp/rpc" "github.com/lightningnetwork/lnd/lntemp/rpc"
@ -1759,3 +1760,95 @@ func (h *HarnessTest) ReceiveTrackPayment(
return nil return nil
} }
// ReceiveHtlcEvent waits until a message is received on the subscribe
// htlc event stream or the timeout is reached.
func (h *HarnessTest) ReceiveHtlcEvent(
stream rpc.HtlcEventsClient) *routerrpc.HtlcEvent {
chanMsg := make(chan *routerrpc.HtlcEvent)
errChan := make(chan error)
go func() {
// Consume one message. This will block until the message is
// received.
resp, err := stream.Recv()
if err != nil {
errChan <- err
return
}
chanMsg <- resp
}()
select {
case <-time.After(DefaultTimeout):
require.Fail(h, "timeout", "timeout receiving htlc "+
"event update")
case err := <-errChan:
require.Failf(h, "err from stream",
"received err from stream: %v", err)
case updateMsg := <-chanMsg:
return updateMsg
}
return nil
}
// AssertHtlcEventType consumes one event from a client and asserts the event
// type is matched.
func (h *HarnessTest) AssertHtlcEventType(client rpc.HtlcEventsClient,
userType routerrpc.HtlcEvent_EventType) *routerrpc.HtlcEvent {
event := h.ReceiveHtlcEvent(client)
require.Equalf(h, userType, event.EventType, "wrong event type, "+
"want %v got %v", userType, event.EventType)
return event
}
// HtlcEvent maps the series of event types used in `*routerrpc.HtlcEvent_*`.
type HtlcEvent int
const (
HtlcEventForward HtlcEvent = iota
HtlcEventForwardFail
HtlcEventSettle
HtlcEventLinkFail
HtlcEventFinal
)
// AssertHtlcEventType consumes one event from a client and asserts both the
// user event type the event.Event type is matched.
func (h *HarnessTest) AssertHtlcEventTypes(client rpc.HtlcEventsClient,
userType routerrpc.HtlcEvent_EventType,
eventType HtlcEvent) *routerrpc.HtlcEvent {
event := h.ReceiveHtlcEvent(client)
require.Equalf(h, userType, event.EventType, "wrong event type, "+
"want %v got %v", userType, event.EventType)
var ok bool
switch eventType {
case HtlcEventForward:
_, ok = event.Event.(*routerrpc.HtlcEvent_ForwardEvent)
case HtlcEventForwardFail:
_, ok = event.Event.(*routerrpc.HtlcEvent_ForwardFailEvent)
case HtlcEventSettle:
_, ok = event.Event.(*routerrpc.HtlcEvent_SettleEvent)
case HtlcEventLinkFail:
_, ok = event.Event.(*routerrpc.HtlcEvent_LinkFailEvent)
case HtlcEventFinal:
_, ok = event.Event.(*routerrpc.HtlcEvent_FinalHtlcEvent)
}
require.Truef(h, ok, "wrong event type: %T, want %T", event.Event,
eventType)
return event
}

View File

@ -39,3 +39,56 @@ func (h *HarnessRPC) SendPayment(
return stream return stream
} }
type HtlcEventsClient routerrpc.Router_SubscribeHtlcEventsClient
// SubscribeHtlcEvents makes a subscription to the HTLC events and returns a
// htlc event client.
func (h *HarnessRPC) SubscribeHtlcEvents() HtlcEventsClient {
// Use runCtx here to keep the client alive for the scope of the test.
client, err := h.Router.SubscribeHtlcEvents(
h.runCtx, &routerrpc.SubscribeHtlcEventsRequest{},
)
h.NoError(err, "SubscribeHtlcEvents")
return client
}
// GetMissionControlConfig makes a RPC call to the node's
// GetMissionControlConfig and asserts.
//
//nolint:lll
func (h *HarnessRPC) GetMissionControlConfig() *routerrpc.GetMissionControlConfigResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
req := &routerrpc.GetMissionControlConfigRequest{}
resp, err := h.Router.GetMissionControlConfig(ctxt, req)
h.NoError(err, "GetMissionControlConfig")
return resp
}
// SetMissionControlConfig makes a RPC call to the node's
// SetMissionControlConfig and asserts.
func (h *HarnessRPC) SetMissionControlConfig(
config *routerrpc.MissionControlConfig) {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
req := &routerrpc.SetMissionControlConfigRequest{Config: config}
_, err := h.Router.SetMissionControlConfig(ctxt, req)
h.NoError(err, "SetMissionControlConfig")
}
// ResetMissionControl makes a RPC call to the node's ResetMissionControl and
// asserts.
func (h *HarnessRPC) ResetMissionControl() {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
req := &routerrpc.ResetMissionControlRequest{}
_, err := h.Router.ResetMissionControl(ctxt, req)
h.NoError(err, "ResetMissionControl")
}

View File

@ -255,4 +255,8 @@ var allTestCasesTemp = []*lntemp.TestCase{
Name: "max htlc pathfind", Name: "max htlc pathfind",
TestFunc: testMaxHtlcPathfind, TestFunc: testMaxHtlcPathfind,
}, },
{
Name: "multi-hop htlc error propagation",
TestFunc: testHtlcErrorPropagation,
},
} }

View File

@ -1,47 +1,65 @@
package itest package itest
import ( import (
"context"
"math" "math"
"strings"
"time"
"github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/funding"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntemp"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require"
) )
func testHtlcErrorPropagation(net *lntest.NetworkHarness, t *harnessTest) { func testHtlcErrorPropagation(ht *lntemp.HarnessTest) {
ctxb := context.Background()
// In this test we wish to exercise the daemon's correct parsing, // In this test we wish to exercise the daemon's correct parsing,
// handling, and propagation of errors that occur while processing a // handling, and propagation of errors that occur while processing a
// multi-hop payment. // multi-hop payment.
const chanAmt = funding.MaxBtcFundingAmount const chanAmt = funding.MaxBtcFundingAmount
alice, bob := ht.Alice, ht.Bob
// Since we'd like to test some multi-hop failure scenarios, we'll
// introduce another node into our test network: Carol.
carol := ht.NewNode("Carol", nil)
ht.ConnectNodes(bob, carol)
// Before we start sending payments, subscribe to htlc events for each
// node.
aliceEvents := alice.RPC.SubscribeHtlcEvents()
bobEvents := bob.RPC.SubscribeHtlcEvents()
carolEvents := carol.RPC.SubscribeHtlcEvents()
// Once subscribed, the first event will be UNKNOWN.
ht.AssertHtlcEventType(aliceEvents, routerrpc.HtlcEvent_UNKNOWN)
ht.AssertHtlcEventType(bobEvents, routerrpc.HtlcEvent_UNKNOWN)
ht.AssertHtlcEventType(carolEvents, routerrpc.HtlcEvent_UNKNOWN)
// First establish a channel with a capacity of 0.5 BTC between Alice // First establish a channel with a capacity of 0.5 BTC between Alice
// and Bob. // and Bob.
chanPointAlice := openChannelAndAssert( chanPointAlice := ht.OpenChannel(
t, net, net.Alice, net.Bob, alice, bob,
lntest.OpenChannelParams{ lntemp.OpenChannelParams{Amt: chanAmt},
Amt: chanAmt,
},
) )
if err := net.Alice.WaitForNetworkChannelOpen(chanPointAlice); err != nil {
t.Fatalf("channel not seen by alice before timeout: %v", err)
}
cType, err := channelCommitType(net.Alice, chanPointAlice) // Next, we'll create a connection from Bob to Carol, and open a
if err != nil { // channel between them so we have the topology: Alice -> Bob -> Carol.
t.Fatalf("unable to get channel type: %v", err) // The channel created will be of lower capacity that the one created
} // above.
const bobChanAmt = funding.MaxBtcFundingAmount
chanPointBob := ht.OpenChannel(
bob, carol, lntemp.OpenChannelParams{Amt: chanAmt},
)
// Ensure that Alice has Carol in her routing table before proceeding.
ht.AssertTopologyChannelOpen(alice, chanPointBob)
cType := ht.GetChannelCommitType(alice, chanPointAlice)
commitFee := calcStaticFee(cType, 0) commitFee := calcStaticFee(cType, 0)
assertBaseBalance := func() { assertBaseBalance := func() {
// Alice has opened a channel with Bob with zero push amount, so // Alice has opened a channel with Bob with zero push amount,
// it's remote balance is zero. // so it's remote balance is zero.
expBalanceAlice := &lnrpc.ChannelBalanceResponse{ expBalanceAlice := &lnrpc.ChannelBalanceResponse{
LocalBalance: &lnrpc.Amount{ LocalBalance: &lnrpc.Amount{
Sat: uint64(chanAmt - commitFee), Sat: uint64(chanAmt - commitFee),
@ -57,7 +75,7 @@ func testHtlcErrorPropagation(net *lntest.NetworkHarness, t *harnessTest) {
// Deprecated fields. // Deprecated fields.
Balance: int64(chanAmt - commitFee), Balance: int64(chanAmt - commitFee),
} }
assertChannelBalanceResp(t, net.Alice, expBalanceAlice) ht.AssertChannelBalanceResp(alice, expBalanceAlice)
// Bob has a channel with Alice and another with Carol, so it's // Bob has a channel with Alice and another with Carol, so it's
// local and remote balances are both chanAmt - commitFee. // local and remote balances are both chanAmt - commitFee.
@ -81,52 +99,21 @@ func testHtlcErrorPropagation(net *lntest.NetworkHarness, t *harnessTest) {
// Deprecated fields. // Deprecated fields.
Balance: int64(chanAmt - commitFee), Balance: int64(chanAmt - commitFee),
} }
assertChannelBalanceResp(t, net.Bob, expBalanceBob) ht.AssertChannelBalanceResp(bob, expBalanceBob)
} }
// Since we'd like to test some multi-hop failure scenarios, we'll // assertLinkFailure checks that the stream provided has a single link
// introduce another node into our test network: Carol. // failure the failure detail provided.
carol := net.NewNode(t.t, "Carol", nil) assertLinkFailure := func(event *routerrpc.HtlcEvent,
failureDetail routerrpc.FailureDetail) {
// Next, we'll create a connection from Bob to Carol, and open a linkFail, ok := event.Event.(*routerrpc.HtlcEvent_LinkFailEvent)
// channel between them so we have the topology: Alice -> Bob -> Carol. require.Truef(ht, ok, "expected forwarding failure, got: %T",
// The channel created will be of lower capacity that the one created linkFail)
// above.
net.ConnectNodes(t.t, net.Bob, carol)
const bobChanAmt = funding.MaxBtcFundingAmount
chanPointBob := openChannelAndAssert(
t, net, net.Bob, carol,
lntest.OpenChannelParams{
Amt: chanAmt,
},
)
// Ensure that Alice has Carol in her routing table before proceeding. require.Equal(ht, failureDetail,
nodeInfoReq := &lnrpc.NodeInfoRequest{ linkFail.LinkFailEvent.FailureDetail,
PubKey: carol.PubKeyStr, "wrong link fail detail")
}
checkTableTimeout := time.After(time.Second * 10)
checkTableTicker := time.NewTicker(100 * time.Millisecond)
defer checkTableTicker.Stop()
out:
// TODO(roasbeef): make into async hook for node announcements
for {
select {
case <-checkTableTicker.C:
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
_, err := net.Alice.GetNodeInfo(ctxt, nodeInfoReq)
if err != nil && strings.Contains(err.Error(),
"unable to find") {
continue
}
break out
case <-checkTableTimeout:
t.Fatalf("carol's node announcement didn't propagate within " +
"the timeout period")
}
} }
// With the channels, open we can now start to test our multi-hop error // With the channels, open we can now start to test our multi-hop error
@ -137,61 +124,14 @@ out:
Memo: "kek99", Memo: "kek99",
Value: payAmt, Value: payAmt,
} }
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
carolInvoice, err := carol.AddInvoice(ctxt, invoiceReq)
if err != nil {
t.Fatalf("unable to generate carol invoice: %v", err)
}
carolPayReq, err := carol.DecodePayReq(ctxb, carolInvoice := carol.RPC.AddInvoice(invoiceReq)
&lnrpc.PayReqString{ carolPayReq := carol.RPC.DecodePayReq(carolInvoice.PaymentRequest)
PayReq: carolInvoice.PaymentRequest,
})
if err != nil {
t.Fatalf("unable to decode generated payment request: %v", err)
}
// Before we send the payment, ensure that the announcement of the new
// channel has been processed by Alice.
if err := net.Alice.WaitForNetworkChannelOpen(chanPointBob); err != nil {
t.Fatalf("channel not seen by alice before timeout: %v", err)
}
// Before we start sending payments, subscribe to htlc events for each
// node.
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
defer cancel()
aliceEvents, err := net.Alice.RouterClient.SubscribeHtlcEvents(
ctxt, &routerrpc.SubscribeHtlcEventsRequest{},
)
if err != nil {
t.Fatalf("could not subscribe events: %v", err)
}
assertSubscribed(t, aliceEvents)
bobEvents, err := net.Bob.RouterClient.SubscribeHtlcEvents(
ctxt, &routerrpc.SubscribeHtlcEventsRequest{},
)
if err != nil {
t.Fatalf("could not subscribe events: %v", err)
}
assertSubscribed(t, bobEvents)
carolEvents, err := carol.RouterClient.SubscribeHtlcEvents(
ctxt, &routerrpc.SubscribeHtlcEventsRequest{},
)
if err != nil {
t.Fatalf("could not subscribe events: %v", err)
}
assertSubscribed(t, carolEvents)
// For the first scenario, we'll test the cancellation of an HTLC with // For the first scenario, we'll test the cancellation of an HTLC with
// an unknown payment hash. // an unknown payment hash.
// TODO(roasbeef): return failure response rather than failing entire
// stream on payment error.
sendReq := &routerrpc.SendPaymentRequest{ sendReq := &routerrpc.SendPaymentRequest{
PaymentHash: makeFakePayHash(t), PaymentHash: ht.Random32Bytes(),
Dest: carol.PubKey[:], Dest: carol.PubKey[:],
Amt: payAmt, Amt: payAmt,
FinalCltvDelta: int32(carolPayReq.CltvExpiry), FinalCltvDelta: int32(carolPayReq.CltvExpiry),
@ -199,25 +139,56 @@ out:
FeeLimitMsat: noFeeLimitMsat, FeeLimitMsat: noFeeLimitMsat,
MaxParts: 1, MaxParts: 1,
} }
sendAndAssertFailure( ht.SendPaymentAssertFail(
t, net.Alice, alice, sendReq,
sendReq, lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, //nolint:lll
) )
assertLastHTLCError( ht.AssertLastHTLCError(
t, net.Alice, alice, lnrpc.Failure_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS,
lnrpc.Failure_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS,
) )
// assertAliceAndBob is a helper closure that asserts Alice and Bob
// each has one forward and one forward fail event, and Bob has the
// final htlc fail event.
assertAliceAndBob := func() {
ht.AssertHtlcEventTypes(
aliceEvents, routerrpc.HtlcEvent_SEND,
lntemp.HtlcEventForward,
)
ht.AssertHtlcEventTypes(
aliceEvents, routerrpc.HtlcEvent_SEND,
lntemp.HtlcEventForwardFail,
)
ht.AssertHtlcEventTypes(
bobEvents, routerrpc.HtlcEvent_FORWARD,
lntemp.HtlcEventForward,
)
ht.AssertHtlcEventTypes(
bobEvents, routerrpc.HtlcEvent_FORWARD,
lntemp.HtlcEventForwardFail,
)
ht.AssertHtlcEventTypes(
bobEvents, routerrpc.HtlcEvent_UNKNOWN,
lntemp.HtlcEventFinal,
)
}
// We expect alice and bob to each have one forward and one forward // We expect alice and bob to each have one forward and one forward
// fail event at this stage. // fail event at this stage.
assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_SEND, aliceEvents) assertAliceAndBob()
assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_FORWARD, bobEvents)
// Carol should have a link failure because the htlc failed on her // Carol should have a link failure because the htlc failed on her
// incoming link. // incoming link.
assertLinkFailure( event := ht.AssertHtlcEventType(
t, routerrpc.HtlcEvent_RECEIVE, carolEvents, routerrpc.HtlcEvent_RECEIVE,
routerrpc.FailureDetail_UNKNOWN_INVOICE, carolEvents, )
assertLinkFailure(event, routerrpc.FailureDetail_UNKNOWN_INVOICE)
// There's also a final htlc event that gives the final outcome of the
// htlc.
ht.AssertHtlcEventTypes(
carolEvents, routerrpc.HtlcEvent_UNKNOWN, lntemp.HtlcEventFinal,
) )
// The balances of all parties should be the same as initially since // The balances of all parties should be the same as initially since
@ -228,33 +199,36 @@ out:
// value on the extended HTLC. // value on the extended HTLC.
htlcAmt := lnwire.NewMSatFromSatoshis(1000) htlcAmt := lnwire.NewMSatFromSatoshis(1000)
sendReq = &routerrpc.SendPaymentRequest{ sendReq = &routerrpc.SendPaymentRequest{
PaymentHash: carolInvoice.RHash, PaymentHash: carolInvoice.RHash,
Dest: carol.PubKey[:], Dest: carol.PubKey[:],
Amt: int64(htlcAmt.ToSatoshis()), // 10k satoshis are expected. // 10k satoshis are expected.
Amt: int64(htlcAmt.ToSatoshis()),
FinalCltvDelta: int32(carolPayReq.CltvExpiry), FinalCltvDelta: int32(carolPayReq.CltvExpiry),
TimeoutSeconds: 60, TimeoutSeconds: 60,
FeeLimitMsat: noFeeLimitMsat, FeeLimitMsat: noFeeLimitMsat,
MaxParts: 1, MaxParts: 1,
} }
sendAndAssertFailure( ht.SendPaymentAssertFail(
t, net.Alice, alice, sendReq,
sendReq, lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, //nolint:lll
) )
assertLastHTLCError( ht.AssertLastHTLCError(
t, net.Alice, alice, lnrpc.Failure_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS,
lnrpc.Failure_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS,
) )
// We expect alice and bob to each have one forward and one forward // We expect alice and bob to each have one forward and one forward
// fail event at this stage. // fail event at this stage.
assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_SEND, aliceEvents) assertAliceAndBob()
assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_FORWARD, bobEvents)
// Carol should have a link failure because the htlc failed on her // Carol should have a link failure because the htlc failed on her
// incoming link. // incoming link.
assertLinkFailure( event = ht.AssertHtlcEventType(carolEvents, routerrpc.HtlcEvent_RECEIVE)
t, routerrpc.HtlcEvent_RECEIVE, assertLinkFailure(event, routerrpc.FailureDetail_INVOICE_UNDERPAID)
routerrpc.FailureDetail_INVOICE_UNDERPAID, carolEvents,
// There's also a final htlc event that gives the final outcome of the
// htlc.
ht.AssertHtlcEventTypes(
carolEvents, routerrpc.HtlcEvent_UNKNOWN, lntemp.HtlcEventFinal,
) )
// The balances of all parties should be the same as initially since // The balances of all parties should be the same as initially since
@ -284,29 +258,34 @@ out:
invoiceReq = &lnrpc.Invoice{ invoiceReq = &lnrpc.Invoice{
Value: toSend, Value: toSend,
} }
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) carolInvoice2 := carol.RPC.AddInvoice(invoiceReq)
carolInvoice2, err := carol.AddInvoice(ctxt, invoiceReq)
if err != nil {
t.Fatalf("unable to generate carol invoice: %v", err)
}
sendAndAssertSuccess( req := &routerrpc.SendPaymentRequest{
t, net.Bob, &routerrpc.SendPaymentRequest{ PaymentRequest: carolInvoice2.PaymentRequest,
PaymentRequest: carolInvoice2.PaymentRequest, TimeoutSeconds: 60,
TimeoutSeconds: 60, FeeLimitMsat: noFeeLimitMsat,
FeeLimitMsat: noFeeLimitMsat, MaxParts: 1,
MaxParts: 1, }
}, ht.SendPaymentAndAssertStatus(bob, req, lnrpc.Payment_SUCCEEDED)
)
// For each send bob makes, we need to check that bob has a // For each send bob makes, we need to check that bob has a
// forward and settle event for his send, and carol has a // forward and settle event for his send, and carol has a
// settle event for her receive. // settle event and a final htlc event for her receive.
assertHtlcEvents( ht.AssertHtlcEventTypes(
t, 1, 0, 1, routerrpc.HtlcEvent_SEND, bobEvents, bobEvents, routerrpc.HtlcEvent_SEND,
lntemp.HtlcEventForward,
) )
assertHtlcEvents( ht.AssertHtlcEventTypes(
t, 0, 0, 1, routerrpc.HtlcEvent_RECEIVE, carolEvents, bobEvents, routerrpc.HtlcEvent_SEND,
lntemp.HtlcEventSettle,
)
ht.AssertHtlcEventTypes(
carolEvents, routerrpc.HtlcEvent_RECEIVE,
lntemp.HtlcEventSettle,
)
ht.AssertHtlcEventTypes(
carolEvents, routerrpc.HtlcEvent_UNKNOWN,
lntemp.HtlcEventFinal,
) )
amtSent += toSend amtSent += toSend
@ -318,11 +297,7 @@ out:
invoiceReq = &lnrpc.Invoice{ invoiceReq = &lnrpc.Invoice{
Value: 100000, Value: 100000,
} }
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) carolInvoice3 := carol.RPC.AddInvoice(invoiceReq)
carolInvoice3, err := carol.AddInvoice(ctxt, invoiceReq)
if err != nil {
t.Fatalf("unable to generate carol invoice: %v", err)
}
sendReq = &routerrpc.SendPaymentRequest{ sendReq = &routerrpc.SendPaymentRequest{
PaymentRequest: carolInvoice3.PaymentRequest, PaymentRequest: carolInvoice3.PaymentRequest,
@ -330,30 +305,37 @@ out:
FeeLimitMsat: noFeeLimitMsat, FeeLimitMsat: noFeeLimitMsat,
MaxParts: 1, MaxParts: 1,
} }
sendAndAssertFailure( ht.SendPaymentAssertFail(
t, net.Alice, alice, sendReq,
sendReq, lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE, lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE,
) )
assertLastHTLCError( ht.AssertLastHTLCError(
t, net.Alice, lnrpc.Failure_TEMPORARY_CHANNEL_FAILURE, alice, lnrpc.Failure_TEMPORARY_CHANNEL_FAILURE,
) )
// Alice should have a forwarding event and a forwarding failure. // Alice should have a forwarding event and a forwarding failure.
assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_SEND, aliceEvents) ht.AssertHtlcEventTypes(
aliceEvents, routerrpc.HtlcEvent_SEND,
lntemp.HtlcEventForward,
)
ht.AssertHtlcEventTypes(
aliceEvents, routerrpc.HtlcEvent_SEND,
lntemp.HtlcEventForwardFail,
)
// Bob should have a link failure because the htlc failed on his // Bob should have a link failure because the htlc failed on his
// outgoing link. // outgoing link.
assertLinkFailure( event = ht.AssertHtlcEventType(bobEvents, routerrpc.HtlcEvent_FORWARD)
t, routerrpc.HtlcEvent_FORWARD, assertLinkFailure(event, routerrpc.FailureDetail_INSUFFICIENT_BALANCE)
routerrpc.FailureDetail_INSUFFICIENT_BALANCE, bobEvents,
// There's also a final htlc event that gives the final outcome of the
// htlc.
ht.AssertHtlcEventTypes(
bobEvents, routerrpc.HtlcEvent_UNKNOWN, lntemp.HtlcEventFinal,
) )
// Generate new invoice to not pay same invoice twice. // Generate new invoice to not pay same invoice twice.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) carolInvoice = carol.RPC.AddInvoice(invoiceReq)
carolInvoice, err = carol.AddInvoice(ctxt, invoiceReq)
if err != nil {
t.Fatalf("unable to generate carol invoice: %v", err)
}
// For our final test, we'll ensure that if a target link isn't // For our final test, we'll ensure that if a target link isn't
// available for what ever reason then the payment fails accordingly. // available for what ever reason then the payment fails accordingly.
@ -361,77 +343,48 @@ out:
// We'll attempt to complete the original invoice we created with Carol // We'll attempt to complete the original invoice we created with Carol
// above, but before we do so, Carol will go offline, resulting in a // above, but before we do so, Carol will go offline, resulting in a
// failed payment. // failed payment.
shutdownAndAssert(net, t, carol) ht.Shutdown(carol)
// Reset mission control to forget the temporary channel failure above. // Reset mission control to forget the temporary channel failure above.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) alice.RPC.ResetMissionControl()
_, err = net.Alice.RouterClient.ResetMissionControl(
ctxt, &routerrpc.ResetMissionControlRequest{},
)
if err != nil {
t.Fatalf("unable to reset mission control: %v", err)
}
sendAndAssertFailure( req := &routerrpc.SendPaymentRequest{
t, net.Alice, PaymentRequest: carolInvoice.PaymentRequest,
&routerrpc.SendPaymentRequest{ TimeoutSeconds: 60,
PaymentRequest: carolInvoice.PaymentRequest, FeeLimitMsat: noFeeLimitMsat,
TimeoutSeconds: 60, MaxParts: 1,
FeeLimitMsat: noFeeLimitMsat, }
MaxParts: 1, ht.SendPaymentAssertFail(
}, alice, req, lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE,
lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE,
) )
assertLastHTLCError(t, net.Alice, lnrpc.Failure_UNKNOWN_NEXT_PEER) ht.AssertLastHTLCError(alice, lnrpc.Failure_UNKNOWN_NEXT_PEER)
// Alice should have a forwarding event and subsequent fail. // Alice should have a forwarding event and subsequent fail.
assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_SEND, aliceEvents) ht.AssertHtlcEventTypes(
aliceEvents, routerrpc.HtlcEvent_SEND,
lntemp.HtlcEventForward,
)
ht.AssertHtlcEventTypes(
aliceEvents, routerrpc.HtlcEvent_SEND,
lntemp.HtlcEventForwardFail,
)
// Bob should have a link failure because he could not find the next // Bob should have a link failure because he could not find the next
// peer. // peer.
assertLinkFailure( event = ht.AssertHtlcEventType(bobEvents, routerrpc.HtlcEvent_FORWARD)
t, routerrpc.HtlcEvent_FORWARD, assertLinkFailure(event, routerrpc.FailureDetail_NO_DETAIL)
routerrpc.FailureDetail_NO_DETAIL, bobEvents,
// There's also a final htlc event that gives the final outcome of the
// htlc.
ht.AssertHtlcEventTypes(
bobEvents, routerrpc.HtlcEvent_UNKNOWN, lntemp.HtlcEventFinal,
) )
// Finally, immediately close the channel. This function will also // Finally, immediately close the channel. This function will also
// block until the channel is closed and will additionally assert the // block until the channel is closed and will additionally assert the
// relevant channel closing post conditions. // relevant channel closing post conditions.
closeChannelAndAssert(t, net, net.Alice, chanPointAlice, false) ht.CloseChannel(alice, chanPointAlice)
// Force close Bob's final channel. // Force close Bob's final channel.
closeChannelAndAssert(t, net, net.Bob, chanPointBob, true) ht.ForceCloseChannel(bob, chanPointBob)
// Cleanup by mining the force close and sweep transaction.
cleanupForceClose(t, net, net.Bob, chanPointBob)
}
// assertLinkFailure checks that the stream provided has a single link failure
// the the failure detail provided.
func assertLinkFailure(t *harnessTest,
eventType routerrpc.HtlcEvent_EventType,
failureDetail routerrpc.FailureDetail,
client routerrpc.Router_SubscribeHtlcEventsClient) {
event := assertEventAndType(t, eventType, client)
linkFail, ok := event.Event.(*routerrpc.HtlcEvent_LinkFailEvent)
if !ok {
t.Fatalf("expected forwarding failure, got: %T", linkFail)
}
if linkFail.LinkFailEvent.FailureDetail != failureDetail {
t.Fatalf("expected: %v, got: %v", failureDetail,
linkFail.LinkFailEvent.FailureDetail)
}
event = assertEventAndType(t, routerrpc.HtlcEvent_UNKNOWN, client)
finalHtlc, ok := event.Event.(*routerrpc.HtlcEvent_FinalHtlcEvent)
if !ok {
t.Fatalf("expected final htlc, got: %T", event.Event)
}
if finalHtlc.FinalHtlcEvent.Settled {
t.Fatalf("expected final fail")
}
} }

View File

@ -44,10 +44,6 @@ var allTestCases = []*testCase{
name: "multiple channel creation and update subscription", name: "multiple channel creation and update subscription",
test: testBasicChannelCreationAndUpdates, test: testBasicChannelCreationAndUpdates,
}, },
{
name: "multi-hop htlc error propagation",
test: testHtlcErrorPropagation,
},
{ {
name: "derive shared key", name: "derive shared key",
test: testDeriveSharedKey, test: testDeriveSharedKey,