mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
Merge pull request #9194 from lightningnetwork/aux-channel-htlc
multi: generate and pass along HTLC resolution blobs for aux channels
This commit is contained in:
commit
95b248a1ef
@ -752,7 +752,7 @@ justiceTxBroadcast:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return aux.NotifyBroadcast(
|
return aux.NotifyBroadcast(
|
||||||
&bumpReq, finalTx.justiceTx, finalTx.fee,
|
&bumpReq, finalTx.justiceTx, finalTx.fee, nil,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1160,6 +1160,11 @@ func (bo *breachedOutput) SignDesc() *input.SignDescriptor {
|
|||||||
return &bo.signDesc
|
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
|
// CraftInputScript computes a valid witness that allows us to spend from the
|
||||||
// breached output. It does so by first generating and memoizing the witness
|
// breached output. It does so by first generating and memoizing the witness
|
||||||
// generation function, which parameterized primarily by the witness type and
|
// generation function, which parameterized primarily by the witness type and
|
||||||
|
@ -1571,6 +1571,7 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
htlcBlobs := newAuxHtlcBlobs()
|
||||||
for _, htlc := range c.HtlcResolutions.IncomingHTLCs {
|
for _, htlc := range c.HtlcResolutions.IncomingHTLCs {
|
||||||
htlc := htlc
|
htlc := htlc
|
||||||
|
|
||||||
@ -1581,8 +1582,9 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var resID resolverID
|
||||||
if htlc.SignedSuccessTx != nil {
|
if htlc.SignedSuccessTx != nil {
|
||||||
resID := newResolverID(
|
resID = newResolverID(
|
||||||
htlc.SignedSuccessTx.TxIn[0].PreviousOutPoint,
|
htlc.SignedSuccessTx.TxIn[0].PreviousOutPoint,
|
||||||
)
|
)
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
@ -1598,10 +1600,14 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
|
|||||||
tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = bridgeCtrlBlock
|
tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = bridgeCtrlBlock
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resID := newResolverID(htlc.ClaimOutpoint)
|
resID = newResolverID(htlc.ClaimOutpoint)
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = ctrlBlock
|
tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = ctrlBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
htlc.ResolutionBlob.WhenSome(func(b []byte) {
|
||||||
|
htlcBlobs[resID] = b
|
||||||
|
})
|
||||||
}
|
}
|
||||||
for _, htlc := range c.HtlcResolutions.OutgoingHTLCs {
|
for _, htlc := range c.HtlcResolutions.OutgoingHTLCs {
|
||||||
htlc := htlc
|
htlc := htlc
|
||||||
@ -1613,8 +1619,9 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var resID resolverID
|
||||||
if htlc.SignedTimeoutTx != nil {
|
if htlc.SignedTimeoutTx != nil {
|
||||||
resID := newResolverID(
|
resID = newResolverID(
|
||||||
htlc.SignedTimeoutTx.TxIn[0].PreviousOutPoint,
|
htlc.SignedTimeoutTx.TxIn[0].PreviousOutPoint,
|
||||||
)
|
)
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
@ -1632,10 +1639,14 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
|
|||||||
tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = bridgeCtrlBlock
|
tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = bridgeCtrlBlock
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resID := newResolverID(htlc.ClaimOutpoint)
|
resID = newResolverID(htlc.ClaimOutpoint)
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = ctrlBlock
|
tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = ctrlBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
htlc.ResolutionBlob.WhenSome(func(b []byte) {
|
||||||
|
htlcBlobs[resID] = b
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.AnchorResolution != nil {
|
if c.AnchorResolution != nil {
|
||||||
@ -1643,6 +1654,12 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
|
|||||||
tapCase.TapTweaks.Val.AnchorTweak = anchorSignDesc.TapTweak
|
tapCase.TapTweaks.Val.AnchorTweak = anchorSignDesc.TapTweak
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(htlcBlobs) != 0 {
|
||||||
|
tapCase.HtlcBlobs = tlv.SomeRecordT(
|
||||||
|
tlv.NewRecordT[tlv.TlvType4](htlcBlobs),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return tapCase.Encode(w)
|
return tapCase.Encode(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1661,6 +1678,8 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
htlcBlobs := tapCase.HtlcBlobs.ValOpt().UnwrapOr(newAuxHtlcBlobs())
|
||||||
|
|
||||||
for i := range c.HtlcResolutions.IncomingHTLCs {
|
for i := range c.HtlcResolutions.IncomingHTLCs {
|
||||||
htlc := c.HtlcResolutions.IncomingHTLCs[i]
|
htlc := c.HtlcResolutions.IncomingHTLCs[i]
|
||||||
|
|
||||||
@ -1687,7 +1706,12 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
|
|||||||
htlc.SweepSignDesc.ControlBlock = ctrlBlock
|
htlc.SweepSignDesc.ControlBlock = ctrlBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if htlcBlob, ok := htlcBlobs[resID]; ok {
|
||||||
|
htlc.ResolutionBlob = fn.Some(htlcBlob)
|
||||||
|
}
|
||||||
|
|
||||||
c.HtlcResolutions.IncomingHTLCs[i] = htlc
|
c.HtlcResolutions.IncomingHTLCs[i] = htlc
|
||||||
|
|
||||||
}
|
}
|
||||||
for i := range c.HtlcResolutions.OutgoingHTLCs {
|
for i := range c.HtlcResolutions.OutgoingHTLCs {
|
||||||
htlc := c.HtlcResolutions.OutgoingHTLCs[i]
|
htlc := c.HtlcResolutions.OutgoingHTLCs[i]
|
||||||
@ -1715,6 +1739,10 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
|
|||||||
htlc.SweepSignDesc.ControlBlock = ctrlBlock
|
htlc.SweepSignDesc.ControlBlock = ctrlBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if htlcBlob, ok := htlcBlobs[resID]; ok {
|
||||||
|
htlc.ResolutionBlob = fn.Some(htlcBlob)
|
||||||
|
}
|
||||||
|
|
||||||
c.HtlcResolutions.OutgoingHTLCs[i] = htlc
|
c.HtlcResolutions.OutgoingHTLCs[i] = htlc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,6 +436,7 @@ func (c *chainWatcher) handleUnknownLocalState(
|
|||||||
return s.FetchLeavesFromCommit(
|
return s.FetchLeavesFromCommit(
|
||||||
lnwallet.NewAuxChanState(c.cfg.chanState),
|
lnwallet.NewAuxChanState(c.cfg.chanState),
|
||||||
c.cfg.chanState.LocalCommitment, *commitKeyRing,
|
c.cfg.chanState.LocalCommitment, *commitKeyRing,
|
||||||
|
lntypes.Local,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
).Unpack()
|
).Unpack()
|
||||||
|
@ -99,17 +99,6 @@ func (h *htlcIncomingContestResolver) Resolve(
|
|||||||
return nil, nil
|
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
|
// First try to parse the payload. If that fails, we can stop resolution
|
||||||
// now.
|
// now.
|
||||||
payload, nextHopOnionBlob, err := h.decodePayload()
|
payload, nextHopOnionBlob, err := h.decodePayload()
|
||||||
|
@ -6,7 +6,9 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// htlcLeaseResolver is a struct that houses the lease specific HTLC resolution
|
// htlcLeaseResolver is a struct that houses the lease specific HTLC resolution
|
||||||
@ -52,8 +54,8 @@ func (h *htlcLeaseResolver) deriveWaitHeight(csvDelay uint32,
|
|||||||
// send to the sweeper so the output can ultimately be swept.
|
// send to the sweeper so the output can ultimately be swept.
|
||||||
func (h *htlcLeaseResolver) makeSweepInput(op *wire.OutPoint,
|
func (h *htlcLeaseResolver) makeSweepInput(op *wire.OutPoint,
|
||||||
wType, cltvWtype input.StandardWitnessType,
|
wType, cltvWtype input.StandardWitnessType,
|
||||||
signDesc *input.SignDescriptor,
|
signDesc *input.SignDescriptor, csvDelay, broadcastHeight uint32,
|
||||||
csvDelay, broadcastHeight uint32, payHash [32]byte) *input.BaseInput {
|
payHash [32]byte, resBlob fn.Option[tlv.Blob]) *input.BaseInput {
|
||||||
|
|
||||||
if h.hasCLTV() {
|
if h.hasCLTV() {
|
||||||
log.Infof("%T(%x): CSV and CLTV locks expired, offering "+
|
log.Infof("%T(%x): CSV and CLTV locks expired, offering "+
|
||||||
@ -63,13 +65,17 @@ func (h *htlcLeaseResolver) makeSweepInput(op *wire.OutPoint,
|
|||||||
op, cltvWtype, signDesc,
|
op, cltvWtype, signDesc,
|
||||||
broadcastHeight, csvDelay,
|
broadcastHeight, csvDelay,
|
||||||
h.leaseExpiry,
|
h.leaseExpiry,
|
||||||
|
input.WithResolutionBlob(resBlob),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("%T(%x): CSV lock expired, offering second-layer output to "+
|
log.Infof("%T(%x): CSV lock expired, offering second-layer output to "+
|
||||||
"sweeper: %v", h, payHash, op)
|
"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
|
// SupplementState allows the user of a ContractResolver to supplement it with
|
||||||
|
@ -58,17 +58,6 @@ func (h *htlcOutgoingContestResolver) Resolve(
|
|||||||
return nil, nil
|
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
|
// Otherwise, we'll watch for two external signals to decide if we'll
|
||||||
// morph into another resolver, or fully resolve the contract.
|
// morph into another resolver, or fully resolve the contract.
|
||||||
//
|
//
|
||||||
|
@ -123,17 +123,6 @@ func (h *htlcSuccessResolver) Resolve(
|
|||||||
return nil, nil
|
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
|
// If we don't have a success transaction, then this means that this is
|
||||||
// an output on the remote party's commitment transaction.
|
// an output on the remote party's commitment transaction.
|
||||||
if h.htlcResolution.SignedSuccessTx == nil {
|
if h.htlcResolution.SignedSuccessTx == nil {
|
||||||
@ -258,6 +247,9 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx(immediate bool) (
|
|||||||
h.htlcResolution.SignedSuccessTx,
|
h.htlcResolution.SignedSuccessTx,
|
||||||
h.htlcResolution.SignDetails, h.htlcResolution.Preimage,
|
h.htlcResolution.SignDetails, h.htlcResolution.Preimage,
|
||||||
h.broadcastHeight,
|
h.broadcastHeight,
|
||||||
|
input.WithResolutionBlob(
|
||||||
|
h.htlcResolution.ResolutionBlob,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
@ -414,7 +406,7 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx(immediate bool) (
|
|||||||
input.LeaseHtlcAcceptedSuccessSecondLevel,
|
input.LeaseHtlcAcceptedSuccessSecondLevel,
|
||||||
&h.htlcResolution.SweepSignDesc,
|
&h.htlcResolution.SweepSignDesc,
|
||||||
h.htlcResolution.CsvDelay, uint32(commitSpend.SpendingHeight),
|
h.htlcResolution.CsvDelay, uint32(commitSpend.SpendingHeight),
|
||||||
h.htlc.RHash,
|
h.htlc.RHash, h.htlcResolution.ResolutionBlob,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Calculate the budget for this sweep.
|
// Calculate the budget for this sweep.
|
||||||
@ -470,6 +462,9 @@ func (h *htlcSuccessResolver) resolveRemoteCommitOutput(immediate bool) (
|
|||||||
h.htlcResolution.Preimage[:],
|
h.htlcResolution.Preimage[:],
|
||||||
h.broadcastHeight,
|
h.broadcastHeight,
|
||||||
h.htlcResolution.CsvDelay,
|
h.htlcResolution.CsvDelay,
|
||||||
|
input.WithResolutionBlob(
|
||||||
|
h.htlcResolution.ResolutionBlob,
|
||||||
|
),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
inp = lnutils.Ptr(input.MakeHtlcSucceedInput(
|
inp = lnutils.Ptr(input.MakeHtlcSucceedInput(
|
||||||
|
@ -426,17 +426,6 @@ func (h *htlcTimeoutResolver) Resolve(
|
|||||||
return nil, nil
|
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
|
// Start by spending the HTLC output, either by broadcasting the
|
||||||
// second-level timeout transaction, or directly if this is the remote
|
// second-level timeout transaction, or directly if this is the remote
|
||||||
// commitment.
|
// commitment.
|
||||||
@ -499,6 +488,9 @@ func (h *htlcTimeoutResolver) sweepSecondLevelTx(immediate bool) error {
|
|||||||
h.htlcResolution.SignedTimeoutTx,
|
h.htlcResolution.SignedTimeoutTx,
|
||||||
h.htlcResolution.SignDetails,
|
h.htlcResolution.SignDetails,
|
||||||
h.broadcastHeight,
|
h.broadcastHeight,
|
||||||
|
input.WithResolutionBlob(
|
||||||
|
h.htlcResolution.ResolutionBlob,
|
||||||
|
),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
inp = lnutils.Ptr(input.MakeHtlcSecondLevelTimeoutAnchorInput(
|
inp = lnutils.Ptr(input.MakeHtlcSecondLevelTimeoutAnchorInput(
|
||||||
@ -592,6 +584,7 @@ func (h *htlcTimeoutResolver) sweepDirectHtlcOutput(immediate bool) error {
|
|||||||
&h.htlcResolution.ClaimOutpoint, htlcWitnessType,
|
&h.htlcResolution.ClaimOutpoint, htlcWitnessType,
|
||||||
&h.htlcResolution.SweepSignDesc, h.broadcastHeight,
|
&h.htlcResolution.SweepSignDesc, h.broadcastHeight,
|
||||||
h.htlcResolution.CsvDelay, h.htlcResolution.Expiry,
|
h.htlcResolution.CsvDelay, h.htlcResolution.Expiry,
|
||||||
|
input.WithResolutionBlob(h.htlcResolution.ResolutionBlob),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Calculate the budget.
|
// Calculate the budget.
|
||||||
@ -846,6 +839,7 @@ func (h *htlcTimeoutResolver) handleCommitSpend(
|
|||||||
&h.htlcResolution.SweepSignDesc,
|
&h.htlcResolution.SweepSignDesc,
|
||||||
h.htlcResolution.CsvDelay,
|
h.htlcResolution.CsvDelay,
|
||||||
uint32(commitSpend.SpendingHeight), h.htlc.RHash,
|
uint32(commitSpend.SpendingHeight), h.htlc.RHash,
|
||||||
|
h.htlcResolution.ResolutionBlob,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Calculate the budget for this sweep.
|
// Calculate the budget for this sweep.
|
||||||
|
@ -39,7 +39,9 @@ type taprootBriefcase struct {
|
|||||||
// used to sweep a remote party's breached output.
|
// used to sweep a remote party's breached output.
|
||||||
BreachedCommitBlob tlv.OptionalRecordT[tlv.TlvType3, tlv.Blob]
|
BreachedCommitBlob tlv.OptionalRecordT[tlv.TlvType3, tlv.Blob]
|
||||||
|
|
||||||
// TODO(roasbeef): htlc blobs
|
// HtlcBlobs is an optikonal record that contains the opaque blobs for
|
||||||
|
// the set of active HTLCs on the commitment transaction.
|
||||||
|
HtlcBlobs tlv.OptionalRecordT[tlv.TlvType4, htlcAuxBlobs]
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(roasbeef): morph into new tlv record
|
// TODO(roasbeef): morph into new tlv record
|
||||||
@ -70,6 +72,9 @@ func (t *taprootBriefcase) EncodeRecords() []tlv.Record {
|
|||||||
records = append(records, r.Record())
|
records = append(records, r.Record())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
t.HtlcBlobs.WhenSome(func(r tlv.RecordT[tlv.TlvType4, htlcAuxBlobs]) {
|
||||||
|
records = append(records, r.Record())
|
||||||
|
})
|
||||||
|
|
||||||
return records
|
return records
|
||||||
}
|
}
|
||||||
@ -96,10 +101,11 @@ func (t *taprootBriefcase) Encode(w io.Writer) error {
|
|||||||
func (t *taprootBriefcase) Decode(r io.Reader) error {
|
func (t *taprootBriefcase) Decode(r io.Reader) error {
|
||||||
settledCommitBlob := t.SettledCommitBlob.Zero()
|
settledCommitBlob := t.SettledCommitBlob.Zero()
|
||||||
breachedCommitBlob := t.BreachedCommitBlob.Zero()
|
breachedCommitBlob := t.BreachedCommitBlob.Zero()
|
||||||
|
htlcBlobs := t.HtlcBlobs.Zero()
|
||||||
|
|
||||||
records := append(
|
records := append(
|
||||||
t.DecodeRecords(),
|
t.DecodeRecords(), settledCommitBlob.Record(),
|
||||||
settledCommitBlob.Record(),
|
breachedCommitBlob.Record(), htlcBlobs.Record(),
|
||||||
breachedCommitBlob.Record(),
|
|
||||||
)
|
)
|
||||||
stream, err := tlv.NewStream(records...)
|
stream, err := tlv.NewStream(records...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -117,6 +123,9 @@ func (t *taprootBriefcase) Decode(r io.Reader) error {
|
|||||||
if v, ok := typeMap[t.BreachedCommitBlob.TlvType()]; ok && v == nil {
|
if v, ok := typeMap[t.BreachedCommitBlob.TlvType()]; ok && v == nil {
|
||||||
t.BreachedCommitBlob = tlv.SomeRecordT(breachedCommitBlob)
|
t.BreachedCommitBlob = tlv.SomeRecordT(breachedCommitBlob)
|
||||||
}
|
}
|
||||||
|
if v, ok := typeMap[t.HtlcBlobs.TlvType()]; ok && v == nil {
|
||||||
|
t.HtlcBlobs = tlv.SomeRecordT(htlcBlobs)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -686,3 +695,110 @@ func (t *tapTweaks) Decode(r io.Reader) error {
|
|||||||
|
|
||||||
return stream.Decode(r)
|
return stream.Decode(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// htlcAuxBlobs is a map of resolver IDs to their corresponding HTLC blobs.
|
||||||
|
// This is used to store the resolution blobs for HTLCs that are not yet
|
||||||
|
// resolved.
|
||||||
|
type htlcAuxBlobs map[resolverID]tlv.Blob
|
||||||
|
|
||||||
|
// newAuxHtlcBlobs returns a new instance of the htlcAuxBlobs struct.
|
||||||
|
func newAuxHtlcBlobs() htlcAuxBlobs {
|
||||||
|
return make(htlcAuxBlobs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode encodes the set of HTLC blobs into the target writer.
|
||||||
|
func (h *htlcAuxBlobs) Encode(w io.Writer) error {
|
||||||
|
var buf [8]byte
|
||||||
|
|
||||||
|
numBlobs := uint64(len(*h))
|
||||||
|
if err := tlv.WriteVarInt(w, numBlobs, &buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, blob := range *h {
|
||||||
|
if _, err := w.Write(id[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := varBytesEncoder(w, &blob, &buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decodes the set of HTLC blobs from the target reader.
|
||||||
|
func (h *htlcAuxBlobs) Decode(r io.Reader) error {
|
||||||
|
var buf [8]byte
|
||||||
|
|
||||||
|
numBlobs, err := tlv.ReadVarInt(r, &buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := uint64(0); i < numBlobs; i++ {
|
||||||
|
var id resolverID
|
||||||
|
if _, err := io.ReadFull(r, id[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var blob tlv.Blob
|
||||||
|
if err := varBytesDecoder(r, &blob, &buf, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
(*h)[id] = blob
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// eHtlcAuxBlobsEncoder is a custom TLV encoder for the htlcAuxBlobs struct.
|
||||||
|
func htlcAuxBlobsEncoder(w io.Writer, val any, _ *[8]byte) error {
|
||||||
|
if t, ok := val.(*htlcAuxBlobs); ok {
|
||||||
|
return (*t).Encode(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlv.NewTypeForEncodingErr(val, "htlcAuxBlobs")
|
||||||
|
}
|
||||||
|
|
||||||
|
// dHtlcAuxBlobsDecoder is a custom TLV decoder for the htlcAuxBlobs struct.
|
||||||
|
func htlcAuxBlobsDecoder(r io.Reader, val any, _ *[8]byte,
|
||||||
|
l uint64) error {
|
||||||
|
|
||||||
|
if typ, ok := val.(*htlcAuxBlobs); ok {
|
||||||
|
blobReader := io.LimitReader(r, int64(l))
|
||||||
|
|
||||||
|
htlcBlobs := newAuxHtlcBlobs()
|
||||||
|
err := htlcBlobs.Decode(blobReader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*typ = htlcBlobs
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlv.NewTypeForDecodingErr(val, "htlcAuxBlobs", l, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record returns a tlv.Record for the htlcAuxBlobs struct.
|
||||||
|
func (h *htlcAuxBlobs) Record() tlv.Record {
|
||||||
|
recordSize := func() uint64 {
|
||||||
|
var (
|
||||||
|
b bytes.Buffer
|
||||||
|
buf [8]byte
|
||||||
|
)
|
||||||
|
if err := htlcAuxBlobsEncoder(&b, h, &buf); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint64(len(b.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlv.MakeDynamicRecord(
|
||||||
|
0, h, recordSize, htlcAuxBlobsEncoder, htlcAuxBlobsDecoder,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"pgregory.net/rapid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func randResolverCtrlBlocks(t *testing.T) resolverCtrlBlocks {
|
func randResolverCtrlBlocks(t *testing.T) resolverCtrlBlocks {
|
||||||
@ -53,6 +54,25 @@ func randHtlcTweaks(t *testing.T) htlcTapTweaks {
|
|||||||
return tweaks
|
return tweaks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func randHtlcAuxBlobs(t *testing.T) htlcAuxBlobs {
|
||||||
|
numBlobs := rand.Int() % 256
|
||||||
|
blobs := make(htlcAuxBlobs, numBlobs)
|
||||||
|
|
||||||
|
for i := 0; i < numBlobs; i++ {
|
||||||
|
var id resolverID
|
||||||
|
_, err := rand.Read(id[:])
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var blob [100]byte
|
||||||
|
_, err = rand.Read(blob[:])
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
blobs[id] = blob[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return blobs
|
||||||
|
}
|
||||||
|
|
||||||
// TestTaprootBriefcase tests the encode/decode methods of the taproot
|
// TestTaprootBriefcase tests the encode/decode methods of the taproot
|
||||||
// briefcase extension.
|
// briefcase extension.
|
||||||
func TestTaprootBriefcase(t *testing.T) {
|
func TestTaprootBriefcase(t *testing.T) {
|
||||||
@ -93,6 +113,9 @@ func TestTaprootBriefcase(t *testing.T) {
|
|||||||
BreachedCommitBlob: tlv.SomeRecordT(
|
BreachedCommitBlob: tlv.SomeRecordT(
|
||||||
tlv.NewPrimitiveRecord[tlv.TlvType3](commitBlob[:]),
|
tlv.NewPrimitiveRecord[tlv.TlvType3](commitBlob[:]),
|
||||||
),
|
),
|
||||||
|
HtlcBlobs: tlv.SomeRecordT(
|
||||||
|
tlv.NewRecordT[tlv.TlvType4](randHtlcAuxBlobs(t)),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
@ -103,3 +126,21 @@ func TestTaprootBriefcase(t *testing.T) {
|
|||||||
|
|
||||||
require.Equal(t, testCase, &decodedCase)
|
require.Equal(t, testCase, &decodedCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestHtlcAuxBlobEncodeDecode tests the encode/decode methods of the HTLC aux
|
||||||
|
// blobs.
|
||||||
|
func TestHtlcAuxBlobEncodeDecode(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
rapid.Check(t, func(t *rapid.T) {
|
||||||
|
htlcBlobs := rapid.Make[htlcAuxBlobs]().Draw(t, "htlcAuxBlobs")
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
require.NoError(t, htlcBlobs.Encode(&b))
|
||||||
|
|
||||||
|
decodedBlobs := newAuxHtlcBlobs()
|
||||||
|
require.NoError(t, decodedBlobs.Decode(&b))
|
||||||
|
|
||||||
|
require.Equal(t, htlcBlobs, decodedBlobs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
# 2024/09/02 14:02:53.354676 [TestHtlcAuxBlobEncodeDecode] [rapid] draw htlcAuxBlobs: contractcourt.htlcAuxBlobs{contractcourt.resolverID{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}:[]uint8{}}
|
||||||
|
#
|
||||||
|
v0.4.8#15807814492030881602
|
||||||
|
0x5555555555555
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
||||||
|
0x0
|
@ -18,11 +18,23 @@
|
|||||||
- [Tooling and Documentation](#tooling-and-documentation)
|
- [Tooling and Documentation](#tooling-and-documentation)
|
||||||
|
|
||||||
# Bug Fixes
|
# Bug Fixes
|
||||||
|
|
||||||
|
* [Fix a bug](https://github.com/lightningnetwork/lnd/pull/9134) that would
|
||||||
|
cause a nil pointer dereference during the probing of a payment request that
|
||||||
|
does not contain a payment address.
|
||||||
|
|
||||||
# New Features
|
# New Features
|
||||||
|
|
||||||
The main channel state machine and database now allow for processing and storing
|
The main channel state machine and database now allow for processing and storing
|
||||||
custom Taproot script leaves, [allowing the implementation of custom channel
|
custom Taproot script leaves, allowing the implementation of custom channel
|
||||||
types](https://github.com/lightningnetwork/lnd/pull/8960).
|
types in a series of changes:
|
||||||
|
* https://github.com/lightningnetwork/lnd/pull/9025
|
||||||
|
* https://github.com/lightningnetwork/lnd/pull/9030
|
||||||
|
* https://github.com/lightningnetwork/lnd/pull/9049
|
||||||
|
* https://github.com/lightningnetwork/lnd/pull/9072
|
||||||
|
* https://github.com/lightningnetwork/lnd/pull/9095
|
||||||
|
* https://github.com/lightningnetwork/lnd/pull/8960
|
||||||
|
* https://github.com/lightningnetwork/lnd/pull/9194
|
||||||
|
|
||||||
## Functional Enhancements
|
## Functional Enhancements
|
||||||
|
|
||||||
@ -82,6 +94,10 @@ types](https://github.com/lightningnetwork/lnd/pull/8960).
|
|||||||
## Breaking Changes
|
## Breaking Changes
|
||||||
## Performance Improvements
|
## Performance Improvements
|
||||||
|
|
||||||
|
* [A new method](https://github.com/lightningnetwork/lnd/pull/9195)
|
||||||
|
`AssertTxnsNotInMempool` has been added to `lntest` package to allow batch
|
||||||
|
exclusion check in itest.
|
||||||
|
|
||||||
# Technical and Architectural Updates
|
# Technical and Architectural Updates
|
||||||
## BOLT Spec Updates
|
## BOLT Spec Updates
|
||||||
|
|
||||||
|
@ -23,10 +23,6 @@
|
|||||||
propagate mission control and debug level config values to the main LND config
|
propagate mission control and debug level config values to the main LND config
|
||||||
struct so that the GetDebugInfo response is accurate.
|
struct so that the GetDebugInfo response is accurate.
|
||||||
|
|
||||||
* [Fix a bug](https://github.com/lightningnetwork/lnd/pull/9134) that would
|
|
||||||
cause a nil pointer dereference during the probing of a payment request that
|
|
||||||
does not contain a payment address.
|
|
||||||
|
|
||||||
* [Fixed a bug](https://github.com/lightningnetwork/lnd/pull/9033) where we
|
* [Fixed a bug](https://github.com/lightningnetwork/lnd/pull/9033) where we
|
||||||
would not signal an error when trying to bump a non-anchor channel but
|
would not signal an error when trying to bump a non-anchor channel but
|
||||||
instead report a successful cpfp registration although no fee bumping is
|
instead report a successful cpfp registration although no fee bumping is
|
||||||
@ -163,10 +159,6 @@ The underlying functionality between those two options remain the same.
|
|||||||
|
|
||||||
* Log rotation can now use ZSTD
|
* Log rotation can now use ZSTD
|
||||||
|
|
||||||
* [A new method](https://github.com/lightningnetwork/lnd/pull/9195)
|
|
||||||
`AssertTxnsNotInMempool` has been added to `lntest` package to allow batch
|
|
||||||
exclusion check in itest.
|
|
||||||
|
|
||||||
# Technical and Architectural Updates
|
# Technical and Architectural Updates
|
||||||
## BOLT Spec Updates
|
## BOLT Spec Updates
|
||||||
|
|
||||||
|
1
go.mod
1
go.mod
@ -64,6 +64,7 @@ require (
|
|||||||
google.golang.org/protobuf v1.33.0
|
google.golang.org/protobuf v1.33.0
|
||||||
gopkg.in/macaroon-bakery.v2 v2.0.1
|
gopkg.in/macaroon-bakery.v2 v2.0.1
|
||||||
gopkg.in/macaroon.v2 v2.0.0
|
gopkg.in/macaroon.v2 v2.0.0
|
||||||
|
pgregory.net/rapid v1.1.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
2
go.sum
2
go.sum
@ -1076,6 +1076,8 @@ modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
|||||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
|
||||||
|
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
@ -69,6 +69,9 @@ type Input interface {
|
|||||||
// ResolutionBlob returns a special opaque blob to be used to
|
// ResolutionBlob returns a special opaque blob to be used to
|
||||||
// sweep/resolve this input.
|
// sweep/resolve this input.
|
||||||
ResolutionBlob() fn.Option[tlv.Blob]
|
ResolutionBlob() fn.Option[tlv.Blob]
|
||||||
|
|
||||||
|
// Preimage returns the preimage for the input if it is an HTLC input.
|
||||||
|
Preimage() fn.Option[lntypes.Preimage]
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxInfo describes properties of a parent tx that are relevant for CPFP.
|
// TxInfo describes properties of a parent tx that are relevant for CPFP.
|
||||||
@ -285,6 +288,11 @@ func (bi *BaseInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
|
|||||||
return witnessFunc(txn, hashCache, txinIdx)
|
return witnessFunc(txn, hashCache, txinIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preimage returns the preimage for the input if it is an HTLC input.
|
||||||
|
func (bi *BaseInput) Preimage() fn.Option[lntypes.Preimage] {
|
||||||
|
return fn.None[lntypes.Preimage]()
|
||||||
|
}
|
||||||
|
|
||||||
// HtlcSucceedInput constitutes a sweep input that needs a pre-image. The input
|
// HtlcSucceedInput constitutes a sweep input that needs a pre-image. The input
|
||||||
// is expected to reside on the commitment tx of the remote party and should
|
// is expected to reside on the commitment tx of the remote party and should
|
||||||
// not be a second level tx output.
|
// not be a second level tx output.
|
||||||
@ -357,7 +365,6 @@ func (h *HtlcSucceedInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
desc.SignMethod = TaprootScriptSpendSignMethod
|
desc.SignMethod = TaprootScriptSpendSignMethod
|
||||||
|
|
||||||
witness, err = SenderHTLCScriptTaprootRedeem(
|
witness, err = SenderHTLCScriptTaprootRedeem(
|
||||||
signer, &desc, txn, h.preimage, nil, nil,
|
signer, &desc, txn, h.preimage, nil, nil,
|
||||||
)
|
)
|
||||||
@ -375,6 +382,15 @@ func (h *HtlcSucceedInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preimage returns the preimage for the input if it is an HTLC input.
|
||||||
|
func (h *HtlcSucceedInput) Preimage() fn.Option[lntypes.Preimage] {
|
||||||
|
if len(h.preimage) == 0 {
|
||||||
|
return fn.None[lntypes.Preimage]()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn.Some(lntypes.Preimage(h.preimage))
|
||||||
|
}
|
||||||
|
|
||||||
// HtlcSecondLevelAnchorInput is an input type used to spend HTLC outputs
|
// HtlcSecondLevelAnchorInput is an input type used to spend HTLC outputs
|
||||||
// using a re-signed second level transaction, either via the timeout or success
|
// using a re-signed second level transaction, either via the timeout or success
|
||||||
// paths.
|
// paths.
|
||||||
@ -391,6 +407,8 @@ type HtlcSecondLevelAnchorInput struct {
|
|||||||
hashCache *txscript.TxSigHashes,
|
hashCache *txscript.TxSigHashes,
|
||||||
prevOutputFetcher txscript.PrevOutputFetcher,
|
prevOutputFetcher txscript.PrevOutputFetcher,
|
||||||
txinIdx int) (wire.TxWitness, error)
|
txinIdx int) (wire.TxWitness, error)
|
||||||
|
|
||||||
|
preimage []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequiredTxOut returns the tx out needed to be present on the sweep tx for
|
// RequiredTxOut returns the tx out needed to be present on the sweep tx for
|
||||||
@ -427,6 +445,15 @@ func (i *HtlcSecondLevelAnchorInput) CraftInputScript(signer Signer,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preimage returns the preimage for the input if it is an HTLC input.
|
||||||
|
func (i *HtlcSecondLevelAnchorInput) Preimage() fn.Option[lntypes.Preimage] {
|
||||||
|
if len(i.preimage) == 0 {
|
||||||
|
return fn.None[lntypes.Preimage]()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn.Some(lntypes.Preimage(i.preimage))
|
||||||
|
}
|
||||||
|
|
||||||
// MakeHtlcSecondLevelTimeoutAnchorInput creates an input allowing the sweeper
|
// MakeHtlcSecondLevelTimeoutAnchorInput creates an input allowing the sweeper
|
||||||
// to spend the HTLC output on our commit using the second level timeout
|
// to spend the HTLC output on our commit using the second level timeout
|
||||||
// transaction.
|
// transaction.
|
||||||
@ -545,6 +572,7 @@ func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx,
|
|||||||
SignedTx: signedTx,
|
SignedTx: signedTx,
|
||||||
inputKit: input.inputKit,
|
inputKit: input.inputKit,
|
||||||
createWitness: createWitness,
|
createWitness: createWitness,
|
||||||
|
preimage: preimage[:],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,6 +616,7 @@ func MakeHtlcSecondLevelSuccessTaprootInput(signedTx *wire.MsgTx,
|
|||||||
inputKit: input.inputKit,
|
inputKit: input.inputKit,
|
||||||
SignedTx: signedTx,
|
SignedTx: signedTx,
|
||||||
createWitness: createWitness,
|
createWitness: createWitness,
|
||||||
|
preimage: preimage[:],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,6 +140,17 @@ func (m *MockInput) ResolutionBlob() fn.Option[tlv.Blob] {
|
|||||||
return info.(fn.Option[tlv.Blob])
|
return info.(fn.Option[tlv.Blob])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MockInput) Preimage() fn.Option[lntypes.Preimage] {
|
||||||
|
args := m.Called()
|
||||||
|
|
||||||
|
info := args.Get(0)
|
||||||
|
if info == nil {
|
||||||
|
return fn.None[lntypes.Preimage]()
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.(fn.Option[lntypes.Preimage])
|
||||||
|
}
|
||||||
|
|
||||||
// MockWitnessType implements the `WitnessType` interface and is used by other
|
// MockWitnessType implements the `WitnessType` interface and is used by other
|
||||||
// packages for mock testing.
|
// packages for mock testing.
|
||||||
type MockWitnessType struct {
|
type MockWitnessType struct {
|
||||||
|
@ -178,7 +178,8 @@ type AuxLeafStore interface {
|
|||||||
// commitment.
|
// commitment.
|
||||||
FetchLeavesFromCommit(chanState AuxChanState,
|
FetchLeavesFromCommit(chanState AuxChanState,
|
||||||
commit channeldb.ChannelCommitment,
|
commit channeldb.ChannelCommitment,
|
||||||
keyRing CommitmentKeyRing) fn.Result[CommitDiffAuxResult]
|
keyRing CommitmentKeyRing, whoseCommit lntypes.ChannelParty,
|
||||||
|
) fn.Result[CommitDiffAuxResult]
|
||||||
|
|
||||||
// FetchLeavesFromRevocation attempts to fetch the auxiliary leaves
|
// FetchLeavesFromRevocation attempts to fetch the auxiliary leaves
|
||||||
// from a channel revocation that stores balance + blob information.
|
// from a channel revocation that stores balance + blob information.
|
||||||
|
@ -3,6 +3,7 @@ package lnwallet
|
|||||||
import (
|
import (
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/fn"
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@ -24,6 +25,19 @@ const (
|
|||||||
Breach
|
Breach
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AuxSigDesc stores optional information related to 2nd level HTLCs for aux
|
||||||
|
// channels.
|
||||||
|
type AuxSigDesc struct {
|
||||||
|
// AuxSig is the second-level signature for the HTLC that we are trying
|
||||||
|
// to resolve. This is only present if this is a resolution request for
|
||||||
|
// an HTLC on our commitment transaction.
|
||||||
|
AuxSig []byte
|
||||||
|
|
||||||
|
// SignDetails is the sign details for the second-level HTLC. This may
|
||||||
|
// be used to generate the second signature needed for broadcast.
|
||||||
|
SignDetails input.SignDetails
|
||||||
|
}
|
||||||
|
|
||||||
// ResolutionReq is used to ask an outside sub-system for additional
|
// ResolutionReq is used to ask an outside sub-system for additional
|
||||||
// information needed to resolve a contract.
|
// information needed to resolve a contract.
|
||||||
type ResolutionReq struct {
|
type ResolutionReq struct {
|
||||||
@ -31,6 +45,9 @@ type ResolutionReq struct {
|
|||||||
// resolve.
|
// resolve.
|
||||||
ChanPoint wire.OutPoint
|
ChanPoint wire.OutPoint
|
||||||
|
|
||||||
|
// ChanType is the type of the channel that we are trying to resolve.
|
||||||
|
ChanType channeldb.ChannelType
|
||||||
|
|
||||||
// ShortChanID is the short channel ID of the channel that we are
|
// ShortChanID is the short channel ID of the channel that we are
|
||||||
// trying to resolve.
|
// trying to resolve.
|
||||||
ShortChanID lnwire.ShortChannelID
|
ShortChanID lnwire.ShortChannelID
|
||||||
@ -44,6 +61,13 @@ type ResolutionReq struct {
|
|||||||
// FundingBlob is an optional funding blob for the channel.
|
// FundingBlob is an optional funding blob for the channel.
|
||||||
FundingBlob fn.Option[tlv.Blob]
|
FundingBlob fn.Option[tlv.Blob]
|
||||||
|
|
||||||
|
// HtlcID is the ID of the HTLC that we are trying to resolve. This is
|
||||||
|
// only set if this is a resolution request for an HTLC.
|
||||||
|
HtlcID fn.Option[input.HtlcIndex]
|
||||||
|
|
||||||
|
// HtlcAmt is the amount of the HTLC that we are trying to resolve.
|
||||||
|
HtlcAmt btcutil.Amount
|
||||||
|
|
||||||
// Type is the type of the witness that we are trying to resolve.
|
// Type is the type of the witness that we are trying to resolve.
|
||||||
Type input.WitnessType
|
Type input.WitnessType
|
||||||
|
|
||||||
@ -69,14 +93,26 @@ type ResolutionReq struct {
|
|||||||
// CsvDelay is the CSV delay for the local output for this commitment.
|
// CsvDelay is the CSV delay for the local output for this commitment.
|
||||||
CsvDelay uint32
|
CsvDelay uint32
|
||||||
|
|
||||||
|
// CommitCsvDelay is the CSV delay for the remote output for this
|
||||||
|
// commitment.
|
||||||
|
CommitCsvDelay uint32
|
||||||
|
|
||||||
// BreachCsvDelay is the CSV delay for the remote output. This is only
|
// BreachCsvDelay is the CSV delay for the remote output. This is only
|
||||||
// set when the CloseType is Breach. This indicates the CSV delay to
|
// set when the CloseType is Breach. This indicates the CSV delay to
|
||||||
// use for the remote party's to_local delayed output, that is now
|
// use for the remote party's to_local delayed output, that is now
|
||||||
// rightfully ours in a breach situation.
|
// rightfully ours in a breach situation.
|
||||||
BreachCsvDelay fn.Option[uint32]
|
BreachCsvDelay fn.Option[uint32]
|
||||||
|
|
||||||
// CltvDelay is the CLTV delay for the outpoint.
|
// CltvDelay is the CLTV delay for the outpoint/transaction.
|
||||||
CltvDelay fn.Option[uint32]
|
CltvDelay fn.Option[uint32]
|
||||||
|
|
||||||
|
// PayHash is the payment hash for the HTLC that we are trying to
|
||||||
|
// resolve. This is optional as it only applies HTLC outputs.
|
||||||
|
PayHash fn.Option[[32]byte]
|
||||||
|
|
||||||
|
// AuxSigDesc is an optional field that contains additional information
|
||||||
|
// needed to sweep second level HTLCs.
|
||||||
|
AuxSigDesc fn.Option[AuxSigDesc]
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuxContractResolver is an interface that is used to resolve contracts that
|
// AuxContractResolver is an interface that is used to resolve contracts that
|
||||||
|
@ -666,6 +666,7 @@ func (lc *LightningChannel) diskCommitToMemCommit(
|
|||||||
return s.FetchLeavesFromCommit(
|
return s.FetchLeavesFromCommit(
|
||||||
NewAuxChanState(lc.channelState), *diskCommit,
|
NewAuxChanState(lc.channelState), *diskCommit,
|
||||||
*commitKeys.GetForParty(whoseCommit),
|
*commitKeys.GetForParty(whoseCommit),
|
||||||
|
whoseCommit,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
).Unpack()
|
).Unpack()
|
||||||
@ -1834,7 +1835,7 @@ func (lc *LightningChannel) restorePendingLocalUpdates(
|
|||||||
func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
|
func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
|
||||||
return s.FetchLeavesFromCommit(
|
return s.FetchLeavesFromCommit(
|
||||||
NewAuxChanState(lc.channelState), pendingCommit,
|
NewAuxChanState(lc.channelState), pendingCommit,
|
||||||
*pendingRemoteKeys,
|
*pendingRemoteKeys, lntypes.Remote,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
).Unpack()
|
).Unpack()
|
||||||
@ -2202,6 +2203,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||||||
// resolution data for this output.
|
// resolution data for this output.
|
||||||
resolveReq := ResolutionReq{
|
resolveReq := ResolutionReq{
|
||||||
ChanPoint: chanState.FundingOutpoint,
|
ChanPoint: chanState.FundingOutpoint,
|
||||||
|
ChanType: chanState.ChanType,
|
||||||
ShortChanID: chanState.ShortChanID(),
|
ShortChanID: chanState.ShortChanID(),
|
||||||
Initiator: chanState.IsInitiator,
|
Initiator: chanState.IsInitiator,
|
||||||
FundingBlob: chanState.CustomBlob,
|
FundingBlob: chanState.CustomBlob,
|
||||||
@ -2281,6 +2283,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||||||
// resolution data for this output.
|
// resolution data for this output.
|
||||||
resolveReq := ResolutionReq{
|
resolveReq := ResolutionReq{
|
||||||
ChanPoint: chanState.FundingOutpoint,
|
ChanPoint: chanState.FundingOutpoint,
|
||||||
|
ChanType: chanState.ChanType,
|
||||||
ShortChanID: chanState.ShortChanID(),
|
ShortChanID: chanState.ShortChanID(),
|
||||||
Initiator: chanState.IsInitiator,
|
Initiator: chanState.IsInitiator,
|
||||||
FundingBlob: chanState.CustomBlob,
|
FundingBlob: chanState.CustomBlob,
|
||||||
@ -3152,7 +3155,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
|
|||||||
leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
|
leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
|
||||||
return s.FetchLeavesFromCommit(
|
return s.FetchLeavesFromCommit(
|
||||||
NewAuxChanState(chanState), *diskCommit,
|
NewAuxChanState(chanState), *diskCommit,
|
||||||
*keyRing,
|
*keyRing, lntypes.Remote,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
).Unpack()
|
).Unpack()
|
||||||
@ -4453,7 +4456,9 @@ func (lc *LightningChannel) ProcessChanSyncMsg(ctx context.Context,
|
|||||||
// Next, we'll need to send over any updates we sent as part of
|
// Next, we'll need to send over any updates we sent as part of
|
||||||
// this new proposed commitment state.
|
// this new proposed commitment state.
|
||||||
for _, logUpdate := range commitDiff.LogUpdates {
|
for _, logUpdate := range commitDiff.LogUpdates {
|
||||||
commitUpdates = append(commitUpdates, logUpdate.UpdateMsg)
|
commitUpdates = append(
|
||||||
|
commitUpdates, logUpdate.UpdateMsg,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a taproot channel, then we need to regenerate the
|
// If this is a taproot channel, then we need to regenerate the
|
||||||
@ -4736,7 +4741,7 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel,
|
|||||||
leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
|
leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
|
||||||
return s.FetchLeavesFromCommit(
|
return s.FetchLeavesFromCommit(
|
||||||
NewAuxChanState(chanState), *diskCommit,
|
NewAuxChanState(chanState), *diskCommit,
|
||||||
*keyRing,
|
*keyRing, lntypes.Local,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
).Unpack()
|
).Unpack()
|
||||||
@ -6704,7 +6709,7 @@ type UnilateralCloseSummary struct {
|
|||||||
// happen in case we have lost state) it should be set to an empty struct, in
|
// happen in case we have lost state) it should be set to an empty struct, in
|
||||||
// which case we will attempt to sweep the non-HTLC output using the passed
|
// which case we will attempt to sweep the non-HTLC output using the passed
|
||||||
// commitPoint.
|
// commitPoint.
|
||||||
func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
|
func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, //nolint:funlen
|
||||||
signer input.Signer, commitSpend *chainntnfs.SpendDetail,
|
signer input.Signer, commitSpend *chainntnfs.SpendDetail,
|
||||||
remoteCommit channeldb.ChannelCommitment, commitPoint *btcec.PublicKey,
|
remoteCommit channeldb.ChannelCommitment, commitPoint *btcec.PublicKey,
|
||||||
leafStore fn.Option[AuxLeafStore],
|
leafStore fn.Option[AuxLeafStore],
|
||||||
@ -6723,7 +6728,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
|
|||||||
leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
|
leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
|
||||||
return s.FetchLeavesFromCommit(
|
return s.FetchLeavesFromCommit(
|
||||||
NewAuxChanState(chanState), remoteCommit,
|
NewAuxChanState(chanState), remoteCommit,
|
||||||
*keyRing,
|
*keyRing, lntypes.Remote,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
).Unpack()
|
).Unpack()
|
||||||
@ -6748,8 +6753,8 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
|
|||||||
chainfee.SatPerKWeight(remoteCommit.FeePerKw), commitType,
|
chainfee.SatPerKWeight(remoteCommit.FeePerKw), commitType,
|
||||||
signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
||||||
&chanState.RemoteChanCfg, commitSpend.SpendingTx,
|
&chanState.RemoteChanCfg, commitSpend.SpendingTx,
|
||||||
chanState.ChanType, isRemoteInitiator, leaseExpiry,
|
chanState.ChanType, isRemoteInitiator, leaseExpiry, chanState,
|
||||||
auxResult.AuxLeaves,
|
auxResult.AuxLeaves, auxResolver,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to create htlc resolutions: %w",
|
return nil, fmt.Errorf("unable to create htlc resolutions: %w",
|
||||||
@ -6839,6 +6844,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
|
|||||||
// resolution data for this output.
|
// resolution data for this output.
|
||||||
resolveReq := ResolutionReq{
|
resolveReq := ResolutionReq{
|
||||||
ChanPoint: chanState.FundingOutpoint,
|
ChanPoint: chanState.FundingOutpoint,
|
||||||
|
ChanType: chanState.ChanType,
|
||||||
ShortChanID: chanState.ShortChanID(),
|
ShortChanID: chanState.ShortChanID(),
|
||||||
Initiator: chanState.IsInitiator,
|
Initiator: chanState.IsInitiator,
|
||||||
CommitBlob: chanState.RemoteCommitment.CustomBlob,
|
CommitBlob: chanState.RemoteCommitment.CustomBlob,
|
||||||
@ -6956,6 +6962,11 @@ type IncomingHtlcResolution struct {
|
|||||||
// necessary items required to spend the sole output of the above
|
// necessary items required to spend the sole output of the above
|
||||||
// transaction.
|
// transaction.
|
||||||
SweepSignDesc input.SignDescriptor
|
SweepSignDesc input.SignDescriptor
|
||||||
|
|
||||||
|
// ResolutionBlob is a blob used for aux channels that permits a
|
||||||
|
// spender of the output to properly resolve it in the case of a force
|
||||||
|
// close.
|
||||||
|
ResolutionBlob fn.Option[tlv.Blob]
|
||||||
}
|
}
|
||||||
|
|
||||||
// OutgoingHtlcResolution houses the information necessary to sweep any
|
// OutgoingHtlcResolution houses the information necessary to sweep any
|
||||||
@ -7005,6 +7016,11 @@ type OutgoingHtlcResolution struct {
|
|||||||
// necessary items required to spend the sole output of the above
|
// necessary items required to spend the sole output of the above
|
||||||
// transaction.
|
// transaction.
|
||||||
SweepSignDesc input.SignDescriptor
|
SweepSignDesc input.SignDescriptor
|
||||||
|
|
||||||
|
// ResolutionBlob is a blob used for aux channels that permits a
|
||||||
|
// spender of the output to properly resolve it in the case of a force
|
||||||
|
// close.
|
||||||
|
ResolutionBlob fn.Option[tlv.Blob]
|
||||||
}
|
}
|
||||||
|
|
||||||
// HtlcResolutions contains the items necessary to sweep HTLC's on chain
|
// HtlcResolutions contains the items necessary to sweep HTLC's on chain
|
||||||
@ -7029,8 +7045,10 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||||||
htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
|
htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
|
||||||
feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32,
|
feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32,
|
||||||
whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool,
|
whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool,
|
||||||
chanType channeldb.ChannelType,
|
chanType channeldb.ChannelType, chanState *channeldb.OpenChannel,
|
||||||
auxLeaves fn.Option[CommitAuxLeaves]) (*OutgoingHtlcResolution, error) {
|
auxLeaves fn.Option[CommitAuxLeaves],
|
||||||
|
auxResolver fn.Option[AuxContractResolver],
|
||||||
|
) (*OutgoingHtlcResolution, error) {
|
||||||
|
|
||||||
op := wire.OutPoint{
|
op := wire.OutPoint{
|
||||||
Hash: commitTx.TxHash(),
|
Hash: commitTx.TxHash(),
|
||||||
@ -7061,6 +7079,8 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
htlcCsvDelay := HtlcSecondLevelInputSequence(chanType)
|
||||||
|
|
||||||
// If we're spending this HTLC output from the remote node's
|
// If we're spending this HTLC output from the remote node's
|
||||||
// commitment, then we won't need to go to the second level as our
|
// commitment, then we won't need to go to the second level as our
|
||||||
// outputs don't have a CSV delay.
|
// outputs don't have a CSV delay.
|
||||||
@ -7098,11 +7118,43 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resReq := ResolutionReq{
|
||||||
|
ChanPoint: chanState.FundingOutpoint,
|
||||||
|
ChanType: chanType,
|
||||||
|
ShortChanID: chanState.ShortChanID(),
|
||||||
|
Initiator: chanState.IsInitiator,
|
||||||
|
CommitBlob: chanState.RemoteCommitment.CustomBlob,
|
||||||
|
FundingBlob: chanState.CustomBlob,
|
||||||
|
Type: input.TaprootHtlcOfferedRemoteTimeout,
|
||||||
|
CloseType: RemoteForceClose,
|
||||||
|
CommitTx: commitTx,
|
||||||
|
ContractPoint: op,
|
||||||
|
SignDesc: signDesc,
|
||||||
|
KeyRing: keyRing,
|
||||||
|
CsvDelay: htlcCsvDelay,
|
||||||
|
CltvDelay: fn.Some(htlc.RefundTimeout),
|
||||||
|
CommitFee: chanState.RemoteCommitment.CommitFee,
|
||||||
|
HtlcID: fn.Some(htlc.HtlcIndex),
|
||||||
|
PayHash: fn.Some(htlc.RHash),
|
||||||
|
}
|
||||||
|
resolveRes := fn.MapOptionZ(
|
||||||
|
auxResolver,
|
||||||
|
func(a AuxContractResolver) fn.Result[tlv.Blob] {
|
||||||
|
return a.ResolveContract(resReq)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err := resolveRes.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to aux resolve: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resolutionBlob := resolveRes.Option()
|
||||||
|
|
||||||
return &OutgoingHtlcResolution{
|
return &OutgoingHtlcResolution{
|
||||||
Expiry: htlc.RefundTimeout,
|
Expiry: htlc.RefundTimeout,
|
||||||
ClaimOutpoint: op,
|
ClaimOutpoint: op,
|
||||||
SweepSignDesc: signDesc,
|
SweepSignDesc: signDesc,
|
||||||
CsvDelay: HtlcSecondLevelInputSequence(chanType),
|
CsvDelay: csvDelay,
|
||||||
|
ResolutionBlob: resolutionBlob,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7253,16 +7305,9 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||||||
keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey,
|
keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey,
|
||||||
)
|
)
|
||||||
|
|
||||||
return &OutgoingHtlcResolution{
|
// In addition to the info in txSignDetails, we also need extra
|
||||||
Expiry: htlc.RefundTimeout,
|
// information to sweep the second level output after confirmation.
|
||||||
SignedTimeoutTx: timeoutTx,
|
sweepSignDesc := input.SignDescriptor{
|
||||||
SignDetails: txSignDetails,
|
|
||||||
CsvDelay: csvDelay,
|
|
||||||
ClaimOutpoint: wire.OutPoint{
|
|
||||||
Hash: timeoutTx.TxHash(),
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
SweepSignDesc: input.SignDescriptor{
|
|
||||||
KeyDesc: localChanCfg.DelayBasePoint,
|
KeyDesc: localChanCfg.DelayBasePoint,
|
||||||
SingleTweak: localDelayTweak,
|
SingleTweak: localDelayTweak,
|
||||||
WitnessScript: htlcSweepWitnessScript,
|
WitnessScript: htlcSweepWitnessScript,
|
||||||
@ -7277,7 +7322,61 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||||||
),
|
),
|
||||||
SignMethod: signMethod,
|
SignMethod: signMethod,
|
||||||
ControlBlock: ctrlBlock,
|
ControlBlock: ctrlBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
// This might be an aux channel, so we'll go ahead and attempt to
|
||||||
|
// generate the resolution blob for the channel so we can pass along to
|
||||||
|
// the sweeping sub-system.
|
||||||
|
resolveRes := fn.MapOptionZ(
|
||||||
|
auxResolver, func(a AuxContractResolver) fn.Result[tlv.Blob] {
|
||||||
|
resReq := ResolutionReq{
|
||||||
|
ChanPoint: chanState.FundingOutpoint,
|
||||||
|
ChanType: chanType,
|
||||||
|
ShortChanID: chanState.ShortChanID(),
|
||||||
|
Initiator: chanState.IsInitiator,
|
||||||
|
CommitBlob: chanState.LocalCommitment.CustomBlob, //nolint:lll
|
||||||
|
FundingBlob: chanState.CustomBlob,
|
||||||
|
Type: input.TaprootHtlcLocalOfferedTimeout, //nolint:lll
|
||||||
|
CloseType: LocalForceClose,
|
||||||
|
CommitTx: commitTx,
|
||||||
|
ContractPoint: op,
|
||||||
|
SignDesc: sweepSignDesc,
|
||||||
|
KeyRing: keyRing,
|
||||||
|
CsvDelay: htlcCsvDelay,
|
||||||
|
HtlcAmt: btcutil.Amount(txOut.Value),
|
||||||
|
CommitCsvDelay: csvDelay,
|
||||||
|
CltvDelay: fn.Some(htlc.RefundTimeout),
|
||||||
|
CommitFee: chanState.LocalCommitment.CommitFee, //nolint:lll
|
||||||
|
HtlcID: fn.Some(htlc.HtlcIndex),
|
||||||
|
PayHash: fn.Some(htlc.RHash),
|
||||||
|
AuxSigDesc: fn.Some(AuxSigDesc{
|
||||||
|
SignDetails: *txSignDetails,
|
||||||
|
AuxSig: func() []byte {
|
||||||
|
tlvType := htlcCustomSigType.TypeVal() //nolint:lll
|
||||||
|
return htlc.CustomRecords[uint64(tlvType)] //nolint:lll
|
||||||
|
}(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.ResolveContract(resReq)
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
if err := resolveRes.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to aux resolve: %w", err)
|
||||||
|
}
|
||||||
|
resolutionBlob := resolveRes.Option()
|
||||||
|
|
||||||
|
return &OutgoingHtlcResolution{
|
||||||
|
Expiry: htlc.RefundTimeout,
|
||||||
|
SignedTimeoutTx: timeoutTx,
|
||||||
|
SignDetails: txSignDetails,
|
||||||
|
CsvDelay: csvDelay,
|
||||||
|
ResolutionBlob: resolutionBlob,
|
||||||
|
ClaimOutpoint: wire.OutPoint{
|
||||||
|
Hash: timeoutTx.TxHash(),
|
||||||
|
Index: 0,
|
||||||
|
},
|
||||||
|
SweepSignDesc: sweepSignDesc,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7293,8 +7392,10 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||||||
htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
|
htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
|
||||||
feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32,
|
feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32,
|
||||||
whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool,
|
whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool,
|
||||||
chanType channeldb.ChannelType,
|
chanType channeldb.ChannelType, chanState *channeldb.OpenChannel,
|
||||||
auxLeaves fn.Option[CommitAuxLeaves]) (*IncomingHtlcResolution, error) {
|
auxLeaves fn.Option[CommitAuxLeaves],
|
||||||
|
auxResolver fn.Option[AuxContractResolver],
|
||||||
|
) (*IncomingHtlcResolution, error) {
|
||||||
|
|
||||||
op := wire.OutPoint{
|
op := wire.OutPoint{
|
||||||
Hash: commitTx.TxHash(),
|
Hash: commitTx.TxHash(),
|
||||||
@ -7326,6 +7427,8 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
htlcCsvDelay := HtlcSecondLevelInputSequence(chanType)
|
||||||
|
|
||||||
// If we're spending this output from the remote node's commitment,
|
// If we're spending this output from the remote node's commitment,
|
||||||
// then we can skip the second layer and spend the output directly.
|
// then we can skip the second layer and spend the output directly.
|
||||||
if whoseCommit.IsRemote() {
|
if whoseCommit.IsRemote() {
|
||||||
@ -7361,10 +7464,44 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resReq := ResolutionReq{
|
||||||
|
ChanPoint: chanState.FundingOutpoint,
|
||||||
|
ChanType: chanType,
|
||||||
|
ShortChanID: chanState.ShortChanID(),
|
||||||
|
Initiator: chanState.IsInitiator,
|
||||||
|
CommitBlob: chanState.RemoteCommitment.CustomBlob,
|
||||||
|
Type: input.TaprootHtlcAcceptedRemoteSuccess,
|
||||||
|
FundingBlob: chanState.CustomBlob,
|
||||||
|
CloseType: RemoteForceClose,
|
||||||
|
CommitTx: commitTx,
|
||||||
|
ContractPoint: op,
|
||||||
|
SignDesc: signDesc,
|
||||||
|
KeyRing: keyRing,
|
||||||
|
HtlcID: fn.Some(htlc.HtlcIndex),
|
||||||
|
CsvDelay: htlcCsvDelay,
|
||||||
|
CltvDelay: fn.Some(htlc.RefundTimeout),
|
||||||
|
CommitFee: chanState.RemoteCommitment.CommitFee,
|
||||||
|
PayHash: fn.Some(htlc.RHash),
|
||||||
|
CommitCsvDelay: csvDelay,
|
||||||
|
HtlcAmt: htlc.Amt.ToSatoshis(),
|
||||||
|
}
|
||||||
|
resolveRes := fn.MapOptionZ(
|
||||||
|
auxResolver,
|
||||||
|
func(a AuxContractResolver) fn.Result[tlv.Blob] {
|
||||||
|
return a.ResolveContract(resReq)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err := resolveRes.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to aux resolve: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resolutionBlob := resolveRes.Option()
|
||||||
|
|
||||||
return &IncomingHtlcResolution{
|
return &IncomingHtlcResolution{
|
||||||
ClaimOutpoint: op,
|
ClaimOutpoint: op,
|
||||||
SweepSignDesc: signDesc,
|
SweepSignDesc: signDesc,
|
||||||
CsvDelay: HtlcSecondLevelInputSequence(chanType),
|
CsvDelay: htlcCsvDelay,
|
||||||
|
ResolutionBlob: resolutionBlob,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7510,15 +7647,10 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||||||
localDelayTweak := input.SingleTweakBytes(
|
localDelayTweak := input.SingleTweakBytes(
|
||||||
keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey,
|
keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey,
|
||||||
)
|
)
|
||||||
return &IncomingHtlcResolution{
|
|
||||||
SignedSuccessTx: successTx,
|
// In addition to the info in txSignDetails, we also need extra
|
||||||
SignDetails: txSignDetails,
|
// information to sweep the second level output after confirmation.
|
||||||
CsvDelay: csvDelay,
|
sweepSignDesc := input.SignDescriptor{
|
||||||
ClaimOutpoint: wire.OutPoint{
|
|
||||||
Hash: successTx.TxHash(),
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
SweepSignDesc: input.SignDescriptor{
|
|
||||||
KeyDesc: localChanCfg.DelayBasePoint,
|
KeyDesc: localChanCfg.DelayBasePoint,
|
||||||
SingleTweak: localDelayTweak,
|
SingleTweak: localDelayTweak,
|
||||||
WitnessScript: htlcSweepWitnessScript,
|
WitnessScript: htlcSweepWitnessScript,
|
||||||
@ -7533,7 +7665,58 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||||||
),
|
),
|
||||||
SignMethod: signMethod,
|
SignMethod: signMethod,
|
||||||
ControlBlock: ctrlBlock,
|
ControlBlock: ctrlBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveRes := fn.MapOptionZ(
|
||||||
|
auxResolver, func(a AuxContractResolver) fn.Result[tlv.Blob] {
|
||||||
|
resReq := ResolutionReq{
|
||||||
|
ChanPoint: chanState.FundingOutpoint,
|
||||||
|
ChanType: chanType,
|
||||||
|
ShortChanID: chanState.ShortChanID(),
|
||||||
|
Initiator: chanState.IsInitiator,
|
||||||
|
CommitBlob: chanState.LocalCommitment.CustomBlob, //nolint:lll
|
||||||
|
Type: input.TaprootHtlcAcceptedLocalSuccess, //nolint:lll
|
||||||
|
FundingBlob: chanState.CustomBlob,
|
||||||
|
CloseType: LocalForceClose,
|
||||||
|
CommitTx: commitTx,
|
||||||
|
ContractPoint: op,
|
||||||
|
SignDesc: sweepSignDesc,
|
||||||
|
KeyRing: keyRing,
|
||||||
|
HtlcID: fn.Some(htlc.HtlcIndex),
|
||||||
|
CsvDelay: htlcCsvDelay,
|
||||||
|
CommitFee: chanState.LocalCommitment.CommitFee, //nolint:lll
|
||||||
|
PayHash: fn.Some(htlc.RHash),
|
||||||
|
AuxSigDesc: fn.Some(AuxSigDesc{
|
||||||
|
SignDetails: *txSignDetails,
|
||||||
|
AuxSig: func() []byte {
|
||||||
|
tlvType := htlcCustomSigType.TypeVal() //nolint:lll
|
||||||
|
return htlc.CustomRecords[uint64(tlvType)] //nolint:lll
|
||||||
|
}(),
|
||||||
|
}),
|
||||||
|
CommitCsvDelay: csvDelay,
|
||||||
|
HtlcAmt: btcutil.Amount(txOut.Value),
|
||||||
|
CltvDelay: fn.Some(htlc.RefundTimeout),
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.ResolveContract(resReq)
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
if err := resolveRes.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to aux resolve: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resolutionBlob := resolveRes.Option()
|
||||||
|
|
||||||
|
return &IncomingHtlcResolution{
|
||||||
|
SignedSuccessTx: successTx,
|
||||||
|
SignDetails: txSignDetails,
|
||||||
|
CsvDelay: csvDelay,
|
||||||
|
ResolutionBlob: resolutionBlob,
|
||||||
|
ClaimOutpoint: wire.OutPoint{
|
||||||
|
Hash: successTx.TxHash(),
|
||||||
|
Index: 0,
|
||||||
|
},
|
||||||
|
SweepSignDesc: sweepSignDesc,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7570,7 +7753,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight,
|
|||||||
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
|
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
|
||||||
commitTx *wire.MsgTx, chanType channeldb.ChannelType,
|
commitTx *wire.MsgTx, chanType channeldb.ChannelType,
|
||||||
isCommitFromInitiator bool, leaseExpiry uint32,
|
isCommitFromInitiator bool, leaseExpiry uint32,
|
||||||
auxLeaves fn.Option[CommitAuxLeaves]) (*HtlcResolutions, error) {
|
chanState *channeldb.OpenChannel, auxLeaves fn.Option[CommitAuxLeaves],
|
||||||
|
auxResolver fn.Option[AuxContractResolver]) (*HtlcResolutions, error) {
|
||||||
|
|
||||||
// TODO(roasbeef): don't need to swap csv delay?
|
// TODO(roasbeef): don't need to swap csv delay?
|
||||||
dustLimit := remoteChanCfg.DustLimit
|
dustLimit := remoteChanCfg.DustLimit
|
||||||
@ -7605,7 +7789,7 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight,
|
|||||||
signer, localChanCfg, commitTx, &htlc,
|
signer, localChanCfg, commitTx, &htlc,
|
||||||
keyRing, feePerKw, uint32(csvDelay),
|
keyRing, feePerKw, uint32(csvDelay),
|
||||||
leaseExpiry, whoseCommit, isCommitFromInitiator,
|
leaseExpiry, whoseCommit, isCommitFromInitiator,
|
||||||
chanType, auxLeaves,
|
chanType, chanState, auxLeaves, auxResolver,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("incoming resolution "+
|
return nil, fmt.Errorf("incoming resolution "+
|
||||||
@ -7619,7 +7803,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight,
|
|||||||
ohr, err := newOutgoingHtlcResolution(
|
ohr, err := newOutgoingHtlcResolution(
|
||||||
signer, localChanCfg, commitTx, &htlc, keyRing,
|
signer, localChanCfg, commitTx, &htlc, keyRing,
|
||||||
feePerKw, uint32(csvDelay), leaseExpiry, whoseCommit,
|
feePerKw, uint32(csvDelay), leaseExpiry, whoseCommit,
|
||||||
isCommitFromInitiator, chanType, auxLeaves,
|
isCommitFromInitiator, chanType, chanState, auxLeaves,
|
||||||
|
auxResolver,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("outgoing resolution "+
|
return nil, fmt.Errorf("outgoing resolution "+
|
||||||
@ -7765,6 +7950,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
|||||||
return s.FetchLeavesFromCommit(
|
return s.FetchLeavesFromCommit(
|
||||||
NewAuxChanState(chanState),
|
NewAuxChanState(chanState),
|
||||||
chanState.LocalCommitment, *keyRing,
|
chanState.LocalCommitment, *keyRing,
|
||||||
|
lntypes.Local,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
).Unpack()
|
).Unpack()
|
||||||
@ -7871,7 +8057,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
|||||||
func(a AuxContractResolver) fn.Result[tlv.Blob] {
|
func(a AuxContractResolver) fn.Result[tlv.Blob] {
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
return a.ResolveContract(ResolutionReq{
|
return a.ResolveContract(ResolutionReq{
|
||||||
ChanPoint: chanState.FundingOutpoint,
|
ChanPoint: chanState.FundingOutpoint, //nolint:lll
|
||||||
|
ChanType: chanState.ChanType,
|
||||||
ShortChanID: chanState.ShortChanID(),
|
ShortChanID: chanState.ShortChanID(),
|
||||||
Initiator: chanState.IsInitiator,
|
Initiator: chanState.IsInitiator,
|
||||||
CommitBlob: chanState.LocalCommitment.CustomBlob,
|
CommitBlob: chanState.LocalCommitment.CustomBlob,
|
||||||
@ -7904,7 +8091,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
|||||||
chainfee.SatPerKWeight(localCommit.FeePerKw), lntypes.Local,
|
chainfee.SatPerKWeight(localCommit.FeePerKw), lntypes.Local,
|
||||||
signer, localCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
signer, localCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
||||||
&chanState.RemoteChanCfg, commitTx, chanState.ChanType,
|
&chanState.RemoteChanCfg, commitTx, chanState.ChanType,
|
||||||
chanState.IsInitiator, leaseExpiry, auxResult.AuxLeaves,
|
chanState.IsInitiator, leaseExpiry, chanState,
|
||||||
|
auxResult.AuxLeaves, auxResolver,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to gen htlc resolution: %w", err)
|
return nil, fmt.Errorf("unable to gen htlc resolution: %w", err)
|
||||||
|
@ -1307,7 +1307,7 @@ func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash,
|
|||||||
leafStore, func(a AuxLeafStore) fn.Result[CommitDiffAuxResult] {
|
leafStore, func(a AuxLeafStore) fn.Result[CommitDiffAuxResult] {
|
||||||
return a.FetchLeavesFromCommit(
|
return a.FetchLeavesFromCommit(
|
||||||
NewAuxChanState(chanState), chanCommit,
|
NewAuxChanState(chanState), chanCommit,
|
||||||
*keyRing,
|
*keyRing, lntypes.Remote,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
).Unpack()
|
).Unpack()
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/fn"
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
@ -420,8 +421,8 @@ func (*MockAuxLeafStore) FetchLeavesFromView(
|
|||||||
// correspond to the passed aux blob, and an existing channel
|
// correspond to the passed aux blob, and an existing channel
|
||||||
// commitment.
|
// commitment.
|
||||||
func (*MockAuxLeafStore) FetchLeavesFromCommit(_ AuxChanState,
|
func (*MockAuxLeafStore) FetchLeavesFromCommit(_ AuxChanState,
|
||||||
_ channeldb.ChannelCommitment,
|
_ channeldb.ChannelCommitment, _ CommitmentKeyRing,
|
||||||
_ CommitmentKeyRing) fn.Result[CommitDiffAuxResult] {
|
_ lntypes.ChannelParty) fn.Result[CommitDiffAuxResult] {
|
||||||
|
|
||||||
return fn.Ok(CommitDiffAuxResult{})
|
return fn.Ok(CommitDiffAuxResult{})
|
||||||
}
|
}
|
||||||
|
51
macaroons/fuzz_test.go
Normal file
51
macaroons/fuzz_test.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package macaroons
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
|
"gopkg.in/macaroon.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuzzUnmarshalMacaroon(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
mac := &macaroon.Macaroon{}
|
||||||
|
_ = mac.UnmarshalBinary(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzAuthChecker(f *testing.F) {
|
||||||
|
rootKeyStore := bakery.NewMemRootKeyStore()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, location, entity, action, method string,
|
||||||
|
rootKey, id []byte) {
|
||||||
|
|
||||||
|
macService, err := NewService(
|
||||||
|
rootKeyStore, location, true, IPLockChecker,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
requiredPermissions := []bakery.Op{{
|
||||||
|
Entity: entity,
|
||||||
|
Action: action,
|
||||||
|
}}
|
||||||
|
|
||||||
|
mac, err := macaroon.New(rootKey, id, location, macaroon.V2)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
macBytes, err := mac.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = macService.CheckMacAuth(
|
||||||
|
ctx, macBytes, requiredPermissions, method,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
@ -603,7 +603,9 @@ func (t *TxPublisher) broadcast(requestID uint64) (*BumpResult, error) {
|
|||||||
// Before we go to broadcast, we'll notify the aux sweeper, if it's
|
// Before we go to broadcast, we'll notify the aux sweeper, if it's
|
||||||
// present of this new broadcast attempt.
|
// present of this new broadcast attempt.
|
||||||
err := fn.MapOptionZ(t.cfg.AuxSweeper, func(aux AuxSweeper) error {
|
err := fn.MapOptionZ(t.cfg.AuxSweeper, func(aux AuxSweeper) error {
|
||||||
return aux.NotifyBroadcast(record.req, tx, record.fee)
|
return aux.NotifyBroadcast(
|
||||||
|
record.req, tx, record.fee, record.outpointToTxIndex,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to notify aux sweeper: %w", err)
|
return nil, fmt.Errorf("unable to notify aux sweeper: %w", err)
|
||||||
@ -725,6 +727,9 @@ type monitorRecord struct {
|
|||||||
|
|
||||||
// fee is the fee paid by the tx.
|
// fee is the fee paid by the tx.
|
||||||
fee btcutil.Amount
|
fee btcutil.Amount
|
||||||
|
|
||||||
|
// outpointToTxIndex is a map of outpoint to tx index.
|
||||||
|
outpointToTxIndex map[wire.OutPoint]int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the publisher by subscribing to block epoch updates and kicking
|
// Start starts the publisher by subscribing to block epoch updates and kicking
|
||||||
@ -1046,6 +1051,7 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64,
|
|||||||
req: r.req,
|
req: r.req,
|
||||||
feeFunction: r.feeFunction,
|
feeFunction: r.feeFunction,
|
||||||
fee: sweepCtx.fee,
|
fee: sweepCtx.fee,
|
||||||
|
outpointToTxIndex: sweepCtx.outpointToTxIndex,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Attempt to broadcast this new tx.
|
// Attempt to broadcast this new tx.
|
||||||
@ -1199,6 +1205,10 @@ type sweepTxCtx struct {
|
|||||||
fee btcutil.Amount
|
fee btcutil.Amount
|
||||||
|
|
||||||
extraTxOut fn.Option[SweepOutput]
|
extraTxOut fn.Option[SweepOutput]
|
||||||
|
|
||||||
|
// outpointToTxIndex maps the outpoint of the inputs to their index in
|
||||||
|
// the sweep transaction.
|
||||||
|
outpointToTxIndex map[wire.OutPoint]int
|
||||||
}
|
}
|
||||||
|
|
||||||
// createSweepTx creates a sweeping tx based on the given inputs, change
|
// createSweepTx creates a sweeping tx based on the given inputs, change
|
||||||
@ -1229,6 +1239,7 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input,
|
|||||||
// We start by adding all inputs that commit to an output. We do this
|
// We start by adding all inputs that commit to an output. We do this
|
||||||
// since the input and output index must stay the same for the
|
// since the input and output index must stay the same for the
|
||||||
// signatures to be valid.
|
// signatures to be valid.
|
||||||
|
outpointToTxIndex := make(map[wire.OutPoint]int)
|
||||||
for _, o := range inputs {
|
for _, o := range inputs {
|
||||||
if o.RequiredTxOut() == nil {
|
if o.RequiredTxOut() == nil {
|
||||||
continue
|
continue
|
||||||
@ -1240,6 +1251,8 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input,
|
|||||||
Sequence: o.BlocksToMaturity(),
|
Sequence: o.BlocksToMaturity(),
|
||||||
})
|
})
|
||||||
sweepTx.AddTxOut(o.RequiredTxOut())
|
sweepTx.AddTxOut(o.RequiredTxOut())
|
||||||
|
|
||||||
|
outpointToTxIndex[o.OutPoint()] = len(sweepTx.TxOut) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sum up the value contained in the remaining inputs, and add them to
|
// Sum up the value contained in the remaining inputs, and add them to
|
||||||
@ -1334,6 +1347,7 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input,
|
|||||||
tx: sweepTx,
|
tx: sweepTx,
|
||||||
fee: txFee,
|
fee: txFee,
|
||||||
extraTxOut: fn.FlattenOption(extraTxOut),
|
extraTxOut: fn.FlattenOption(extraTxOut),
|
||||||
|
outpointToTxIndex: outpointToTxIndex,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,5 +93,6 @@ type AuxSweeper interface {
|
|||||||
// NotifyBroadcast is used to notify external callers of the broadcast
|
// NotifyBroadcast is used to notify external callers of the broadcast
|
||||||
// of a sweep transaction, generated by the passed BumpRequest.
|
// of a sweep transaction, generated by the passed BumpRequest.
|
||||||
NotifyBroadcast(req *BumpRequest, tx *wire.MsgTx,
|
NotifyBroadcast(req *BumpRequest, tx *wire.MsgTx,
|
||||||
totalFees btcutil.Amount) error
|
totalFees btcutil.Amount,
|
||||||
|
outpointToTxIndex map[wire.OutPoint]int) error
|
||||||
}
|
}
|
||||||
|
@ -352,7 +352,7 @@ func (m *MockAuxSweeper) ExtraBudgetForInputs(
|
|||||||
// NotifyBroadcast is used to notify external callers of the broadcast
|
// NotifyBroadcast is used to notify external callers of the broadcast
|
||||||
// of a sweep transaction, generated by the passed BumpRequest.
|
// of a sweep transaction, generated by the passed BumpRequest.
|
||||||
func (*MockAuxSweeper) NotifyBroadcast(_ *BumpRequest, _ *wire.MsgTx,
|
func (*MockAuxSweeper) NotifyBroadcast(_ *BumpRequest, _ *wire.MsgTx,
|
||||||
_ btcutil.Amount) error {
|
_ btcutil.Amount, _ map[wire.OutPoint]int) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -373,7 +373,9 @@ func (b *BudgetInputSet) Budget() btcutil.Amount {
|
|||||||
budget += input.params.Budget
|
budget += input.params.Budget
|
||||||
}
|
}
|
||||||
|
|
||||||
return budget
|
// We'll also tack on the extra budget which will eventually be
|
||||||
|
// accounted for by the wallet txns when we're broadcasting.
|
||||||
|
return budget + b.extraBudget
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeadlineHeight returns the deadline height of the set.
|
// DeadlineHeight returns the deadline height of the set.
|
||||||
|
Loading…
Reference in New Issue
Block a user