htlcswitch: convert blinded failures for blinded payments

This commit is contained in:
Carla Kirk-Cohen 2024-04-08 15:51:15 -04:00
parent de9c9c028c
commit 43687181f7
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91
2 changed files with 117 additions and 7 deletions

View File

@ -39,6 +39,12 @@ const (
EncrypterTypeRelaying = 4
)
// IsBlinded returns a boolean indicating whether the error encrypter belongs
// to a blinded route.
func (e EncrypterType) IsBlinded() bool {
return e == EncrypterTypeIntroduction || e == EncrypterTypeRelaying
}
// ErrorEncrypterExtracter defines a function signature that extracts an
// ErrorEncrypter from an sphinx OnionPacket.
type ErrorEncrypterExtracter func(*btcec.PublicKey) (ErrorEncrypter,

View File

@ -1793,8 +1793,20 @@ func (l *channelLink) handleDownstreamPkt(pkt *htlcPacket) {
htlc.ID = pkt.incomingHTLCID
// We send the HTLC message to the peer which initially created
// the HTLC.
l.cfg.Peer.SendMessage(false, htlc)
// the HTLC. If the incoming blinding point is non-nil, we
// know that we are a relaying node in a blinded path.
// Otherwise, we're either an introduction node or not part of
// a blinded path at all.
if err := l.sendIncomingHTLCFailureMsg(
htlc.ID,
pkt.obfuscator,
htlc.Reason,
); err != nil {
l.log.Errorf("unable to send HTLC failure: %v",
err)
return
}
// If the packet does not have a link failure set, it failed
// further down the route so we notify a forwarding failure.
@ -3720,11 +3732,14 @@ func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor,
return
}
l.cfg.Peer.SendMessage(false, &lnwire.UpdateFailHTLC{
ChanID: l.ChanID(),
ID: pd.HtlcIndex,
Reason: reason,
})
// Send the appropriate failure message depending on whether we're
// in a blinded route or not.
if err := l.sendIncomingHTLCFailureMsg(
pd.HtlcIndex, e, reason,
); err != nil {
l.log.Errorf("unable to send HTLC failure: %v", err)
return
}
// Notify a link failure on our incoming link. Outgoing htlc information
// is not available at this point, because we have not decrypted the
@ -3753,6 +3768,95 @@ func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor,
)
}
// sendPeerHTLCFailure handles sending a HTLC failure message back to the
// peer from which the HTLC was received. This function is primarily used to
// handle the special requirements of route blinding, specifically:
// - Forwarding nodes must switch out any errors with MalformedFailHTLC
// - Introduction nodes should return regular HTLC failure messages.
//
// It accepts the original opaque failure, which will be used in the case
// that we're not part of a blinded route and an error encrypter that'll be
// used if we are the introduction node and need to present an error as if
// we're the failing party.
//
// Note: this function does not yet handle special error cases for receiving
// nodes in blinded paths, as LND does not support blinded receives.
func (l *channelLink) sendIncomingHTLCFailureMsg(htlcIndex uint64,
e hop.ErrorEncrypter,
originalFailure lnwire.OpaqueReason) error {
var msg lnwire.Message
switch {
// Our circuit's error encrypter will be nil if this was a locally
// initiated payment. We can only hit a blinded error for a locally
// initiated payment if we allow ourselves to be picked as the
// introduction node for our own payments and in that case we
// shouldn't reach this code. To prevent the HTLC getting stuck,
// we fail it back and log an error.
// code.
case e == nil:
msg = &lnwire.UpdateFailHTLC{
ChanID: l.ChanID(),
ID: htlcIndex,
Reason: originalFailure,
}
l.log.Errorf("Unexpected blinded failure when "+
"we are the sending node, incoming htlc: %v(%v)",
l.ShortChanID(), htlcIndex)
// For cleartext hops (ie, non-blinded/normal) we don't need any
// transformation on the error message and can just send the original.
case !e.Type().IsBlinded():
msg = &lnwire.UpdateFailHTLC{
ChanID: l.ChanID(),
ID: htlcIndex,
Reason: originalFailure,
}
// When we're the introduction node, we need to convert the error to
// a UpdateFailHTLC.
case e.Type() == hop.EncrypterTypeIntroduction:
l.log.Debugf("Introduction blinded node switching out failure "+
"error: %v", htlcIndex)
// The specification does not require that we set the onion
// blob.
failureMsg := lnwire.NewInvalidBlinding(nil)
reason, err := e.EncryptFirstHop(failureMsg)
if err != nil {
return err
}
msg = &lnwire.UpdateFailHTLC{
ChanID: l.ChanID(),
ID: htlcIndex,
Reason: reason,
}
// If we are a relaying node, we need to switch out any error that
// we've received to a malformed HTLC error.
case e.Type() == hop.EncrypterTypeRelaying:
l.log.Debugf("Relaying blinded node switching out malformed "+
"error: %v", htlcIndex)
msg = &lnwire.UpdateFailMalformedHTLC{
ChanID: l.ChanID(),
ID: htlcIndex,
FailureCode: lnwire.CodeInvalidBlinding,
}
default:
return fmt.Errorf("unexpected encrypter: %d", e)
}
if err := l.cfg.Peer.SendMessage(false, msg); err != nil {
l.log.Warnf("Send update fail failed: %v", err)
}
return nil
}
// sendMalformedHTLCError helper function which sends the malformed HTLC update
// to the payment sender.
func (l *channelLink) sendMalformedHTLCError(htlcIndex uint64,