htlcswitch+routing: type check on ClearTextError

Update the type check used for checking local payment
failures to check on the ClearTextError interface rather
than on the ForwardingError type. This change prepares
for splitting payment errors up into Link and Forwarding
errors.
This commit is contained in:
carla 2020-01-14 15:07:42 +02:00
parent 6a83b06ab7
commit b5a2d75465
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91
5 changed files with 94 additions and 62 deletions

@ -574,12 +574,12 @@ func TestExitNodeTimelockPayloadMismatch(t *testing.T) {
t.Fatalf("payment should have failed but didn't") t.Fatalf("payment should have failed but didn't")
} }
ferr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected a ForwardingError, instead got: %T", err) t.Fatalf("expected a ClearTextError, instead got: %T", err)
} }
switch ferr.WireMessage().(type) { switch rtErr.WireMessage().(type) {
case *lnwire.FailFinalIncorrectCltvExpiry: case *lnwire.FailFinalIncorrectCltvExpiry:
default: default:
t.Fatalf("incorrect error, expected incorrect cltv expiry, "+ t.Fatalf("incorrect error, expected incorrect cltv expiry, "+
@ -674,12 +674,12 @@ func TestLinkForwardTimelockPolicyMismatch(t *testing.T) {
t.Fatalf("payment should have failed but didn't") t.Fatalf("payment should have failed but didn't")
} }
ferr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected a ForwardingError, instead got: %T", err) t.Fatalf("expected a ClearTextError, instead got: %T", err)
} }
switch ferr.WireMessage().(type) { switch rtErr.WireMessage().(type) {
case *lnwire.FailIncorrectCltvExpiry: case *lnwire.FailIncorrectCltvExpiry:
default: default:
t.Fatalf("incorrect error, expected incorrect cltv expiry, "+ t.Fatalf("incorrect error, expected incorrect cltv expiry, "+
@ -732,12 +732,12 @@ func TestLinkForwardFeePolicyMismatch(t *testing.T) {
t.Fatalf("payment should have failed but didn't") t.Fatalf("payment should have failed but didn't")
} }
ferr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected a ForwardingError, instead got: %T", err) t.Fatalf("expected a ClearTextError, instead got: %T", err)
} }
switch ferr.WireMessage().(type) { switch rtErr.WireMessage().(type) {
case *lnwire.FailFeeInsufficient: case *lnwire.FailFeeInsufficient:
default: default:
t.Fatalf("incorrect error, expected fee insufficient, "+ t.Fatalf("incorrect error, expected fee insufficient, "+
@ -790,12 +790,12 @@ func TestLinkForwardMinHTLCPolicyMismatch(t *testing.T) {
t.Fatalf("payment should have failed but didn't") t.Fatalf("payment should have failed but didn't")
} }
ferr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected a ForwardingError, instead got: %T", err) t.Fatalf("expected a ClearTextError, instead got: %T", err)
} }
switch ferr.WireMessage().(type) { switch rtErr.WireMessage().(type) {
case *lnwire.FailAmountBelowMinimum: case *lnwire.FailAmountBelowMinimum:
default: default:
t.Fatalf("incorrect error, expected amount below minimum, "+ t.Fatalf("incorrect error, expected amount below minimum, "+
@ -857,12 +857,12 @@ func TestLinkForwardMaxHTLCPolicyMismatch(t *testing.T) {
t.Fatalf("payment should have failed but didn't") t.Fatalf("payment should have failed but didn't")
} }
ferr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected a ForwardingError, instead got: %T", err) t.Fatalf("expected a ClearTextError, instead got: %T", err)
} }
switch ferr.WireMessage().(type) { switch rtErr.WireMessage().(type) {
case *lnwire.FailTemporaryChannelFailure: case *lnwire.FailTemporaryChannelFailure:
default: default:
t.Fatalf("incorrect error, expected temporary channel failure, "+ t.Fatalf("incorrect error, expected temporary channel failure, "+
@ -964,11 +964,12 @@ func TestUpdateForwardingPolicy(t *testing.T) {
t.Fatalf("payment should've been rejected") t.Fatalf("payment should've been rejected")
} }
ferr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected a ForwardingError, instead got (%T): %v", err, err) t.Fatalf("expected a ClearTextError, instead got (%T): %v", err, err)
} }
switch ferr.WireMessage().(type) {
switch rtErr.WireMessage().(type) {
case *lnwire.FailFeeInsufficient: case *lnwire.FailFeeInsufficient:
default: default:
t.Fatalf("expected FailFeeInsufficient instead got: %v", err) t.Fatalf("expected FailFeeInsufficient instead got: %v", err)
@ -1003,12 +1004,13 @@ func TestUpdateForwardingPolicy(t *testing.T) {
t.Fatalf("payment should've been rejected") t.Fatalf("payment should've been rejected")
} }
ferr, ok = err.(*ForwardingError) rtErr, ok = err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected a ForwardingError, instead got (%T): %v", t.Fatalf("expected a ClearTextError, instead got (%T): %v",
err, err) err, err)
} }
switch ferr.WireMessage().(type) {
switch rtErr.WireMessage().(type) {
case *lnwire.FailTemporaryChannelFailure: case *lnwire.FailTemporaryChannelFailure:
default: default:
t.Fatalf("expected TemporaryChannelFailure, instead got: %v", t.Fatalf("expected TemporaryChannelFailure, instead got: %v",
@ -1249,13 +1251,14 @@ func TestChannelLinkMultiHopUnknownNextHop(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("error haven't been received") t.Fatal("error haven't been received")
} }
fErr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected ForwardingError") t.Fatalf("expected ClearTextError")
} }
if _, ok = fErr.WireMessage().(*lnwire.FailUnknownNextPeer); !ok {
if _, ok = rtErr.WireMessage().(*lnwire.FailUnknownNextPeer); !ok {
t.Fatalf("wrong error has been received: %T", t.Fatalf("wrong error has been received: %T",
fErr.WireMessage()) rtErr.WireMessage())
} }
// Wait for Alice to receive the revocation. // Wait for Alice to receive the revocation.
@ -1364,12 +1367,12 @@ func TestChannelLinkMultiHopDecodeError(t *testing.T) {
t.Fatal("error haven't been received") t.Fatal("error haven't been received")
} }
ferr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected a ForwardingError, instead got: %T", err) t.Fatalf("expected a ClearTextError, instead got: %T", err)
} }
switch ferr.WireMessage().(type) { switch rtErr.WireMessage().(type) {
case *lnwire.FailInvalidOnionVersion: case *lnwire.FailInvalidOnionVersion:
default: default:
t.Fatalf("wrong error have been received: %v", err) t.Fatalf("wrong error have been received: %v", err)
@ -1456,13 +1459,13 @@ func TestChannelLinkExpiryTooSoonExitNode(t *testing.T) {
"time lock value") "time lock value")
} }
ferr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected a ForwardingError, instead got: %T %v", t.Fatalf("expected a ClearTextError, instead got: %T %v",
err, err) rtErr, err)
} }
switch ferr.WireMessage().(type) { switch rtErr.WireMessage().(type) {
case *lnwire.FailIncorrectDetails: case *lnwire.FailIncorrectDetails:
default: default:
t.Fatalf("expected incorrect_or_unknown_payment_details, "+ t.Fatalf("expected incorrect_or_unknown_payment_details, "+
@ -1519,12 +1522,13 @@ func TestChannelLinkExpiryTooSoonMidNode(t *testing.T) {
"time lock value") "time lock value")
} }
ferr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected a ForwardingError, instead got: %T: %v", err, err) t.Fatalf("expected a ClearTextError, instead got: %T: %v",
rtErr, err)
} }
switch ferr.WireMessage().(type) { switch rtErr.WireMessage().(type) {
case *lnwire.FailExpiryTooSoon: case *lnwire.FailExpiryTooSoon:
default: default:
t.Fatalf("incorrect error, expected final time lock too "+ t.Fatalf("incorrect error, expected final time lock too "+
@ -5632,11 +5636,11 @@ func TestChannelLinkCanceledInvoice(t *testing.T) {
// Because the invoice is canceled, we expect an unknown payment hash // Because the invoice is canceled, we expect an unknown payment hash
// result. // result.
fErr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected ForwardingError, but got %v", err) t.Fatalf("expected ClearTextError, but got %v", err)
} }
_, ok = fErr.WireMessage().(*lnwire.FailIncorrectDetails) _, ok = rtErr.WireMessage().(*lnwire.FailIncorrectDetails)
if !ok { if !ok {
t.Fatalf("expected unknown payment hash, but got %v", err) t.Fatalf("expected unknown payment hash, but got %v", err)
} }
@ -6213,16 +6217,16 @@ func TestChannelLinkReceiveEmptySig(t *testing.T) {
aliceLink.Stop() aliceLink.Stop()
} }
// assertFailureCode asserts that an error is of type ForwardingError and that // assertFailureCode asserts that an error is of type ClearTextError and that
// the failure code is as expected. // the failure code is as expected.
func assertFailureCode(t *testing.T, err error, code lnwire.FailCode) { func assertFailureCode(t *testing.T, err error, code lnwire.FailCode) {
fErr, ok := err.(*ForwardingError) rtErr, ok := err.(ClearTextError)
if !ok { if !ok {
t.Fatalf("expected ForwardingError but got %T", err) t.Fatalf("expected ClearTextError but got %T", err)
} }
if fErr.WireMessage().Code() != code { if rtErr.WireMessage().Code() != code {
t.Fatalf("expected %v but got %v", t.Fatalf("expected %v but got %v",
code, fErr.WireMessage().Code()) code, rtErr.WireMessage().Code())
} }
} }

@ -2178,11 +2178,11 @@ func TestUpdateFailMalformedHTLCErrorConversion(t *testing.T) {
t.Fatalf("unable to send payment: %v", err) t.Fatalf("unable to send payment: %v", err)
} }
fwdingErr := err.(*ForwardingError) routingErr := err.(ClearTextError)
failureMsg := fwdingErr.WireMessage() failureMsg := routingErr.WireMessage()
if _, ok := failureMsg.(*lnwire.FailInvalidOnionKey); !ok { if _, ok := failureMsg.(*lnwire.FailInvalidOnionKey); !ok {
t.Fatalf("expected onion failure instead got: %v", t.Fatalf("expected onion failure instead got: %v",
fwdingErr.WireMessage()) routingErr.WireMessage())
} }
} }
@ -2441,14 +2441,18 @@ func TestInvalidFailure(t *testing.T) {
select { select {
case result := <-resultChan: case result := <-resultChan:
fErr, ok := result.Error.(*ForwardingError) rtErr, ok := result.Error.(ClearTextError)
if !ok { if !ok {
t.Fatal("expected ForwardingError") t.Fatal("expected ClearTextError")
} }
if fErr.FailureSourceIdx != 2 { source, ok := rtErr.(*ForwardingError)
if !ok {
t.Fatalf("expected forwarding error, got: %T", rtErr)
}
if source.FailureSourceIdx != 2 {
t.Fatal("unexpected error source index") t.Fatal("unexpected error source index")
} }
if fErr.WireMessage() != nil { if rtErr.WireMessage() != nil {
t.Fatal("expected empty failure message") t.Fatal("expected empty failure message")
} }

@ -326,12 +326,12 @@ func marshallError(sendError error) (*Failure, error) {
return response, nil return response, nil
} }
fErr, ok := sendError.(*htlcswitch.ForwardingError) rtErr, ok := sendError.(htlcswitch.ClearTextError)
if !ok { if !ok {
return nil, sendError return nil, sendError
} }
switch onionErr := fErr.WireMessage().(type) { switch onionErr := rtErr.WireMessage().(type) {
case *lnwire.FailIncorrectDetails: case *lnwire.FailIncorrectDetails:
response.Code = Failure_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS response.Code = Failure_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS
@ -425,7 +425,16 @@ func marshallError(sendError error) (*Failure, error) {
return nil, fmt.Errorf("cannot marshall failure %T", onionErr) return nil, fmt.Errorf("cannot marshall failure %T", onionErr)
} }
// If the ClearTextError received is a ForwardingError, the error
// originated from a node along the route, not locally on our outgoing
// link. We set failureSourceIdx to the index of the node where the
// failure occurred. If the error is not a ForwardingError, the failure
// occurred at our node, so we leave the index as 0 to indicate that
// we failed locally.
fErr, ok := rtErr.(*htlcswitch.ForwardingError)
if ok {
response.FailureSourceIndex = uint32(fErr.FailureSourceIdx) response.FailureSourceIndex = uint32(fErr.FailureSourceIdx)
}
return response, nil return response, nil
} }

@ -35,12 +35,12 @@ func (m *mockPaymentAttemptDispatcher) SendHTLC(firstHop lnwire.ShortChannelID,
var result *htlcswitch.PaymentResult var result *htlcswitch.PaymentResult
preimage, err := m.onPayment(firstHop) preimage, err := m.onPayment(firstHop)
if err != nil { if err != nil {
fwdErr, ok := err.(*htlcswitch.ForwardingError) rtErr, ok := err.(htlcswitch.ClearTextError)
if !ok { if !ok {
return err return err
} }
result = &htlcswitch.PaymentResult{ result = &htlcswitch.PaymentResult{
Error: fwdErr, Error: rtErr,
} }
} else { } else {
result = &htlcswitch.PaymentResult{Preimage: preimage} result = &htlcswitch.PaymentResult{Preimage: preimage}

@ -1910,18 +1910,33 @@ func (r *ChannelRouter) processSendError(paymentID uint64, rt *route.Route,
return reportFail(nil, nil) return reportFail(nil, nil)
} }
// If an internal, non-forwarding error occurred, we can stop
// trying. // If the error is a ClearTextError, we have received a valid wire
fErr, ok := sendErr.(*htlcswitch.ForwardingError) // failure message, either from our own outgoing link or from a node
// down the route. If the error is not related to the propagation of
// our payment, we can stop trying because an internal error has
// occurred.
rtErr, ok := sendErr.(htlcswitch.ClearTextError)
if !ok { if !ok {
return &internalErrorReason return &internalErrorReason
} }
failureMessage := fErr.WireMessage() // failureSourceIdx is the index of the node that the failure occurred
failureSourceIdx := fErr.FailureSourceIdx // at. If the ClearTextError received is not a ForwardingError the
// payment error occurred at our node, so we leave this value as 0
// to indicate that the failure occurred locally. If the error is a
// ForwardingError, it did not originate at our node, so we set
// failureSourceIdx to the index of the node where the failure occurred.
failureSourceIdx := 0
source, ok := rtErr.(*htlcswitch.ForwardingError)
if ok {
failureSourceIdx = source.FailureSourceIdx
}
// Apply channel update if the error contains one. For unknown // Extract the wire failure and apply channel update if it contains one.
// failures, failureMessage is nil. // If we received an unknown failure message from a node along the
// route, the failure message will be nil.
failureMessage := rtErr.WireMessage()
if failureMessage != nil { if failureMessage != nil {
err := r.tryApplyChannelUpdate( err := r.tryApplyChannelUpdate(
rt, failureSourceIdx, failureMessage, rt, failureSourceIdx, failureMessage,