htlcswitch: Fix failure error handling on outgoing adds.
This commit is contained in:
parent
813c012ffe
commit
88dc73adb0
@ -662,22 +662,18 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket, isReProcess bool) {
|
|||||||
// machine, as a result, we'll signal the switch to
|
// machine, as a result, we'll signal the switch to
|
||||||
// cancel the pending payment.
|
// cancel the pending payment.
|
||||||
default:
|
default:
|
||||||
|
log.Warnf("Unable to handle downstream add HTLC: %v", err)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
isObfuscated bool
|
localFailure = false
|
||||||
reason lnwire.OpaqueReason
|
reason lnwire.OpaqueReason
|
||||||
)
|
)
|
||||||
|
|
||||||
// We'll parse the sphinx packet enclosed so we
|
|
||||||
// can obtain the shared secret required to
|
|
||||||
// encrypt the error back to the source.
|
|
||||||
failure := lnwire.NewTemporaryChannelFailure(nil)
|
failure := lnwire.NewTemporaryChannelFailure(nil)
|
||||||
onionReader := bytes.NewReader(htlc.OnionBlob[:])
|
|
||||||
obfuscator, failCode := l.cfg.DecodeOnionObfuscator(onionReader)
|
|
||||||
|
|
||||||
switch {
|
// Encrypt the error back to the source unless the payment was
|
||||||
// If we were unable to parse the onion blob,
|
// generated locally.
|
||||||
// then we'll send an error back to the source.
|
if pkt.obfuscator == nil {
|
||||||
case failCode != lnwire.CodeNone:
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
err := lnwire.EncodeFailure(&b, failure, 0)
|
err := lnwire.EncodeFailure(&b, failure, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -685,24 +681,22 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket, isReProcess bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
reason = lnwire.OpaqueReason(b.Bytes())
|
reason = lnwire.OpaqueReason(b.Bytes())
|
||||||
isObfuscated = false
|
localFailure = true
|
||||||
|
} else {
|
||||||
// Otherwise, we'll send back a proper failure
|
var err error
|
||||||
// message.
|
reason, err = pkt.obfuscator.EncryptFirstHop(failure)
|
||||||
default:
|
|
||||||
reason, err = obfuscator.EncryptFirstHop(failure)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to obfuscate error: %v", err)
|
log.Errorf("unable to obfuscate error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isObfuscated = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
failPkt := &htlcPacket{
|
failPkt := &htlcPacket{
|
||||||
incomingChanID: pkt.incomingChanID,
|
incomingChanID: pkt.incomingChanID,
|
||||||
incomingHTLCID: pkt.incomingHTLCID,
|
incomingHTLCID: pkt.incomingHTLCID,
|
||||||
amount: htlc.Amount,
|
amount: htlc.Amount,
|
||||||
isObfuscated: isObfuscated,
|
isRouted: true,
|
||||||
|
localFailure: localFailure,
|
||||||
htlc: &lnwire.UpdateFailHTLC{
|
htlc: &lnwire.UpdateFailHTLC{
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
},
|
},
|
||||||
@ -711,7 +705,6 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket, isReProcess bool) {
|
|||||||
// TODO(roasbeef): need to identify if sent
|
// TODO(roasbeef): need to identify if sent
|
||||||
// from switch so don't need to obfuscate
|
// from switch so don't need to obfuscate
|
||||||
go l.cfg.Switch.forward(failPkt)
|
go l.cfg.Switch.forward(failPkt)
|
||||||
log.Infof("Unable to handle downstream add HTLC: %v", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1214,7 +1207,6 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
outgoingChanID: l.ShortChanID(),
|
outgoingChanID: l.ShortChanID(),
|
||||||
outgoingHTLCID: pd.ParentIndex,
|
outgoingHTLCID: pd.ParentIndex,
|
||||||
amount: pd.Amount,
|
amount: pd.Amount,
|
||||||
isObfuscated: false,
|
|
||||||
htlc: &lnwire.UpdateFailHTLC{
|
htlc: &lnwire.UpdateFailHTLC{
|
||||||
Reason: lnwire.OpaqueReason(pd.FailReason),
|
Reason: lnwire.OpaqueReason(pd.FailReason),
|
||||||
},
|
},
|
||||||
|
@ -33,7 +33,6 @@ func TestMailBoxCouriers(t *testing.T) {
|
|||||||
outgoingChanID: lnwire.NewShortChanIDFromInt(uint64(prand.Int63())),
|
outgoingChanID: lnwire.NewShortChanIDFromInt(uint64(prand.Int63())),
|
||||||
incomingChanID: lnwire.NewShortChanIDFromInt(uint64(prand.Int63())),
|
incomingChanID: lnwire.NewShortChanIDFromInt(uint64(prand.Int63())),
|
||||||
amount: lnwire.MilliSatoshi(prand.Int63()),
|
amount: lnwire.MilliSatoshi(prand.Int63()),
|
||||||
isObfuscated: i%2 == 0,
|
|
||||||
}
|
}
|
||||||
sentPackets[i] = pkt
|
sentPackets[i] = pkt
|
||||||
|
|
||||||
|
@ -37,11 +37,13 @@ type htlcPacket struct {
|
|||||||
// any forwarded errors in an additional layer of encryption.
|
// any forwarded errors in an additional layer of encryption.
|
||||||
obfuscator ErrorEncrypter
|
obfuscator ErrorEncrypter
|
||||||
|
|
||||||
// isObfuscated is set to true if an error occurs as soon as the switch
|
// localFailure is set to true if an HTLC fails for a local payment before
|
||||||
// forwards a packet to the link. If so, and this is an error packet,
|
// the first hop. In this case, the failure reason is simply encoded, not
|
||||||
// then this allows the switch to avoid doubly encrypting the error.
|
// encrypted with any shared secret.
|
||||||
//
|
localFailure bool
|
||||||
// TODO(andrew.shvv) revisit after refactoring the way of returning
|
|
||||||
// errors inside the htlcswitch packet.
|
// isRouted is set to true if the incomingChanID and incomingHTLCID fields
|
||||||
isObfuscated bool
|
// of a forwarded fail packet are already set and do not need to be looked
|
||||||
|
// up in the circuit map.
|
||||||
|
isRouted bool
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package htlcswitch
|
package htlcswitch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -435,23 +436,39 @@ func (s *Switch) handleLocalDispatch(packet *htlcPacket) error {
|
|||||||
// We've just received a fail update which means we can finalize the
|
// We've just received a fail update which means we can finalize the
|
||||||
// user payment and return fail response.
|
// user payment and return fail response.
|
||||||
case *lnwire.UpdateFailHTLC:
|
case *lnwire.UpdateFailHTLC:
|
||||||
// We'll attempt to fully decrypt the onion encrypted error. If
|
var failure *ForwardingError
|
||||||
// we're unable to then we'll bail early.
|
if packet.localFailure {
|
||||||
failure, err := payment.deobfuscator.DecryptError(htlc.Reason)
|
var userErr string
|
||||||
if err != nil {
|
r := bytes.NewReader(htlc.Reason)
|
||||||
userErr := fmt.Sprintf("unable to de-obfuscate "+
|
failureMsg, err := lnwire.DecodeFailure(r, 0)
|
||||||
"onion failure, htlc with hash(%x): %v",
|
if err != nil {
|
||||||
payment.paymentHash[:], err)
|
userErr = fmt.Sprintf("unable to decode onion failure, "+
|
||||||
log.Error(userErr)
|
"htlc with hash(%x): %v", payment.paymentHash[:], err)
|
||||||
payment.err <- &ForwardingError{
|
log.Error(userErr)
|
||||||
|
failureMsg = lnwire.NewTemporaryChannelFailure(nil)
|
||||||
|
}
|
||||||
|
failure = &ForwardingError{
|
||||||
ErrorSource: s.cfg.SelfKey,
|
ErrorSource: s.cfg.SelfKey,
|
||||||
ExtraMsg: userErr,
|
ExtraMsg: userErr,
|
||||||
FailureMessage: lnwire.NewTemporaryChannelFailure(nil),
|
FailureMessage: failureMsg,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
payment.err <- failure
|
// We'll attempt to fully decrypt the onion encrypted error. If
|
||||||
|
// we're unable to then we'll bail early.
|
||||||
|
failure, err = payment.deobfuscator.DecryptError(htlc.Reason)
|
||||||
|
if err != nil {
|
||||||
|
userErr := fmt.Sprintf("unable to de-obfuscate onion failure, "+
|
||||||
|
"htlc with hash(%x): %v", payment.paymentHash[:], err)
|
||||||
|
log.Error(userErr)
|
||||||
|
failure = &ForwardingError{
|
||||||
|
ErrorSource: s.cfg.SelfKey,
|
||||||
|
ExtraMsg: userErr,
|
||||||
|
FailureMessage: lnwire.NewTemporaryChannelFailure(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
payment.err <- failure
|
||||||
payment.preimage <- zeroPreimage
|
payment.preimage <- zeroPreimage
|
||||||
s.removePendingPayment(packet.incomingHTLCID)
|
s.removePendingPayment(packet.incomingHTLCID)
|
||||||
|
|
||||||
@ -503,7 +520,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
source.HandleSwitchPacket(&htlcPacket{
|
source.HandleSwitchPacket(&htlcPacket{
|
||||||
incomingChanID: packet.incomingChanID,
|
incomingChanID: packet.incomingChanID,
|
||||||
incomingHTLCID: packet.incomingHTLCID,
|
incomingHTLCID: packet.incomingHTLCID,
|
||||||
isObfuscated: true,
|
isRouted: true,
|
||||||
htlc: &lnwire.UpdateFailHTLC{
|
htlc: &lnwire.UpdateFailHTLC{
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
},
|
},
|
||||||
@ -551,7 +568,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
source.HandleSwitchPacket(&htlcPacket{
|
source.HandleSwitchPacket(&htlcPacket{
|
||||||
incomingChanID: packet.incomingChanID,
|
incomingChanID: packet.incomingChanID,
|
||||||
incomingHTLCID: packet.incomingHTLCID,
|
incomingHTLCID: packet.incomingHTLCID,
|
||||||
isObfuscated: true,
|
isRouted: true,
|
||||||
htlc: &lnwire.UpdateFailHTLC{
|
htlc: &lnwire.UpdateFailHTLC{
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
},
|
},
|
||||||
@ -573,7 +590,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
// payment circuit by forwarding the settle msg to the channel from
|
// payment circuit by forwarding the settle msg to the channel from
|
||||||
// which htlc add packet was initially received.
|
// which htlc add packet was initially received.
|
||||||
case *lnwire.UpdateFufillHTLC, *lnwire.UpdateFailHTLC:
|
case *lnwire.UpdateFufillHTLC, *lnwire.UpdateFailHTLC:
|
||||||
if packet.incomingChanID == (lnwire.ShortChannelID{}) {
|
if !packet.isRouted {
|
||||||
// Use circuit map to find the link to forward settle/fail to.
|
// Use circuit map to find the link to forward settle/fail to.
|
||||||
circuit := s.circuits.LookupByHTLC(packet.outgoingChanID,
|
circuit := s.circuits.LookupByHTLC(packet.outgoingChanID,
|
||||||
packet.outgoingHTLCID)
|
packet.outgoingHTLCID)
|
||||||
@ -603,20 +620,22 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
packet.incomingChanID = circuit.IncomingChanID
|
packet.incomingChanID = circuit.IncomingChanID
|
||||||
packet.incomingHTLCID = circuit.IncomingHTLCID
|
packet.incomingHTLCID = circuit.IncomingHTLCID
|
||||||
|
|
||||||
// A blank IncomingChanID in a circuit indicates that it is a
|
|
||||||
// pending user-initiated payment.
|
|
||||||
if circuit.IncomingChanID == (lnwire.ShortChannelID{}) {
|
|
||||||
return s.handleLocalDispatch(packet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obfuscate the error message for fail updates before sending back
|
// Obfuscate the error message for fail updates before sending back
|
||||||
// through the circuit.
|
// through the circuit unless the payment was generated locally.
|
||||||
if htlc, ok := htlc.(*lnwire.UpdateFailHTLC); ok && !packet.isObfuscated {
|
if circuit.ErrorEncrypter != nil {
|
||||||
htlc.Reason = circuit.ErrorEncrypter.IntermediateEncrypt(
|
if htlc, ok := htlc.(*lnwire.UpdateFailHTLC); ok {
|
||||||
htlc.Reason)
|
htlc.Reason = circuit.ErrorEncrypter.IntermediateEncrypt(
|
||||||
|
htlc.Reason)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A blank IncomingChanID in a circuit indicates that it is a pending
|
||||||
|
// user-initiated payment.
|
||||||
|
if packet.incomingChanID == (lnwire.ShortChannelID{}) {
|
||||||
|
return s.handleLocalDispatch(packet)
|
||||||
|
}
|
||||||
|
|
||||||
source, err := s.getLinkByShortID(packet.incomingChanID)
|
source, err := s.getLinkByShortID(packet.incomingChanID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := errors.Errorf("Unable to get source channel link to "+
|
err := errors.Errorf("Unable to get source channel link to "+
|
||||||
|
@ -270,7 +270,6 @@ func TestSwitchCancel(t *testing.T) {
|
|||||||
outgoingChanID: bobChannelLink.ShortChanID(),
|
outgoingChanID: bobChannelLink.ShortChanID(),
|
||||||
outgoingHTLCID: 0,
|
outgoingHTLCID: 0,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
isObfuscated: true,
|
|
||||||
htlc: &lnwire.UpdateFailHTLC{},
|
htlc: &lnwire.UpdateFailHTLC{},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,7 +372,6 @@ func TestSwitchAddSamePayment(t *testing.T) {
|
|||||||
outgoingChanID: bobChannelLink.ShortChanID(),
|
outgoingChanID: bobChannelLink.ShortChanID(),
|
||||||
outgoingHTLCID: 0,
|
outgoingHTLCID: 0,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
isObfuscated: true,
|
|
||||||
htlc: &lnwire.UpdateFailHTLC{},
|
htlc: &lnwire.UpdateFailHTLC{},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,7 +395,6 @@ func TestSwitchAddSamePayment(t *testing.T) {
|
|||||||
outgoingChanID: bobChannelLink.ShortChanID(),
|
outgoingChanID: bobChannelLink.ShortChanID(),
|
||||||
outgoingHTLCID: 1,
|
outgoingHTLCID: 1,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
isObfuscated: true,
|
|
||||||
htlc: &lnwire.UpdateFailHTLC{},
|
htlc: &lnwire.UpdateFailHTLC{},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,7 +497,6 @@ func TestSwitchSendPayment(t *testing.T) {
|
|||||||
outgoingChanID: aliceChannelLink.ShortChanID(),
|
outgoingChanID: aliceChannelLink.ShortChanID(),
|
||||||
outgoingHTLCID: 0,
|
outgoingHTLCID: 0,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
isObfuscated: true,
|
|
||||||
htlc: &lnwire.UpdateFailHTLC{
|
htlc: &lnwire.UpdateFailHTLC{
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
},
|
},
|
||||||
@ -522,7 +518,6 @@ func TestSwitchSendPayment(t *testing.T) {
|
|||||||
packet = &htlcPacket{
|
packet = &htlcPacket{
|
||||||
outgoingChanID: aliceChannelLink.ShortChanID(),
|
outgoingChanID: aliceChannelLink.ShortChanID(),
|
||||||
outgoingHTLCID: 1,
|
outgoingHTLCID: 1,
|
||||||
isObfuscated: true,
|
|
||||||
htlc: &lnwire.UpdateFailHTLC{
|
htlc: &lnwire.UpdateFailHTLC{
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
},
|
},
|
||||||
|
@ -4043,7 +4043,8 @@ func testAsyncPayments(net *networkHarness, t *harnessTest) {
|
|||||||
// Open up a payment stream to Alice that we'll use to send payment to
|
// Open up a payment stream to Alice that we'll use to send payment to
|
||||||
// Bob. We also create a small helper function to send payments to Bob,
|
// Bob. We also create a small helper function to send payments to Bob,
|
||||||
// consuming the payment hashes we generated above.
|
// consuming the payment hashes we generated above.
|
||||||
alicePayStream, err := net.Alice.SendPayment(ctxb)
|
ctxt, _ = context.WithTimeout(ctxb, time.Minute)
|
||||||
|
alicePayStream, err := net.Alice.SendPayment(ctxt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create payment stream for alice: %v", err)
|
t.Fatalf("unable to create payment stream for alice: %v", err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user