htlcswitch: add failure details to incoming failures

This commit adds LinkErrors with failure details to htlcs which fail on
our incoming link. This change is made with the intention of notifying
detailed htlc failure reasons in sendHTLCError. The FailureDetail
interface is implemented on FailureResolutionResults so that they can
directly be used to enrich LinkErrors. sendHtlcError is updated to
take a LinkError in preparation for the addition of a htlcnotifier
which will notify the detail of the error.
This commit is contained in:
carla 2020-02-06 19:35:17 +02:00
parent 74e0d545fe
commit 1ad395ec3f
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91
2 changed files with 27 additions and 13 deletions

@ -1220,8 +1220,8 @@ func (l *channelLink) processHtlcResolution(resolution invoices.HtlcResolution,
failure := getResolutionFailure(res, htlc.pd.Amount) failure := getResolutionFailure(res, htlc.pd.Amount)
l.sendHTLCError( l.sendHTLCError(
htlc.pd.HtlcIndex, failure, htlc.obfuscator, htlc.pd.HtlcIndex, failure,
htlc.pd.SourceRef, htlc.obfuscator, htlc.pd.SourceRef,
) )
return nil return nil
@ -1236,21 +1236,25 @@ func (l *channelLink) processHtlcResolution(resolution invoices.HtlcResolution,
// getResolutionFailure returns the wire message that a htlc resolution should // getResolutionFailure returns the wire message that a htlc resolution should
// be failed with. // be failed with.
func getResolutionFailure(resolution *invoices.HtlcFailResolution, func getResolutionFailure(resolution *invoices.HtlcFailResolution,
amount lnwire.MilliSatoshi) lnwire.FailureMessage { amount lnwire.MilliSatoshi) *LinkError {
// If the resolution has been resolved as part of a MPP timeout, // If the resolution has been resolved as part of a MPP timeout,
// we need to fail the htlc with lnwire.FailMppTimeout. // we need to fail the htlc with lnwire.FailMppTimeout.
if resolution.Outcome == invoices.ResultMppTimeout { if resolution.Outcome == invoices.ResultMppTimeout {
return &lnwire.FailMPPTimeout{} return NewDetailedLinkError(
&lnwire.FailMPPTimeout{}, resolution.Outcome,
)
} }
// If the htlc is not a MPP timeout, we fail it with // If the htlc is not a MPP timeout, we fail it with
// FailIncorrectDetails. This error is sent for invoice payment // FailIncorrectDetails. This error is sent for invoice payment
// failures such as underpayment/ expiry too soon and hodl invoices // failures such as underpayment/ expiry too soon and hodl invoices
// (which return FailIncorrectDetails to avoid leaking information). // (which return FailIncorrectDetails to avoid leaking information).
return lnwire.NewFailIncorrectDetails( incorrectDetails := lnwire.NewFailIncorrectDetails(
amount, uint32(resolution.AcceptHeight), amount, uint32(resolution.AcceptHeight),
) )
return NewDetailedLinkError(incorrectDetails, resolution.Outcome)
} }
// randomFeeUpdateTimeout returns a random timeout between the bounds defined // randomFeeUpdateTimeout returns a random timeout between the bounds defined
@ -2650,9 +2654,10 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
// for TLV payloads that also supports injecting invalid // for TLV payloads that also supports injecting invalid
// payloads. Deferring this non-trival effort till a // payloads. Deferring this non-trival effort till a
// later date // later date
failure := lnwire.NewInvalidOnionPayload(failedType, 0)
l.sendHTLCError( l.sendHTLCError(
pd.HtlcIndex, pd.HtlcIndex,
lnwire.NewInvalidOnionPayload(failedType, 0), NewLinkError(failure),
obfuscator, pd.SourceRef, obfuscator, pd.SourceRef,
) )
@ -2766,7 +2771,10 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
) )
l.sendHTLCError( l.sendHTLCError(
pd.HtlcIndex, failure, obfuscator, pd.SourceRef, pd.HtlcIndex,
NewLinkError(failure),
obfuscator,
pd.SourceRef,
) )
continue continue
} }
@ -2849,7 +2857,9 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor,
"value: expected %v, got %v", pd.RHash, "value: expected %v, got %v", pd.RHash,
pd.Amount, fwdInfo.AmountToForward) pd.Amount, fwdInfo.AmountToForward)
failure := lnwire.NewFinalIncorrectHtlcAmount(pd.Amount) failure := NewLinkError(
lnwire.NewFinalIncorrectHtlcAmount(pd.Amount),
)
l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef) l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef)
return nil return nil
@ -2862,7 +2872,9 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor,
"time-lock: expected %v, got %v", "time-lock: expected %v, got %v",
pd.RHash[:], pd.Timeout, fwdInfo.OutgoingCTLV) pd.RHash[:], pd.Timeout, fwdInfo.OutgoingCTLV)
failure := lnwire.NewFinalIncorrectCltvExpiry(pd.Timeout) failure := NewLinkError(
lnwire.NewFinalIncorrectCltvExpiry(pd.Timeout),
)
l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef) l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef)
return nil return nil
@ -2979,10 +2991,10 @@ func (l *channelLink) handleBatchFwdErrs(errChan chan error) {
// sendHTLCError functions cancels HTLC and send cancel message back to the // sendHTLCError functions cancels HTLC and send cancel message back to the
// peer from which HTLC was received. // peer from which HTLC was received.
func (l *channelLink) sendHTLCError(htlcIndex uint64, failure lnwire.FailureMessage, func (l *channelLink) sendHTLCError(htlcIndex uint64, failure *LinkError,
e hop.ErrorEncrypter, sourceRef *channeldb.AddRef) { e hop.ErrorEncrypter, sourceRef *channeldb.AddRef) {
reason, err := e.EncryptFirstHop(failure) reason, err := e.EncryptFirstHop(failure.WireMessage())
if err != nil { if err != nil {
l.log.Errorf("unable to obfuscate error: %v", err) l.log.Errorf("unable to obfuscate error: %v", err)
return return

@ -107,8 +107,10 @@ const (
ResultMppInProgress ResultMppInProgress
) )
// String returns a string representation of the result. // FailureString returns a string representation of the result.
func (f FailResolutionResult) String() string { //
// Note: it is part of the FailureDetail interface.
func (f FailResolutionResult) FailureString() string {
switch f { switch f {
case resultInvalidFailure: case resultInvalidFailure:
return "invalid failure result" return "invalid failure result"