mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
lnwallet: populate resolution blob for incoming+outgoing HTLC resolutions
In this commit, we populate the resolution blobs for the incoming and outgoing HTLCs. We take care to populate the AuxSigDesc with the correct information, as we need to pass along the second-level aux signature and also sign desc along with it.
This commit is contained in:
parent
08d0aa2368
commit
af60fa0c3a
@ -4455,7 +4455,9 @@ func (lc *LightningChannel) ProcessChanSyncMsg(ctx context.Context,
|
||||
// Next, we'll need to send over any updates we sent as part of
|
||||
// this new proposed commitment state.
|
||||
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
|
||||
@ -6706,7 +6708,7 @@ type UnilateralCloseSummary struct {
|
||||
// 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
|
||||
// commitPoint.
|
||||
func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
|
||||
func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, //nolint:funlen
|
||||
signer input.Signer, commitSpend *chainntnfs.SpendDetail,
|
||||
remoteCommit channeldb.ChannelCommitment, commitPoint *btcec.PublicKey,
|
||||
leafStore fn.Option[AuxLeafStore],
|
||||
@ -6750,8 +6752,8 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
|
||||
chainfee.SatPerKWeight(remoteCommit.FeePerKw), commitType,
|
||||
signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
||||
&chanState.RemoteChanCfg, commitSpend.SpendingTx,
|
||||
chanState.ChanType, isRemoteInitiator, leaseExpiry,
|
||||
auxResult.AuxLeaves,
|
||||
chanState.ChanType, isRemoteInitiator, leaseExpiry, chanState,
|
||||
auxResult.AuxLeaves, auxResolver,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create htlc resolutions: %w",
|
||||
@ -7042,8 +7044,10 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
||||
htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
|
||||
feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32,
|
||||
whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool,
|
||||
chanType channeldb.ChannelType,
|
||||
auxLeaves fn.Option[CommitAuxLeaves]) (*OutgoingHtlcResolution, error) {
|
||||
chanType channeldb.ChannelType, chanState *channeldb.OpenChannel,
|
||||
auxLeaves fn.Option[CommitAuxLeaves],
|
||||
auxResolver fn.Option[AuxContractResolver],
|
||||
) (*OutgoingHtlcResolution, error) {
|
||||
|
||||
op := wire.OutPoint{
|
||||
Hash: commitTx.TxHash(),
|
||||
@ -7074,6 +7078,8 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
htlcCsvDelay := HtlcSecondLevelInputSequence(chanType)
|
||||
|
||||
// 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
|
||||
// outputs don't have a CSV delay.
|
||||
@ -7111,11 +7117,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{
|
||||
Expiry: htlc.RefundTimeout,
|
||||
ClaimOutpoint: op,
|
||||
SweepSignDesc: signDesc,
|
||||
CsvDelay: HtlcSecondLevelInputSequence(chanType),
|
||||
CsvDelay: csvDelay,
|
||||
ResolutionBlob: resolutionBlob,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -7266,16 +7304,9 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
||||
keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey,
|
||||
)
|
||||
|
||||
return &OutgoingHtlcResolution{
|
||||
Expiry: htlc.RefundTimeout,
|
||||
SignedTimeoutTx: timeoutTx,
|
||||
SignDetails: txSignDetails,
|
||||
CsvDelay: csvDelay,
|
||||
ClaimOutpoint: wire.OutPoint{
|
||||
Hash: timeoutTx.TxHash(),
|
||||
Index: 0,
|
||||
},
|
||||
SweepSignDesc: input.SignDescriptor{
|
||||
// In addition to the info in txSignDetails, we also need extra
|
||||
// information to sweep the second level output after confirmation.
|
||||
sweepSignDesc := input.SignDescriptor{
|
||||
KeyDesc: localChanCfg.DelayBasePoint,
|
||||
SingleTweak: localDelayTweak,
|
||||
WitnessScript: htlcSweepWitnessScript,
|
||||
@ -7290,7 +7321,61 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
||||
),
|
||||
SignMethod: signMethod,
|
||||
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
|
||||
}
|
||||
|
||||
@ -7306,8 +7391,10 @@ func newIncomingHtlcResolution(signer input.Signer,
|
||||
htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
|
||||
feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32,
|
||||
whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool,
|
||||
chanType channeldb.ChannelType,
|
||||
auxLeaves fn.Option[CommitAuxLeaves]) (*IncomingHtlcResolution, error) {
|
||||
chanType channeldb.ChannelType, chanState *channeldb.OpenChannel,
|
||||
auxLeaves fn.Option[CommitAuxLeaves],
|
||||
auxResolver fn.Option[AuxContractResolver],
|
||||
) (*IncomingHtlcResolution, error) {
|
||||
|
||||
op := wire.OutPoint{
|
||||
Hash: commitTx.TxHash(),
|
||||
@ -7339,6 +7426,8 @@ func newIncomingHtlcResolution(signer input.Signer,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
htlcCsvDelay := HtlcSecondLevelInputSequence(chanType)
|
||||
|
||||
// If we're spending this output from the remote node's commitment,
|
||||
// then we can skip the second layer and spend the output directly.
|
||||
if whoseCommit.IsRemote() {
|
||||
@ -7374,10 +7463,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{
|
||||
ClaimOutpoint: op,
|
||||
SweepSignDesc: signDesc,
|
||||
CsvDelay: HtlcSecondLevelInputSequence(chanType),
|
||||
CsvDelay: htlcCsvDelay,
|
||||
ResolutionBlob: resolutionBlob,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -7523,15 +7646,10 @@ func newIncomingHtlcResolution(signer input.Signer,
|
||||
localDelayTweak := input.SingleTweakBytes(
|
||||
keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey,
|
||||
)
|
||||
return &IncomingHtlcResolution{
|
||||
SignedSuccessTx: successTx,
|
||||
SignDetails: txSignDetails,
|
||||
CsvDelay: csvDelay,
|
||||
ClaimOutpoint: wire.OutPoint{
|
||||
Hash: successTx.TxHash(),
|
||||
Index: 0,
|
||||
},
|
||||
SweepSignDesc: input.SignDescriptor{
|
||||
|
||||
// In addition to the info in txSignDetails, we also need extra
|
||||
// information to sweep the second level output after confirmation.
|
||||
sweepSignDesc := input.SignDescriptor{
|
||||
KeyDesc: localChanCfg.DelayBasePoint,
|
||||
SingleTweak: localDelayTweak,
|
||||
WitnessScript: htlcSweepWitnessScript,
|
||||
@ -7546,7 +7664,58 @@ func newIncomingHtlcResolution(signer input.Signer,
|
||||
),
|
||||
SignMethod: signMethod,
|
||||
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
|
||||
}
|
||||
|
||||
@ -7583,7 +7752,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight,
|
||||
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
|
||||
commitTx *wire.MsgTx, chanType channeldb.ChannelType,
|
||||
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?
|
||||
dustLimit := remoteChanCfg.DustLimit
|
||||
@ -7618,7 +7788,7 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight,
|
||||
signer, localChanCfg, commitTx, &htlc,
|
||||
keyRing, feePerKw, uint32(csvDelay),
|
||||
leaseExpiry, whoseCommit, isCommitFromInitiator,
|
||||
chanType, auxLeaves,
|
||||
chanType, chanState, auxLeaves, auxResolver,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("incoming resolution "+
|
||||
@ -7632,7 +7802,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight,
|
||||
ohr, err := newOutgoingHtlcResolution(
|
||||
signer, localChanCfg, commitTx, &htlc, keyRing,
|
||||
feePerKw, uint32(csvDelay), leaseExpiry, whoseCommit,
|
||||
isCommitFromInitiator, chanType, auxLeaves,
|
||||
isCommitFromInitiator, chanType, chanState, auxLeaves,
|
||||
auxResolver,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("outgoing resolution "+
|
||||
@ -7884,7 +8055,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
||||
func(a AuxContractResolver) fn.Result[tlv.Blob] {
|
||||
//nolint:lll
|
||||
return a.ResolveContract(ResolutionReq{
|
||||
ChanPoint: chanState.FundingOutpoint,
|
||||
ChanPoint: chanState.FundingOutpoint, //nolint:lll
|
||||
ChanType: chanState.ChanType,
|
||||
ShortChanID: chanState.ShortChanID(),
|
||||
Initiator: chanState.IsInitiator,
|
||||
CommitBlob: chanState.LocalCommitment.CustomBlob,
|
||||
@ -7917,7 +8089,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
||||
chainfee.SatPerKWeight(localCommit.FeePerKw), lntypes.Local,
|
||||
signer, localCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
||||
&chanState.RemoteChanCfg, commitTx, chanState.ChanType,
|
||||
chanState.IsInitiator, leaseExpiry, auxResult.AuxLeaves,
|
||||
chanState.IsInitiator, leaseExpiry, chanState,
|
||||
auxResult.AuxLeaves, auxResolver,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to gen htlc resolution: %w", err)
|
||||
|
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,
|
||||
)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user