From fb95458a1bd53b931ff9b9f2231bffe6976b8351 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Wed, 11 Jun 2025 12:16:09 +0800 Subject: [PATCH] htlcswitch: skip checking replays for reforwarded packets We now rely on the forwarding package's state to decide whether a given packet is a reforwarding or not. If we know it's a reforwarding packet, there's no need to check for replays in the `sharedHashes` bucket, which behaves the same as if we are querying the `batchReplayBkt`. --- htlcswitch/hop/iterator.go | 14 ++++++++------ htlcswitch/link.go | 18 ++++++++++-------- htlcswitch/mock.go | 2 +- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/htlcswitch/hop/iterator.go b/htlcswitch/hop/iterator.go index 2cf2adb54..314f05209 100644 --- a/htlcswitch/hop/iterator.go +++ b/htlcswitch/hop/iterator.go @@ -745,7 +745,8 @@ func (r *DecodeHopIteratorResponse) Result() (Iterator, lnwire.FailCode) { // the presented readers and rhashes *NEVER* deviate across invocations for the // same id. func (p *OnionProcessor) DecodeHopIterators(id []byte, - reqs []DecodeHopIteratorRequest) ([]DecodeHopIteratorResponse, error) { + reqs []DecodeHopIteratorRequest, + reforward bool) ([]DecodeHopIteratorResponse, error) { var ( batchSize = len(reqs) @@ -864,11 +865,12 @@ func (p *OnionProcessor) DecodeHopIterators(id []byte, continue } - // If this index is contained in the replay set, mark it with a - // temporary channel failure error code. We infer that the - // offending error was due to a replayed packet because this - // index was found in the replay set. - if replays.Contains(uint16(i)) { + // If this index is contained in the replay set, and it is not a + // reforwarding on startup, mark it with a permanent channel + // failure error code. We infer that the offending error was due + // to a replayed packet because this index was found in the + // replay set. + if !reforward && replays.Contains(uint16(i)) { log.Errorf("unable to process onion packet: %v", sphinx.ErrReplayedPacket) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 72b506986..ec7cb1539 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -108,8 +108,10 @@ type ChannelLinkConfig struct { // blobs, which are then used to inform how to forward an HTLC. // // NOTE: This function assumes the same set of readers and preimages - // are always presented for the same identifier. - DecodeHopIterators func([]byte, []hop.DecodeHopIteratorRequest) ( + // are always presented for the same identifier. The last boolean is + // used to decide whether this is a reforwarding or not - when it's + // reforwarding, we skip the replay check enforced in our decay log. + DecodeHopIterators func([]byte, []hop.DecodeHopIteratorRequest, bool) ( []hop.DecodeHopIteratorResponse, error) // ExtractErrorEncrypter function is responsible for decoding HTLC @@ -3764,12 +3766,14 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg) { } } + reforward := fwdPkg.State != channeldb.FwdStateLockedIn + // Atomically decode the incoming htlcs, simultaneously checking for // replay attempts. A particular index in the returned, spare list of // channel iterators should only be used if the failure code at the // same index is lnwire.FailCodeNone. decodeResps, sphinxErr := l.cfg.DecodeHopIterators( - fwdPkg.ID(), decodeReqs, + fwdPkg.ID(), decodeReqs, reforward, ) if sphinxErr != nil { l.failf(LinkFailureError{code: ErrInternalError}, @@ -4120,17 +4124,15 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg) { return } - replay := fwdPkg.State != channeldb.FwdStateLockedIn - - l.log.Debugf("forwarding %d packets to switch: replay=%v", - len(switchPackets), replay) + l.log.Debugf("forwarding %d packets to switch: reforward=%v", + len(switchPackets), reforward) // NOTE: This call is made synchronous so that we ensure all circuits // are committed in the exact order that they are processed in the link. // Failing to do this could cause reorderings/gaps in the range of // opened circuits, which violates assumptions made by the circuit // trimming. - l.forwardBatch(replay, switchPackets...) + l.forwardBatch(reforward, switchPackets...) } // experimentalEndorsement returns the value to set for our outgoing diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index cb814e95c..136d5c39b 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -522,7 +522,7 @@ func (p *mockIteratorDecoder) DecodeHopIterator(r io.Reader, rHash []byte, } func (p *mockIteratorDecoder) DecodeHopIterators(id []byte, - reqs []hop.DecodeHopIteratorRequest) ( + reqs []hop.DecodeHopIteratorRequest, _ bool) ( []hop.DecodeHopIteratorResponse, error) { idHash := sha256.Sum256(id)