Skip to content

Commit

Permalink
Merge pull request #9194 from lightningnetwork/aux-channel-htlc
Browse files Browse the repository at this point in the history
multi: generate and pass along HTLC resolution blobs for aux channels
  • Loading branch information
guggero authored Nov 15, 2024
2 parents e6efa2e + 9a1adbe commit 95b248a
Show file tree
Hide file tree
Showing 27 changed files with 728 additions and 141 deletions.
7 changes: 6 additions & 1 deletion contractcourt/breach_arbitrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ justiceTxBroadcast:
}

return aux.NotifyBroadcast(
&bumpReq, finalTx.justiceTx, finalTx.fee,
&bumpReq, finalTx.justiceTx, finalTx.fee, nil,
)
})
if err != nil {
Expand Down Expand Up @@ -1160,6 +1160,11 @@ func (bo *breachedOutput) SignDesc() *input.SignDescriptor {
return &bo.signDesc
}

// Preimage returns the preimage that was used to create the breached output.
func (bo *breachedOutput) Preimage() fn.Option[lntypes.Preimage] {
return fn.None[lntypes.Preimage]()
}

// CraftInputScript computes a valid witness that allows us to spend from the
// breached output. It does so by first generating and memoizing the witness
// generation function, which parameterized primarily by the witness type and
Expand Down
36 changes: 32 additions & 4 deletions contractcourt/briefcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,7 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
})
}

htlcBlobs := newAuxHtlcBlobs()
for _, htlc := range c.HtlcResolutions.IncomingHTLCs {
htlc := htlc

Expand All @@ -1581,8 +1582,9 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
continue
}

var resID resolverID
if htlc.SignedSuccessTx != nil {
resID := newResolverID(
resID = newResolverID(
htlc.SignedSuccessTx.TxIn[0].PreviousOutPoint,
)
//nolint:lll
Expand All @@ -1598,10 +1600,14 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = bridgeCtrlBlock
}
} else {
resID := newResolverID(htlc.ClaimOutpoint)
resID = newResolverID(htlc.ClaimOutpoint)
//nolint:lll
tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = ctrlBlock
}

htlc.ResolutionBlob.WhenSome(func(b []byte) {
htlcBlobs[resID] = b
})
}
for _, htlc := range c.HtlcResolutions.OutgoingHTLCs {
htlc := htlc
Expand All @@ -1613,8 +1619,9 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
continue
}

var resID resolverID
if htlc.SignedTimeoutTx != nil {
resID := newResolverID(
resID = newResolverID(
htlc.SignedTimeoutTx.TxIn[0].PreviousOutPoint,
)
//nolint:lll
Expand All @@ -1632,17 +1639,27 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = bridgeCtrlBlock
}
} else {
resID := newResolverID(htlc.ClaimOutpoint)
resID = newResolverID(htlc.ClaimOutpoint)
//nolint:lll
tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = ctrlBlock
}

htlc.ResolutionBlob.WhenSome(func(b []byte) {
htlcBlobs[resID] = b
})
}

if c.AnchorResolution != nil {
anchorSignDesc := c.AnchorResolution.AnchorSignDescriptor
tapCase.TapTweaks.Val.AnchorTweak = anchorSignDesc.TapTweak
}

if len(htlcBlobs) != 0 {
tapCase.HtlcBlobs = tlv.SomeRecordT(
tlv.NewRecordT[tlv.TlvType4](htlcBlobs),
)
}

return tapCase.Encode(w)
}

Expand All @@ -1661,6 +1678,8 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
})
}

htlcBlobs := tapCase.HtlcBlobs.ValOpt().UnwrapOr(newAuxHtlcBlobs())

for i := range c.HtlcResolutions.IncomingHTLCs {
htlc := c.HtlcResolutions.IncomingHTLCs[i]

Expand All @@ -1687,7 +1706,12 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
htlc.SweepSignDesc.ControlBlock = ctrlBlock
}

if htlcBlob, ok := htlcBlobs[resID]; ok {
htlc.ResolutionBlob = fn.Some(htlcBlob)
}

c.HtlcResolutions.IncomingHTLCs[i] = htlc

}
for i := range c.HtlcResolutions.OutgoingHTLCs {
htlc := c.HtlcResolutions.OutgoingHTLCs[i]
Expand Down Expand Up @@ -1715,6 +1739,10 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
htlc.SweepSignDesc.ControlBlock = ctrlBlock
}

if htlcBlob, ok := htlcBlobs[resID]; ok {
htlc.ResolutionBlob = fn.Some(htlcBlob)
}

c.HtlcResolutions.OutgoingHTLCs[i] = htlc
}

Expand Down
1 change: 1 addition & 0 deletions contractcourt/chain_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ func (c *chainWatcher) handleUnknownLocalState(
return s.FetchLeavesFromCommit(
lnwallet.NewAuxChanState(c.cfg.chanState),
c.cfg.chanState.LocalCommitment, *commitKeyRing,
lntypes.Local,
)
},
).Unpack()
Expand Down
11 changes: 0 additions & 11 deletions contractcourt/htlc_incoming_contest_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,6 @@ func (h *htlcIncomingContestResolver) Resolve(
return nil, nil
}

// If the HTLC has custom records, then for now we'll pause resolution.
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
}
}

// First try to parse the payload. If that fails, we can stop resolution
// now.
payload, nextHopOnionBlob, err := h.decodePayload()
Expand Down
12 changes: 9 additions & 3 deletions contractcourt/htlc_lease_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/tlv"
)

// htlcLeaseResolver is a struct that houses the lease specific HTLC resolution
Expand Down Expand Up @@ -52,8 +54,8 @@ func (h *htlcLeaseResolver) deriveWaitHeight(csvDelay uint32,
// send to the sweeper so the output can ultimately be swept.
func (h *htlcLeaseResolver) makeSweepInput(op *wire.OutPoint,
wType, cltvWtype input.StandardWitnessType,
signDesc *input.SignDescriptor,
csvDelay, broadcastHeight uint32, payHash [32]byte) *input.BaseInput {
signDesc *input.SignDescriptor, csvDelay, broadcastHeight uint32,
payHash [32]byte, resBlob fn.Option[tlv.Blob]) *input.BaseInput {

if h.hasCLTV() {
log.Infof("%T(%x): CSV and CLTV locks expired, offering "+
Expand All @@ -63,13 +65,17 @@ func (h *htlcLeaseResolver) makeSweepInput(op *wire.OutPoint,
op, cltvWtype, signDesc,
broadcastHeight, csvDelay,
h.leaseExpiry,
input.WithResolutionBlob(resBlob),
)
}

log.Infof("%T(%x): CSV lock expired, offering second-layer output to "+
"sweeper: %v", h, payHash, op)

return input.NewCsvInput(op, wType, signDesc, broadcastHeight, csvDelay)
return input.NewCsvInput(
op, wType, signDesc, broadcastHeight, csvDelay,
input.WithResolutionBlob(resBlob),
)
}

// SupplementState allows the user of a ContractResolver to supplement it with
Expand Down
11 changes: 0 additions & 11 deletions contractcourt/htlc_outgoing_contest_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,6 @@ func (h *htlcOutgoingContestResolver) Resolve(
return nil, nil
}

// If the HTLC has custom records, then for now we'll pause resolution.
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
}
}

// Otherwise, we'll watch for two external signals to decide if we'll
// morph into another resolver, or fully resolve the contract.
//
Expand Down
19 changes: 7 additions & 12 deletions contractcourt/htlc_success_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,6 @@ func (h *htlcSuccessResolver) Resolve(
return nil, nil
}

// If the HTLC has custom records, then for now we'll pause resolution.
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
}
}

// If we don't have a success transaction, then this means that this is
// an output on the remote party's commitment transaction.
if h.htlcResolution.SignedSuccessTx == nil {
Expand Down Expand Up @@ -258,6 +247,9 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx(immediate bool) (
h.htlcResolution.SignedSuccessTx,
h.htlcResolution.SignDetails, h.htlcResolution.Preimage,
h.broadcastHeight,
input.WithResolutionBlob(
h.htlcResolution.ResolutionBlob,
),
)
} else {
//nolint:lll
Expand Down Expand Up @@ -414,7 +406,7 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx(immediate bool) (
input.LeaseHtlcAcceptedSuccessSecondLevel,
&h.htlcResolution.SweepSignDesc,
h.htlcResolution.CsvDelay, uint32(commitSpend.SpendingHeight),
h.htlc.RHash,
h.htlc.RHash, h.htlcResolution.ResolutionBlob,
)

// Calculate the budget for this sweep.
Expand Down Expand Up @@ -470,6 +462,9 @@ func (h *htlcSuccessResolver) resolveRemoteCommitOutput(immediate bool) (
h.htlcResolution.Preimage[:],
h.broadcastHeight,
h.htlcResolution.CsvDelay,
input.WithResolutionBlob(
h.htlcResolution.ResolutionBlob,
),
))
} else {
inp = lnutils.Ptr(input.MakeHtlcSucceedInput(
Expand Down
16 changes: 5 additions & 11 deletions contractcourt/htlc_timeout_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,17 +426,6 @@ func (h *htlcTimeoutResolver) Resolve(
return nil, nil
}

// If the HTLC has custom records, then for now we'll pause resolution.
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
}
}

// Start by spending the HTLC output, either by broadcasting the
// second-level timeout transaction, or directly if this is the remote
// commitment.
Expand Down Expand Up @@ -499,6 +488,9 @@ func (h *htlcTimeoutResolver) sweepSecondLevelTx(immediate bool) error {
h.htlcResolution.SignedTimeoutTx,
h.htlcResolution.SignDetails,
h.broadcastHeight,
input.WithResolutionBlob(
h.htlcResolution.ResolutionBlob,
),
))
} else {
inp = lnutils.Ptr(input.MakeHtlcSecondLevelTimeoutAnchorInput(
Expand Down Expand Up @@ -592,6 +584,7 @@ func (h *htlcTimeoutResolver) sweepDirectHtlcOutput(immediate bool) error {
&h.htlcResolution.ClaimOutpoint, htlcWitnessType,
&h.htlcResolution.SweepSignDesc, h.broadcastHeight,
h.htlcResolution.CsvDelay, h.htlcResolution.Expiry,
input.WithResolutionBlob(h.htlcResolution.ResolutionBlob),
)

// Calculate the budget.
Expand Down Expand Up @@ -846,6 +839,7 @@ func (h *htlcTimeoutResolver) handleCommitSpend(
&h.htlcResolution.SweepSignDesc,
h.htlcResolution.CsvDelay,
uint32(commitSpend.SpendingHeight), h.htlc.RHash,
h.htlcResolution.ResolutionBlob,
)

// Calculate the budget for this sweep.
Expand Down
Loading

0 comments on commit 95b248a

Please sign in to comment.