Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
44bf387
lnwallet+contractcourt: gate HTLC sighash on negotiated feature bit
GeorgeTsagk Apr 14, 2026
72058c1
lnwallet: add CPFP anchor to second-level HTLCs under SigHashDefault
GeorgeTsagk Apr 14, 2026
968cb8d
contractcourt: publish pre-signed second-level HTLCs under SigHashDef…
GeorgeTsagk Apr 14, 2026
495e264
contractcourt: CPFP-sweep the second-level HTLC anchor after publish
GeorgeTsagk Aug 17, 2026
44e5a5e
lnwallet/chanfunding: allow maxFeeRatio above 1.0
GeorgeTsagk Aug 17, 2026
a99d271
lnwire: add CustomRecords to RevokeAndAck
GeorgeTsagk Aug 18, 2026
095d279
lnwallet+chanstate: exchange revocation AuxSigs in RevokeAndAck
GeorgeTsagk Aug 18, 2026
da59bf0
lnwallet+lnwire: add revocation AuxSig tests
GeorgeTsagk Aug 18, 2026
d5d04e7
lnwallet: populate resolution blob for htlc retribution
GeorgeTsagk Apr 14, 2026
83e8b99
lnwallet: pass breach height to HTLC resolution requests
GeorgeTsagk Apr 14, 2026
747e399
lnwallet: extend FetchLeavesFromRevocation with channel state params
GeorgeTsagk Apr 14, 2026
874be66
lnwallet: preserve resolution request for HTLC resolutions
GeorgeTsagk Apr 14, 2026
42e4574
lnwallet: populate AuxSigDesc for breach HTLC retribution
GeorgeTsagk Apr 14, 2026
f4e8ae7
sweep+contractcourt: notify aux sweeper only on confirmed sweeps
GeorgeTsagk Apr 14, 2026
1ac1242
contractcourt: add tests for notifyConfirmedJusticeTx
GeorgeTsagk Apr 14, 2026
f1ee78a
contractcourt: enhance breach arbiter for second-level HTLC revocation
GeorgeTsagk Apr 14, 2026
57b56f0
contractcourt: track historic justice tx variants across rebuild cycles
GeorgeTsagk Apr 14, 2026
919fab8
contractcourt: use dust limit for justice tx sweep output check
GeorgeTsagk Apr 14, 2026
970c235
lnwallet+contractcourt+sweep: consume revocation AuxSigs at breach time
GeorgeTsagk Aug 17, 2026
9bb1552
contractcourt: re-resolve aux blob for second-level output sweeps
GeorgeTsagk Aug 17, 2026
db471fa
docs: add release notes entry for aux HTLC revocation
GeorgeTsagk Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions channeldb/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,13 @@ type openChannelTlvData struct {
// Note: if not set, it means either the channel has not been
// closed yet, or it was closed before this field was introduced.
closeConfirmationHeight tlv.OptionalRecordT[tlv.TlvType9, uint32]

// revocationAuxSigs is an optional blob carrying the revocation aux
// signatures attached to the most recently sent RevokeAndAck. It is
// only ever set for aux/custom (taproot asset) channels, so the same
// signatures can be re-attached if that RevokeAndAck has to be
// retransmitted on channel reestablish.
revocationAuxSigs tlv.OptionalRecordT[tlv.TlvType10, tlv.Blob]
}

// encode serializes the openChannelTlvData to the given io.Writer.
Expand Down Expand Up @@ -313,6 +320,11 @@ func (c *openChannelTlvData) encode(w io.Writer) error {
tlvRecords = append(tlvRecords, h.Record())
},
)
c.revocationAuxSigs.WhenSome(
func(sigs tlv.RecordT[tlv.TlvType10, tlv.Blob]) {
tlvRecords = append(tlvRecords, sigs.Record())
},
)

tlv.SortRecords(tlvRecords)

Expand All @@ -331,6 +343,7 @@ func (c *openChannelTlvData) decode(r io.Reader) error {
tapscriptRoot := c.tapscriptRoot.Zero()
blob := c.customBlob.Zero()
closeConfHeight := c.closeConfirmationHeight.Zero()
revocationAuxSigs := c.revocationAuxSigs.Zero()

// Create the tlv stream.
tlvStream, err := tlv.NewStream(
Expand All @@ -343,6 +356,7 @@ func (c *openChannelTlvData) decode(r io.Reader) error {
blob.Record(),
c.confirmationHeight.Record(),
closeConfHeight.Record(),
revocationAuxSigs.Record(),
)
if err != nil {
return err
Expand All @@ -365,6 +379,9 @@ func (c *openChannelTlvData) decode(r io.Reader) error {
if _, ok := tlvs[closeConfHeight.TlvType()]; ok {
c.closeConfirmationHeight = tlv.SomeRecordT(closeConfHeight)
}
if _, ok := tlvs[revocationAuxSigs.TlvType()]; ok {
c.revocationAuxSigs = tlv.SomeRecordT(revocationAuxSigs)
}

return nil
}
Expand Down Expand Up @@ -630,6 +647,9 @@ func amendOpenChannelTlvData(channel *OpenChannel, auxData openChannelTlvData) {
auxData.closeConfirmationHeight.WhenSomeV(func(h uint32) {
channel.CloseConfirmationHeight = fn.Some(h)
})
auxData.revocationAuxSigs.WhenSomeV(func(sigs tlv.Blob) {
channel.RevocationAuxSigs = fn.Some(sigs)
})
}

// extractOpenChannelTlvData creates a new openChannelTlvData from the given
Expand Down Expand Up @@ -673,6 +693,11 @@ func extractOpenChannelTlvData(channel *OpenChannel) openChannelTlvData {
tlv.NewPrimitiveRecord[tlv.TlvType9](h),
)
})
channel.RevocationAuxSigs.WhenSome(func(sigs tlv.Blob) {
auxData.revocationAuxSigs = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType10](sigs),
)
})

return auxData
}
Expand Down
4 changes: 3 additions & 1 deletion channeldb/channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,9 @@ func TestChannelStateTransition(t *testing.T) {
},
}

_, err = channel.UpdateCommitment(&commitment, unsignedAckedUpdates)
_, err = channel.UpdateCommitment(
&commitment, unsignedAckedUpdates, fn.None[tlv.Blob](),
)
require.NoError(t, err, "unable to update commitment")

// Assert that update is correctly written to the database.
Expand Down
4 changes: 3 additions & 1 deletion channeldb/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import (
"github.com/btcsuite/btcd/btcutil/v2"
"github.com/btcsuite/btcd/chainhash/v2"
"github.com/btcsuite/btcd/wire/v2"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/shachain"
"github.com/lightningnetwork/lnd/tlv"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -382,7 +384,7 @@ func TestRestoreChannelShells(t *testing.T) {
// Ensure that it isn't possible to modify the commitment state machine
// of this restored channel.
channel := nodeChans[0]
_, err = channel.UpdateCommitment(nil, nil)
_, err = channel.UpdateCommitment(nil, nil, fn.None[tlv.Blob]())
if err != ErrNoRestoredChannelMutation {
t.Fatalf("able to mutate restored channel")
}
Expand Down
19 changes: 18 additions & 1 deletion chanstate/open_channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,15 @@ type OpenChannel struct {
// immutable.
CustomBlob fn.Option[tlv.Blob]

// RevocationAuxSigs is an optional blob carrying the revocation aux
// signatures attached to the most recently sent RevokeAndAck. It is
// only ever set for aux/custom (taproot asset) channels, and is
// persisted so the exact same signatures can be re-attached if that
// RevokeAndAck has to be retransmitted on channel reestablish. Only
// the latest revocation can ever be owed to the peer, so a single
// slot suffices; it is overwritten on every revocation.
RevocationAuxSigs fn.Option[tlv.Blob]

// Db persists channel state through the Store contract. This field
// intentionally keeps the existing name while callers still construct
// channels through the channeldb compatibility alias. The store
Expand Down Expand Up @@ -791,7 +800,8 @@ func (c *OpenChannel) SyncPending(addr net.Addr, pendingHeight uint32) error {
// commitment. Keys correspond to htlc indices and values indicate whether the
// htlc was settled or failed.
func (c *OpenChannel) UpdateCommitment(newCommitment *ChannelCommitment,
unsignedAckedUpdates []LogUpdate) (map[uint64]bool, error) {
unsignedAckedUpdates []LogUpdate,
revocationAuxSigs fn.Option[tlv.Blob]) (map[uint64]bool, error) {

c.Lock()
defer c.Unlock()
Expand All @@ -803,6 +813,13 @@ func (c *OpenChannel) UpdateCommitment(newCommitment *ChannelCommitment,
return nil, ErrNoRestoredChannelMutation
}

// Record the aux sigs attached to the RevokeAndAck this commitment
// update corresponds to (custom channels only, None otherwise), so
// they persist alongside it and survive for retransmission. This
// overwrites the previous revocation's sigs, which can no longer be
// owed to the peer.
c.RevocationAuxSigs = revocationAuxSigs

finalHtlcs, err := c.Db.UpdateChannelCommitment(
c, newCommitment, unsignedAckedUpdates,
)
Expand Down
Loading
Loading