From ad1566ee3fccbb7636eb00095db02fd730e0aa27 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Mon, 30 Jun 2025 21:38:13 +0800 Subject: [PATCH] htlcswitch: add `processRemoteUpdateFailHTLC` --- htlcswitch/link.go | 76 ++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 2dd7ce2c0..444ff82a1 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -1945,40 +1945,9 @@ func (l *channelLink) handleUpstreamMsg(ctx context.Context, } case *lnwire.UpdateFailHTLC: - // Verify that the failure reason is at least 256 bytes plus - // overhead. - const minimumFailReasonLength = lnwire.FailureMessageLength + - 2 + 2 + 32 - - if len(msg.Reason) < minimumFailReasonLength { - // We've received a reason with a non-compliant length. - // Older nodes happily relay back these failures that - // may originate from a node further downstream. - // Therefore we can't just fail the channel. - // - // We want to be compliant ourselves, so we also can't - // pass back the reason unmodified. And we must make - // sure that we don't hit the magic length check of 260 - // bytes in processRemoteSettleFails either. - // - // Because the reason is unreadable for the payer - // anyway, we just replace it by a compliant-length - // series of random bytes. - msg.Reason = make([]byte, minimumFailReasonLength) - _, err := crand.Read(msg.Reason[:]) - if err != nil { - l.log.Errorf("Random generation error: %v", err) - - return - } - } - - // Add fail to the update log. - idx := msg.ID - err := l.channel.ReceiveFailHTLC(idx, msg.Reason[:]) + err := l.processRemoteUpdateFailHTLC(msg) if err != nil { - l.failf(LinkFailureError{code: ErrInvalidUpdate}, - "unable to handle upstream fail HTLC: %v", err) + l.log.Errorf("failed to process remote fail: %v", err) return } @@ -4684,3 +4653,44 @@ func (l *channelLink) processRemoteUpdateFailMalformedHTLC( return nil } + +// processRemoteUpdateFailHTLC takes an `UpdateFailHTLC` msg sent from the +// remote and processes it. +func (l *channelLink) processRemoteUpdateFailHTLC( + msg *lnwire.UpdateFailHTLC) error { + + // Verify that the failure reason is at least 256 bytes plus overhead. + const minimumFailReasonLength = lnwire.FailureMessageLength + 2 + 2 + 32 + + if len(msg.Reason) < minimumFailReasonLength { + // We've received a reason with a non-compliant length. Older + // nodes happily relay back these failures that may originate + // from a node further downstream. Therefore we can't just fail + // the channel. + // + // We want to be compliant ourselves, so we also can't pass back + // the reason unmodified. And we must make sure that we don't + // hit the magic length check of 260 bytes in + // processRemoteSettleFails either. + // + // Because the reason is unreadable for the payer anyway, we + // just replace it by a compliant-length series of random bytes. + msg.Reason = make([]byte, minimumFailReasonLength) + _, err := crand.Read(msg.Reason[:]) + if err != nil { + return fmt.Errorf("random generation error: %w", err) + } + } + + // Add fail to the update log. + idx := msg.ID + err := l.channel.ReceiveFailHTLC(idx, msg.Reason[:]) + if err != nil { + l.failf(LinkFailureError{code: ErrInvalidUpdate}, + "unable to handle upstream fail HTLC: %v", err) + + return err + } + + return nil +}