Merge pull request #11033 from lightningnetwork/backport-11032-to-v0.20.x-branch

[v0.20.x-branch] Backport #11032: contractcourt: retain deadline across contest resolution
This commit is contained in:
ziggieXXX 2026-08-06 11:18:37 -03:00 committed by GitHub
commit d8ed6781d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 46 additions and 2 deletions

View file

@ -229,10 +229,14 @@ func (h *htlcOutgoingContestResolver) Encode(w io.Writer) error {
return h.htlcTimeoutResolver.Encode(w)
}
// SupplementDeadline does nothing for an incoming htlc resolver.
// SupplementDeadline forwards the incoming HTLC's expiry height to the inner
// timeout resolver. This resolver morphs into that timeout resolver once the
// outgoing HTLC expires on-chain, so the deadline is retained across the
// transition.
//
// NOTE: Part of the htlcContractResolver interface.
func (h *htlcOutgoingContestResolver) SupplementDeadline(_ fn.Option[int32]) {
func (h *htlcOutgoingContestResolver) SupplementDeadline(d fn.Option[int32]) {
h.htlcTimeoutResolver.SupplementDeadline(d)
}
// newOutgoingContestResolverFromReader attempts to decode an encoded ContractResolver

View file

@ -7,6 +7,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/kvdb"
@ -20,6 +21,10 @@ import (
const (
outgoingContestHtlcExpiry = 110
// outgoingContestIncomingHtlcExpiry is kept distinct from the outgoing
// HTLC expiry to verify that the supplied value is retained.
outgoingContestIncomingHtlcExpiry = 144
)
// TestHtlcOutgoingResolverTimeout tests resolution of an offered htlc that
@ -116,6 +121,36 @@ type resolveResult struct {
nextResolver ContractResolver
}
// TestHtlcOutgoingResolverSupplementDeadline checks that the outgoing contest
// resolver forwards the incoming HTLC deadline to the timeout resolver it
// transitions into once the outgoing HTLC expires on-chain.
func TestHtlcOutgoingResolverSupplementDeadline(t *testing.T) {
t.Parallel()
defer timeout()()
ctx := newOutgoingResolverTestContext(t)
// Initially the embedded timeout resolver carries no deadline.
require.True(t, ctx.resolver.incomingHTLCExpiryHeight.IsNone())
// Supply the deadline through the contest resolver, as the channel
// arbitrator does when constructing the resolver.
deadline := fn.Some(int32(outgoingContestIncomingHtlcExpiry))
ctx.resolver.SupplementDeadline(deadline)
// Drive the contest resolver to the point where it returns the embedded
// timeout resolver.
ctx.resolve()
ctx.notifyEpoch(outgoingContestHtlcExpiry)
result := <-ctx.resolverResultChan
require.NoError(t, result.err)
timeoutRes, ok := result.nextResolver.(*htlcTimeoutResolver)
require.True(t, ok, "expected htlcTimeoutResolver")
require.Equal(t, deadline, timeoutRes.incomingHTLCExpiryHeight)
}
type outgoingResolverTestContext struct {
resolver *htlcOutgoingContestResolver
notifier *mock.ChainNotifier

View file

@ -51,6 +51,11 @@
and legacy payment paths, including keysend records and preimage-dependent
settlement outcomes.
* Outgoing contest resolvers now [retain the corresponding incoming HTLC
expiry](https://github.com/lightningnetwork/lnd/pull/11032) when transitioning
to timeout resolution, allowing the sweeper to continue using an
expiry-aware confirmation target.
# New Features
## Functional Enhancements