htlcswitch: use LinkError for internal errors
Update the ChannelLink interface to specifically return the LinkError struct. This error implements the ClearTextError interface, so will be picked up as a routing realted error by the router. With LinkErrors implemented, the switch now returns a LinkError for all failures on our incoming/outgoing link and ForwardingError when the failure occurs down the line.
This commit is contained in:
parent
b5a2d75465
commit
f430fd50c5
@ -33,6 +33,9 @@ type LinkError struct {
|
|||||||
// know the failure type for failures which occur at our own
|
// know the failure type for failures which occur at our own
|
||||||
// node.
|
// node.
|
||||||
msg lnwire.FailureMessage
|
msg lnwire.FailureMessage
|
||||||
|
|
||||||
|
// FailureDetail enriches the wire error with additional information.
|
||||||
|
FailureDetail
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLinkError returns a LinkError with the failure message provided.
|
// NewLinkError returns a LinkError with the failure message provided.
|
||||||
@ -42,6 +45,17 @@ func NewLinkError(msg lnwire.FailureMessage) *LinkError {
|
|||||||
return &LinkError{msg: msg}
|
return &LinkError{msg: msg}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDetailedLinkError returns a link error that enriches a wire message with
|
||||||
|
// a failure detail.
|
||||||
|
func NewDetailedLinkError(msg lnwire.FailureMessage,
|
||||||
|
detail FailureDetail) *LinkError {
|
||||||
|
|
||||||
|
return &LinkError{
|
||||||
|
msg: msg,
|
||||||
|
FailureDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WireMessage extracts a valid wire failure message from an internal
|
// WireMessage extracts a valid wire failure message from an internal
|
||||||
// error which may contain additional metadata (which should not be
|
// error which may contain additional metadata (which should not be
|
||||||
// exposed to the network). This value should never be nil for LinkErrors,
|
// exposed to the network). This value should never be nil for LinkErrors,
|
||||||
@ -56,7 +70,13 @@ func (l *LinkError) WireMessage() lnwire.FailureMessage {
|
|||||||
//
|
//
|
||||||
// Note this is part of the ClearTextError interface.
|
// Note this is part of the ClearTextError interface.
|
||||||
func (l *LinkError) Error() string {
|
func (l *LinkError) Error() string {
|
||||||
return l.msg.Error()
|
// If the link error has no failure detail, return the wire message's
|
||||||
|
// error.
|
||||||
|
if l.FailureDetail == FailureDetailNone {
|
||||||
|
return l.msg.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%v: %v", l.msg.Error(), l.FailureDetail)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForwardingError wraps an lnwire.FailureMessage in a struct that also
|
// ForwardingError wraps an lnwire.FailureMessage in a struct that also
|
||||||
|
58
htlcswitch/failure_detail.go
Normal file
58
htlcswitch/failure_detail.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package htlcswitch
|
||||||
|
|
||||||
|
// FailureDetail is an enum which is used to enrich failures with
|
||||||
|
// additional information.
|
||||||
|
type FailureDetail int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FailureDetailNone is returned when the wire message contains
|
||||||
|
// sufficient information.
|
||||||
|
FailureDetailNone = iota
|
||||||
|
|
||||||
|
// FailureDetailOnionDecode indicates that we could not decode an
|
||||||
|
// onion error.
|
||||||
|
FailureDetailOnionDecode
|
||||||
|
|
||||||
|
// FailureDetailLinkNotEligible indicates that a routing attempt was
|
||||||
|
// made over a link that is not eligible for routing.
|
||||||
|
FailureDetailLinkNotEligible
|
||||||
|
|
||||||
|
// FailureDetailOnChainTimeout indicates that a payment had to be timed
|
||||||
|
// out on chain before it got past the first hop by us or the remote
|
||||||
|
// party.
|
||||||
|
FailureDetailOnChainTimeout
|
||||||
|
|
||||||
|
// FailureDetailHTLCExceedsMax is returned when a htlc exceeds our
|
||||||
|
// policy's maximum htlc amount.
|
||||||
|
FailureDetailHTLCExceedsMax
|
||||||
|
|
||||||
|
// FailureDetailInsufficientBalance is returned when we cannot route a
|
||||||
|
// htlc due to insufficient outgoing capacity.
|
||||||
|
FailureDetailInsufficientBalance
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the string representation of a failure detail.
|
||||||
|
func (fd FailureDetail) String() string {
|
||||||
|
switch fd {
|
||||||
|
case FailureDetailNone:
|
||||||
|
return "no failure detail"
|
||||||
|
|
||||||
|
case FailureDetailOnionDecode:
|
||||||
|
return "could not decode onion"
|
||||||
|
|
||||||
|
case FailureDetailLinkNotEligible:
|
||||||
|
return "link not eligible"
|
||||||
|
|
||||||
|
case FailureDetailOnChainTimeout:
|
||||||
|
return "payment was resolved on-chain, then canceled back"
|
||||||
|
|
||||||
|
case FailureDetailHTLCExceedsMax:
|
||||||
|
return "htlc exceeds maximum policy amount"
|
||||||
|
|
||||||
|
case FailureDetailInsufficientBalance:
|
||||||
|
return "insufficient bandwidth to route htlc"
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "unknown failure detail"
|
||||||
|
}
|
||||||
|
}
|
@ -102,20 +102,21 @@ type ChannelLink interface {
|
|||||||
|
|
||||||
// CheckHtlcForward should return a nil error if the passed HTLC details
|
// CheckHtlcForward should return a nil error if the passed HTLC details
|
||||||
// satisfy the current forwarding policy fo the target link. Otherwise,
|
// satisfy the current forwarding policy fo the target link. Otherwise,
|
||||||
// a valid protocol failure message should be returned in order to
|
// a LinkError with a valid protocol failure message should be returned
|
||||||
// signal to the source of the HTLC, the policy consistency issue.
|
// in order to signal to the source of the HTLC, the policy consistency
|
||||||
|
// issue.
|
||||||
CheckHtlcForward(payHash [32]byte, incomingAmt lnwire.MilliSatoshi,
|
CheckHtlcForward(payHash [32]byte, incomingAmt lnwire.MilliSatoshi,
|
||||||
amtToForward lnwire.MilliSatoshi,
|
amtToForward lnwire.MilliSatoshi,
|
||||||
incomingTimeout, outgoingTimeout uint32,
|
incomingTimeout, outgoingTimeout uint32,
|
||||||
heightNow uint32) lnwire.FailureMessage
|
heightNow uint32) *LinkError
|
||||||
|
|
||||||
// CheckHtlcTransit should return a nil error if the passed HTLC details
|
// CheckHtlcTransit should return a nil error if the passed HTLC details
|
||||||
// satisfy the current channel policy. Otherwise, a valid protocol
|
// satisfy the current channel policy. Otherwise, a LinkError with a
|
||||||
// failure message should be returned in order to signal the violation.
|
// valid protocol failure message should be returned in order to signal
|
||||||
// This call is intended to be used for locally initiated payments for
|
// the violation. This call is intended to be used for locally initiated
|
||||||
// which there is no corresponding incoming htlc.
|
// payments for which there is no corresponding incoming htlc.
|
||||||
CheckHtlcTransit(payHash [32]byte, amt lnwire.MilliSatoshi,
|
CheckHtlcTransit(payHash [32]byte, amt lnwire.MilliSatoshi,
|
||||||
timeout uint32, heightNow uint32) lnwire.FailureMessage
|
timeout uint32, heightNow uint32) *LinkError
|
||||||
|
|
||||||
// Bandwidth returns the amount of milli-satoshis which current link
|
// Bandwidth returns the amount of milli-satoshis which current link
|
||||||
// might pass through channel link. The value returned from this method
|
// might pass through channel link. The value returned from this method
|
||||||
|
@ -2135,16 +2135,17 @@ func (l *channelLink) UpdateForwardingPolicy(newPolicy ForwardingPolicy) {
|
|||||||
l.cfg.FwrdingPolicy = newPolicy
|
l.cfg.FwrdingPolicy = newPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckHtlcForward should return a nil error if the passed HTLC details satisfy
|
// CheckHtlcForward should return a nil error if the passed HTLC details
|
||||||
// the current forwarding policy fo the target link. Otherwise, a valid
|
// satisfy the current forwarding policy fo the target link. Otherwise,
|
||||||
// protocol failure message should be returned in order to signal to the source
|
// a LinkError with a valid protocol failure message should be returned
|
||||||
// of the HTLC, the policy consistency issue.
|
// in order to signal to the source of the HTLC, the policy consistency
|
||||||
|
// issue.
|
||||||
//
|
//
|
||||||
// NOTE: Part of the ChannelLink interface.
|
// NOTE: Part of the ChannelLink interface.
|
||||||
func (l *channelLink) CheckHtlcForward(payHash [32]byte,
|
func (l *channelLink) CheckHtlcForward(payHash [32]byte,
|
||||||
incomingHtlcAmt, amtToForward lnwire.MilliSatoshi,
|
incomingHtlcAmt, amtToForward lnwire.MilliSatoshi,
|
||||||
incomingTimeout, outgoingTimeout uint32,
|
incomingTimeout, outgoingTimeout uint32,
|
||||||
heightNow uint32) lnwire.FailureMessage {
|
heightNow uint32) *LinkError {
|
||||||
|
|
||||||
l.RLock()
|
l.RLock()
|
||||||
policy := l.cfg.FwrdingPolicy
|
policy := l.cfg.FwrdingPolicy
|
||||||
@ -2176,14 +2177,14 @@ func (l *channelLink) CheckHtlcForward(payHash [32]byte,
|
|||||||
|
|
||||||
// As part of the returned error, we'll send our latest routing
|
// As part of the returned error, we'll send our latest routing
|
||||||
// policy so the sending node obtains the most up to date data.
|
// policy so the sending node obtains the most up to date data.
|
||||||
|
failure := l.createFailureWithUpdate(
|
||||||
return l.createFailureWithUpdate(
|
|
||||||
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
||||||
return lnwire.NewFeeInsufficient(
|
return lnwire.NewFeeInsufficient(
|
||||||
amtToForward, *upd,
|
amtToForward, *upd,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
return NewLinkError(failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, we'll ensure that the time-lock on the outgoing HTLC meets
|
// Finally, we'll ensure that the time-lock on the outgoing HTLC meets
|
||||||
@ -2198,26 +2199,27 @@ func (l *channelLink) CheckHtlcForward(payHash [32]byte,
|
|||||||
|
|
||||||
// Grab the latest routing policy so the sending node is up to
|
// Grab the latest routing policy so the sending node is up to
|
||||||
// date with our current policy.
|
// date with our current policy.
|
||||||
return l.createFailureWithUpdate(
|
failure := l.createFailureWithUpdate(
|
||||||
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
||||||
return lnwire.NewIncorrectCltvExpiry(
|
return lnwire.NewIncorrectCltvExpiry(
|
||||||
incomingTimeout, *upd,
|
incomingTimeout, *upd,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
return NewLinkError(failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckHtlcTransit should return a nil error if the passed HTLC details satisfy the
|
// CheckHtlcTransit should return a nil error if the passed HTLC details
|
||||||
// current channel policy. Otherwise, a valid protocol failure message should
|
// satisfy the current channel policy. Otherwise, a LinkError with a
|
||||||
// be returned in order to signal the violation. This call is intended to be
|
// valid protocol failure message should be returned in order to signal
|
||||||
// used for locally initiated payments for which there is no corresponding
|
// the violation. This call is intended to be used for locally initiated
|
||||||
// incoming htlc.
|
// payments for which there is no corresponding incoming htlc.
|
||||||
func (l *channelLink) CheckHtlcTransit(payHash [32]byte,
|
func (l *channelLink) CheckHtlcTransit(payHash [32]byte,
|
||||||
amt lnwire.MilliSatoshi, timeout uint32,
|
amt lnwire.MilliSatoshi, timeout uint32,
|
||||||
heightNow uint32) lnwire.FailureMessage {
|
heightNow uint32) *LinkError {
|
||||||
|
|
||||||
l.RLock()
|
l.RLock()
|
||||||
policy := l.cfg.FwrdingPolicy
|
policy := l.cfg.FwrdingPolicy
|
||||||
@ -2232,7 +2234,7 @@ func (l *channelLink) CheckHtlcTransit(payHash [32]byte,
|
|||||||
// the channel's amount and time lock constraints.
|
// the channel's amount and time lock constraints.
|
||||||
func (l *channelLink) canSendHtlc(policy ForwardingPolicy,
|
func (l *channelLink) canSendHtlc(policy ForwardingPolicy,
|
||||||
payHash [32]byte, amt lnwire.MilliSatoshi, timeout uint32,
|
payHash [32]byte, amt lnwire.MilliSatoshi, timeout uint32,
|
||||||
heightNow uint32) lnwire.FailureMessage {
|
heightNow uint32) *LinkError {
|
||||||
|
|
||||||
// As our first sanity check, we'll ensure that the passed HTLC isn't
|
// As our first sanity check, we'll ensure that the passed HTLC isn't
|
||||||
// too small for the next hop. If so, then we'll cancel the HTLC
|
// too small for the next hop. If so, then we'll cancel the HTLC
|
||||||
@ -2244,13 +2246,14 @@ func (l *channelLink) canSendHtlc(policy ForwardingPolicy,
|
|||||||
|
|
||||||
// As part of the returned error, we'll send our latest routing
|
// As part of the returned error, we'll send our latest routing
|
||||||
// policy so the sending node obtains the most up to date data.
|
// policy so the sending node obtains the most up to date data.
|
||||||
return l.createFailureWithUpdate(
|
failure := l.createFailureWithUpdate(
|
||||||
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
||||||
return lnwire.NewAmountBelowMinimum(
|
return lnwire.NewAmountBelowMinimum(
|
||||||
amt, *upd,
|
amt, *upd,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
return NewLinkError(failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, ensure that the passed HTLC isn't too large. If so, we'll
|
// Next, ensure that the passed HTLC isn't too large. If so, we'll
|
||||||
@ -2261,11 +2264,12 @@ func (l *channelLink) canSendHtlc(policy ForwardingPolicy,
|
|||||||
|
|
||||||
// As part of the returned error, we'll send our latest routing
|
// As part of the returned error, we'll send our latest routing
|
||||||
// policy so the sending node obtains the most up-to-date data.
|
// policy so the sending node obtains the most up-to-date data.
|
||||||
return l.createFailureWithUpdate(
|
failure := l.createFailureWithUpdate(
|
||||||
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
||||||
return lnwire.NewTemporaryChannelFailure(upd)
|
return lnwire.NewTemporaryChannelFailure(upd)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
return NewDetailedLinkError(failure, FailureDetailHTLCExceedsMax)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to avoid offering an HTLC which will expire in the near
|
// We want to avoid offering an HTLC which will expire in the near
|
||||||
@ -2275,12 +2279,12 @@ func (l *channelLink) canSendHtlc(policy ForwardingPolicy,
|
|||||||
l.log.Errorf("htlc(%x) has an expiry that's too soon: "+
|
l.log.Errorf("htlc(%x) has an expiry that's too soon: "+
|
||||||
"outgoing_expiry=%v, best_height=%v", payHash[:],
|
"outgoing_expiry=%v, best_height=%v", payHash[:],
|
||||||
timeout, heightNow)
|
timeout, heightNow)
|
||||||
|
failure := l.createFailureWithUpdate(
|
||||||
return l.createFailureWithUpdate(
|
|
||||||
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
||||||
return lnwire.NewExpiryTooSoon(*upd)
|
return lnwire.NewExpiryTooSoon(*upd)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
return NewLinkError(failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check absolute max delta.
|
// Check absolute max delta.
|
||||||
@ -2289,16 +2293,19 @@ func (l *channelLink) canSendHtlc(policy ForwardingPolicy,
|
|||||||
"the future: got %v, but maximum is %v", payHash[:],
|
"the future: got %v, but maximum is %v", payHash[:],
|
||||||
timeout-heightNow, l.cfg.MaxOutgoingCltvExpiry)
|
timeout-heightNow, l.cfg.MaxOutgoingCltvExpiry)
|
||||||
|
|
||||||
return &lnwire.FailExpiryTooFar{}
|
return NewLinkError(&lnwire.FailExpiryTooFar{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if there is enough balance in this channel.
|
// Check to see if there is enough balance in this channel.
|
||||||
if amt > l.Bandwidth() {
|
if amt > l.Bandwidth() {
|
||||||
return l.createFailureWithUpdate(
|
failure := l.createFailureWithUpdate(
|
||||||
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
||||||
return lnwire.NewTemporaryChannelFailure(upd)
|
return lnwire.NewTemporaryChannelFailure(upd)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
return NewDetailedLinkError(
|
||||||
|
failure, FailureDetailInsufficientBalance,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -5541,7 +5541,7 @@ func TestCheckHtlcForward(t *testing.T) {
|
|||||||
t.Run("below minhtlc", func(t *testing.T) {
|
t.Run("below minhtlc", func(t *testing.T) {
|
||||||
result := link.CheckHtlcForward(hash, 100, 50,
|
result := link.CheckHtlcForward(hash, 100, 50,
|
||||||
200, 150, 0)
|
200, 150, 0)
|
||||||
if _, ok := result.(*lnwire.FailAmountBelowMinimum); !ok {
|
if _, ok := result.WireMessage().(*lnwire.FailAmountBelowMinimum); !ok {
|
||||||
t.Fatalf("expected FailAmountBelowMinimum failure code")
|
t.Fatalf("expected FailAmountBelowMinimum failure code")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -5549,7 +5549,7 @@ func TestCheckHtlcForward(t *testing.T) {
|
|||||||
t.Run("above maxhtlc", func(t *testing.T) {
|
t.Run("above maxhtlc", func(t *testing.T) {
|
||||||
result := link.CheckHtlcForward(hash, 1500, 1200,
|
result := link.CheckHtlcForward(hash, 1500, 1200,
|
||||||
200, 150, 0)
|
200, 150, 0)
|
||||||
if _, ok := result.(*lnwire.FailTemporaryChannelFailure); !ok {
|
if _, ok := result.WireMessage().(*lnwire.FailTemporaryChannelFailure); !ok {
|
||||||
t.Fatalf("expected FailTemporaryChannelFailure failure code")
|
t.Fatalf("expected FailTemporaryChannelFailure failure code")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -5557,7 +5557,7 @@ func TestCheckHtlcForward(t *testing.T) {
|
|||||||
t.Run("insufficient fee", func(t *testing.T) {
|
t.Run("insufficient fee", func(t *testing.T) {
|
||||||
result := link.CheckHtlcForward(hash, 1005, 1000,
|
result := link.CheckHtlcForward(hash, 1005, 1000,
|
||||||
200, 150, 0)
|
200, 150, 0)
|
||||||
if _, ok := result.(*lnwire.FailFeeInsufficient); !ok {
|
if _, ok := result.WireMessage().(*lnwire.FailFeeInsufficient); !ok {
|
||||||
t.Fatalf("expected FailFeeInsufficient failure code")
|
t.Fatalf("expected FailFeeInsufficient failure code")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -5565,7 +5565,7 @@ func TestCheckHtlcForward(t *testing.T) {
|
|||||||
t.Run("expiry too soon", func(t *testing.T) {
|
t.Run("expiry too soon", func(t *testing.T) {
|
||||||
result := link.CheckHtlcForward(hash, 1500, 1000,
|
result := link.CheckHtlcForward(hash, 1500, 1000,
|
||||||
200, 150, 190)
|
200, 150, 190)
|
||||||
if _, ok := result.(*lnwire.FailExpiryTooSoon); !ok {
|
if _, ok := result.WireMessage().(*lnwire.FailExpiryTooSoon); !ok {
|
||||||
t.Fatalf("expected FailExpiryTooSoon failure code")
|
t.Fatalf("expected FailExpiryTooSoon failure code")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -5573,7 +5573,7 @@ func TestCheckHtlcForward(t *testing.T) {
|
|||||||
t.Run("incorrect cltv expiry", func(t *testing.T) {
|
t.Run("incorrect cltv expiry", func(t *testing.T) {
|
||||||
result := link.CheckHtlcForward(hash, 1500, 1000,
|
result := link.CheckHtlcForward(hash, 1500, 1000,
|
||||||
200, 190, 0)
|
200, 190, 0)
|
||||||
if _, ok := result.(*lnwire.FailIncorrectCltvExpiry); !ok {
|
if _, ok := result.WireMessage().(*lnwire.FailIncorrectCltvExpiry); !ok {
|
||||||
t.Fatalf("expected FailIncorrectCltvExpiry failure code")
|
t.Fatalf("expected FailIncorrectCltvExpiry failure code")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5583,7 +5583,7 @@ func TestCheckHtlcForward(t *testing.T) {
|
|||||||
// Check that expiry isn't too far in the future.
|
// Check that expiry isn't too far in the future.
|
||||||
result := link.CheckHtlcForward(hash, 1500, 1000,
|
result := link.CheckHtlcForward(hash, 1500, 1000,
|
||||||
10200, 10100, 0)
|
10200, 10100, 0)
|
||||||
if _, ok := result.(*lnwire.FailExpiryTooFar); !ok {
|
if _, ok := result.WireMessage().(*lnwire.FailExpiryTooFar); !ok {
|
||||||
t.Fatalf("expected FailExpiryTooFar failure code")
|
t.Fatalf("expected FailExpiryTooFar failure code")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -645,9 +645,9 @@ type mockChannelLink struct {
|
|||||||
|
|
||||||
htlcID uint64
|
htlcID uint64
|
||||||
|
|
||||||
checkHtlcTransitResult lnwire.FailureMessage
|
checkHtlcTransitResult *LinkError
|
||||||
|
|
||||||
checkHtlcForwardResult lnwire.FailureMessage
|
checkHtlcForwardResult *LinkError
|
||||||
}
|
}
|
||||||
|
|
||||||
// completeCircuit is a helper method for adding the finalized payment circuit
|
// completeCircuit is a helper method for adding the finalized payment circuit
|
||||||
@ -707,14 +707,14 @@ func (f *mockChannelLink) HandleChannelUpdate(lnwire.Message) {
|
|||||||
func (f *mockChannelLink) UpdateForwardingPolicy(_ ForwardingPolicy) {
|
func (f *mockChannelLink) UpdateForwardingPolicy(_ ForwardingPolicy) {
|
||||||
}
|
}
|
||||||
func (f *mockChannelLink) CheckHtlcForward([32]byte, lnwire.MilliSatoshi,
|
func (f *mockChannelLink) CheckHtlcForward([32]byte, lnwire.MilliSatoshi,
|
||||||
lnwire.MilliSatoshi, uint32, uint32, uint32) lnwire.FailureMessage {
|
lnwire.MilliSatoshi, uint32, uint32, uint32) *LinkError {
|
||||||
|
|
||||||
return f.checkHtlcForwardResult
|
return f.checkHtlcForwardResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *mockChannelLink) CheckHtlcTransit(payHash [32]byte,
|
func (f *mockChannelLink) CheckHtlcTransit(payHash [32]byte,
|
||||||
amt lnwire.MilliSatoshi, timeout uint32,
|
amt lnwire.MilliSatoshi, timeout uint32,
|
||||||
heightNow uint32) lnwire.FailureMessage {
|
heightNow uint32) *LinkError {
|
||||||
|
|
||||||
return f.checkHtlcTransitResult
|
return f.checkHtlcTransitResult
|
||||||
}
|
}
|
||||||
|
@ -756,20 +756,19 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error {
|
|||||||
s.indexMtx.RUnlock()
|
s.indexMtx.RUnlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Link %v not found", pkt.outgoingChanID)
|
log.Errorf("Link %v not found", pkt.outgoingChanID)
|
||||||
return NewForwardingError(
|
return NewLinkError(&lnwire.FailUnknownNextPeer{})
|
||||||
&lnwire.FailUnknownNextPeer{}, 0, "",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !link.EligibleToForward() {
|
if !link.EligibleToForward() {
|
||||||
err := fmt.Errorf("Link %v is not available to forward",
|
log.Errorf("Link %v is not available to forward",
|
||||||
pkt.outgoingChanID)
|
pkt.outgoingChanID)
|
||||||
log.Error(err)
|
|
||||||
|
|
||||||
// The update does not need to be populated as the error
|
// The update does not need to be populated as the error
|
||||||
// will be returned back to the router.
|
// will be returned back to the router.
|
||||||
htlcErr := lnwire.NewTemporaryChannelFailure(nil)
|
return NewDetailedLinkError(
|
||||||
return NewForwardingError(htlcErr, 0, err.Error())
|
lnwire.NewTemporaryChannelFailure(nil),
|
||||||
|
FailureDetailLinkNotEligible,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the htlc satisfies the outgoing channel policy.
|
// Ensure that the htlc satisfies the outgoing channel policy.
|
||||||
@ -782,8 +781,7 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error {
|
|||||||
if htlcErr != nil {
|
if htlcErr != nil {
|
||||||
log.Errorf("Link %v policy for local forward not "+
|
log.Errorf("Link %v policy for local forward not "+
|
||||||
"satisfied", pkt.outgoingChanID)
|
"satisfied", pkt.outgoingChanID)
|
||||||
|
return htlcErr
|
||||||
return NewForwardingError(htlcErr, 0, "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return link.HandleSwitchPacket(pkt)
|
return link.HandleSwitchPacket(pkt)
|
||||||
@ -907,35 +905,43 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter,
|
|||||||
// decrypt the error, simply decode it them report back to the
|
// decrypt the error, simply decode it them report back to the
|
||||||
// user.
|
// user.
|
||||||
case unencrypted:
|
case unencrypted:
|
||||||
var userErr string
|
|
||||||
r := bytes.NewReader(htlc.Reason)
|
r := bytes.NewReader(htlc.Reason)
|
||||||
failureMsg, err := lnwire.DecodeFailure(r, 0)
|
failureMsg, err := lnwire.DecodeFailure(r, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
userErr = fmt.Sprintf("unable to decode onion "+
|
// If we could not decode the failure reason, return a link
|
||||||
"failure (hash=%v, pid=%d): %v",
|
// error indicating that we failed to decode the onion.
|
||||||
paymentHash, paymentID, err)
|
linkError := NewDetailedLinkError(
|
||||||
log.Error(userErr)
|
// As this didn't even clear the link, we don't
|
||||||
|
// need to apply an update here since it goes
|
||||||
|
// directly to the router.
|
||||||
|
lnwire.NewTemporaryChannelFailure(nil),
|
||||||
|
FailureDetailOnionDecode,
|
||||||
|
)
|
||||||
|
|
||||||
// As this didn't even clear the link, we don't need to
|
log.Errorf("%v: (hash=%v, pid=%d): %v",
|
||||||
// apply an update here since it goes directly to the
|
linkError.FailureDetail, paymentHash, paymentID,
|
||||||
// router.
|
err)
|
||||||
failureMsg = lnwire.NewTemporaryChannelFailure(nil)
|
|
||||||
|
return linkError
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewForwardingError(failureMsg, 0, userErr)
|
// If we successfully decoded the failure reason, return it.
|
||||||
|
return NewLinkError(failureMsg)
|
||||||
|
|
||||||
// A payment had to be timed out on chain before it got past
|
// A payment had to be timed out on chain before it got past
|
||||||
// the first hop. In this case, we'll report a permanent
|
// the first hop. In this case, we'll report a permanent
|
||||||
// channel failure as this means us, or the remote party had to
|
// channel failure as this means us, or the remote party had to
|
||||||
// go on chain.
|
// go on chain.
|
||||||
case isResolution && htlc.Reason == nil:
|
case isResolution && htlc.Reason == nil:
|
||||||
userErr := fmt.Sprintf("payment was resolved "+
|
linkError := NewDetailedLinkError(
|
||||||
"on-chain, then canceled back (hash=%v, pid=%d)",
|
&lnwire.FailPermanentChannelFailure{},
|
||||||
|
FailureDetailOnChainTimeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
log.Info("%v: hash=%v, pid=%d", linkError.FailureDetail,
|
||||||
paymentHash, paymentID)
|
paymentHash, paymentID)
|
||||||
|
|
||||||
return NewForwardingError(
|
return linkError
|
||||||
&lnwire.FailPermanentChannelFailure{}, 0, userErr,
|
|
||||||
)
|
|
||||||
|
|
||||||
// A regular multi-hop payment error that we'll need to
|
// A regular multi-hop payment error that we'll need to
|
||||||
// decrypt.
|
// decrypt.
|
||||||
@ -1002,18 +1008,21 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
// selection process. This way we can return the error for
|
// selection process. This way we can return the error for
|
||||||
// precise link that the sender selected, while optimistically
|
// precise link that the sender selected, while optimistically
|
||||||
// trying all links to utilize our available bandwidth.
|
// trying all links to utilize our available bandwidth.
|
||||||
linkErrs := make(map[lnwire.ShortChannelID]lnwire.FailureMessage)
|
linkErrs := make(map[lnwire.ShortChannelID]*LinkError)
|
||||||
|
|
||||||
// Try to find destination channel link with appropriate
|
// Try to find destination channel link with appropriate
|
||||||
// bandwidth.
|
// bandwidth.
|
||||||
var destination ChannelLink
|
var destination ChannelLink
|
||||||
for _, link := range interfaceLinks {
|
for _, link := range interfaceLinks {
|
||||||
var failure lnwire.FailureMessage
|
var failure *LinkError
|
||||||
|
|
||||||
// We'll skip any links that aren't yet eligible for
|
// We'll skip any links that aren't yet eligible for
|
||||||
// forwarding.
|
// forwarding.
|
||||||
if !link.EligibleToForward() {
|
if !link.EligibleToForward() {
|
||||||
failure = &lnwire.FailUnknownNextPeer{}
|
failure = NewDetailedLinkError(
|
||||||
|
&lnwire.FailUnknownNextPeer{},
|
||||||
|
FailureDetailLinkNotEligible,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// We'll ensure that the HTLC satisfies the
|
// We'll ensure that the HTLC satisfies the
|
||||||
// current forwarding conditions of this target
|
// current forwarding conditions of this target
|
||||||
@ -1048,7 +1057,9 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
// If we can't find the error of the source,
|
// If we can't find the error of the source,
|
||||||
// then we'll return an unknown next peer,
|
// then we'll return an unknown next peer,
|
||||||
// though this should never happen.
|
// though this should never happen.
|
||||||
linkErr = &lnwire.FailUnknownNextPeer{}
|
linkErr = NewLinkError(
|
||||||
|
&lnwire.FailUnknownNextPeer{},
|
||||||
|
)
|
||||||
log.Warnf("unable to find err source for "+
|
log.Warnf("unable to find err source for "+
|
||||||
"outgoing_link=%v, errors=%v",
|
"outgoing_link=%v, errors=%v",
|
||||||
packet.outgoingChanID, newLogClosure(func() string {
|
packet.outgoingChanID, newLogClosure(func() string {
|
||||||
@ -1061,7 +1072,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
htlc.PaymentHash[:], packet.outgoingChanID,
|
htlc.PaymentHash[:], packet.outgoingChanID,
|
||||||
linkErr)
|
linkErr)
|
||||||
|
|
||||||
return s.failAddPacket(packet, linkErr, addErr)
|
return s.failAddPacket(packet, linkErr.WireMessage(), addErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the packet to the destination channel link which
|
// Send the packet to the destination channel link which
|
||||||
|
@ -1290,7 +1290,7 @@ func TestSwitchForwardCircuitPersistence(t *testing.T) {
|
|||||||
type multiHopFwdTest struct {
|
type multiHopFwdTest struct {
|
||||||
name string
|
name string
|
||||||
eligible1, eligible2 bool
|
eligible1, eligible2 bool
|
||||||
failure1, failure2 lnwire.FailureMessage
|
failure1, failure2 *LinkError
|
||||||
expectedReply lnwire.FailCode
|
expectedReply lnwire.FailCode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1308,9 +1308,11 @@ func TestSkipIneligibleLinksMultiHopForward(t *testing.T) {
|
|||||||
// Channel one has a policy failure and the other channel isn't
|
// Channel one has a policy failure and the other channel isn't
|
||||||
// available.
|
// available.
|
||||||
{
|
{
|
||||||
name: "policy fail",
|
name: "policy fail",
|
||||||
eligible1: true,
|
eligible1: true,
|
||||||
failure1: lnwire.NewFinalIncorrectCltvExpiry(0),
|
failure1: NewLinkError(
|
||||||
|
lnwire.NewFinalIncorrectCltvExpiry(0),
|
||||||
|
),
|
||||||
expectedReply: lnwire.CodeFinalIncorrectCltvExpiry,
|
expectedReply: lnwire.CodeFinalIncorrectCltvExpiry,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1325,11 +1327,16 @@ func TestSkipIneligibleLinksMultiHopForward(t *testing.T) {
|
|||||||
// The requested channel has insufficient bandwidth and the
|
// The requested channel has insufficient bandwidth and the
|
||||||
// other channel's policy isn't satisfied.
|
// other channel's policy isn't satisfied.
|
||||||
{
|
{
|
||||||
name: "non-strict policy fail",
|
name: "non-strict policy fail",
|
||||||
eligible1: true,
|
eligible1: true,
|
||||||
failure1: lnwire.NewTemporaryChannelFailure(nil),
|
failure1: NewDetailedLinkError(
|
||||||
eligible2: true,
|
lnwire.NewTemporaryChannelFailure(nil),
|
||||||
failure2: lnwire.NewFinalIncorrectCltvExpiry(0),
|
FailureDetailInsufficientBalance,
|
||||||
|
),
|
||||||
|
eligible2: true,
|
||||||
|
failure2: NewLinkError(
|
||||||
|
lnwire.NewFinalIncorrectCltvExpiry(0),
|
||||||
|
),
|
||||||
expectedReply: lnwire.CodeTemporaryChannelFailure,
|
expectedReply: lnwire.CodeTemporaryChannelFailure,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1482,7 +1489,9 @@ func testSkipLinkLocalForward(t *testing.T, eligible bool,
|
|||||||
aliceChannelLink := newMockChannelLink(
|
aliceChannelLink := newMockChannelLink(
|
||||||
s, chanID1, aliceChanID, alicePeer, eligible,
|
s, chanID1, aliceChanID, alicePeer, eligible,
|
||||||
)
|
)
|
||||||
aliceChannelLink.checkHtlcTransitResult = policyResult
|
aliceChannelLink.checkHtlcTransitResult = NewLinkError(
|
||||||
|
policyResult,
|
||||||
|
)
|
||||||
if err := s.AddLink(aliceChannelLink); err != nil {
|
if err := s.AddLink(aliceChannelLink); err != nil {
|
||||||
t.Fatalf("unable to add alice link: %v", err)
|
t.Fatalf("unable to add alice link: %v", err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user