lnwallet: prevent anchor reserve enforcement on legacy inbound channel
This commit aims to address a flaw in our anchor reserve enforcement logic in which an inbound "legacy" channel (i.e. a channel with a commitment type that precedes anchors) would be rejected by the recipient if they have at least one opened channel using the anchors commitment type and do not have enough on-chain funds to meet the anchors reserve.
This commit is contained in:
parent
d7299802d4
commit
6bbe790535
@ -595,33 +595,11 @@ func (l *LightningWallet) PsbtFundingVerify(pendingChanID [32]byte,
|
|||||||
"reservation ID %v", pid)
|
"reservation ID %v", pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now the the PSBT has been verified, we can again check whether the
|
// Now the the PSBT has been populated and verified, we can again check
|
||||||
// value reserved for anchor fee bumping is respected.
|
// whether the value reserved for anchor fee bumping is respected.
|
||||||
numAnchors, err := l.currentNumAnchorChans()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this commit type is an anchor channel we add that to our counter,
|
|
||||||
// but only if we are contributing funds to the channel. This is done
|
|
||||||
// to still allow incoming channels even though we have no UTXOs
|
|
||||||
// available, as in bootstrapping phases. We only count public
|
|
||||||
// channels.
|
|
||||||
isPublic := pendingReservation.partialState.ChannelFlags&lnwire.FFAnnounceChannel != 0
|
isPublic := pendingReservation.partialState.ChannelFlags&lnwire.FFAnnounceChannel != 0
|
||||||
if pendingReservation.partialState.ChanType.HasAnchors() &&
|
hasAnchors := pendingReservation.partialState.ChanType.HasAnchors()
|
||||||
intent.LocalFundingAmt() > 0 && isPublic {
|
return l.enforceNewReservedValue(intent, isPublic, hasAnchors)
|
||||||
numAnchors++
|
|
||||||
}
|
|
||||||
|
|
||||||
// We check the reserve value again, this should already have been
|
|
||||||
// checked for regular FullIntents, but now the PSBT intent is also
|
|
||||||
// populated.
|
|
||||||
return l.WithCoinSelectLock(func() error {
|
|
||||||
_, err := l.CheckReservedValue(
|
|
||||||
intent.Inputs(), intent.Outputs(), numAnchors,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PsbtFundingFinalize looks up a previously registered funding intent by its
|
// PsbtFundingFinalize looks up a previously registered funding intent by its
|
||||||
@ -809,38 +787,15 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
|
|||||||
|
|
||||||
// Now that we have a funding intent, we'll check whether funding a
|
// Now that we have a funding intent, we'll check whether funding a
|
||||||
// channel using it would violate our reserved value for anchor channel
|
// channel using it would violate our reserved value for anchor channel
|
||||||
// fee bumping. We first get our current number of anchor channels.
|
// fee bumping.
|
||||||
numAnchors, err := l.currentNumAnchorChans()
|
//
|
||||||
if err != nil {
|
|
||||||
fundingIntent.Cancel()
|
|
||||||
|
|
||||||
req.err <- err
|
|
||||||
req.resp <- nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this commit type is an anchor channel we add that to our counter,
|
|
||||||
// but only if we are contributing funds to the channel. This is done
|
|
||||||
// to still allow incoming channels even though we have no UTXOs
|
|
||||||
// available, as in bootstrapping phases. We only count public
|
|
||||||
// channels.
|
|
||||||
isPublic := req.Flags&lnwire.FFAnnounceChannel != 0
|
|
||||||
if req.CommitType == CommitmentTypeAnchorsZeroFeeHtlcTx &&
|
|
||||||
fundingIntent.LocalFundingAmt() > 0 && isPublic {
|
|
||||||
numAnchors++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the reserved value using the inputs and outputs given by the
|
// Check the reserved value using the inputs and outputs given by the
|
||||||
// intent. Not that for the PSBT intent type we don't yet have the
|
// intent. Note that for the PSBT intent type we don't yet have the
|
||||||
// funding tx ready, so this will always pass. We'll do another check
|
// funding tx ready, so this will always pass. We'll do another check
|
||||||
// when the PSBT has been verified.
|
// when the PSBT has been verified.
|
||||||
err = l.WithCoinSelectLock(func() error {
|
isPublic := req.Flags&lnwire.FFAnnounceChannel != 0
|
||||||
_, err := l.CheckReservedValue(
|
hasAnchors := req.CommitType == CommitmentTypeAnchorsZeroFeeHtlcTx
|
||||||
fundingIntent.Inputs(), fundingIntent.Outputs(),
|
err = l.enforceNewReservedValue(fundingIntent, isPublic, hasAnchors)
|
||||||
numAnchors,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fundingIntent.Cancel()
|
fundingIntent.Cancel()
|
||||||
|
|
||||||
@ -893,6 +848,42 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
|
|||||||
req.err <- nil
|
req.err <- nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enforceReservedValue enforces that the wallet, upon a new channel being
|
||||||
|
// opened, meets the minimum amount of funds required for each advertised anchor
|
||||||
|
// channel.
|
||||||
|
//
|
||||||
|
// We only enforce the reserve if we are contributing funds to the channel. This
|
||||||
|
// is done to still allow incoming channels even though we have no UTXOs
|
||||||
|
// available, as in bootstrapping phases.
|
||||||
|
func (l *LightningWallet) enforceNewReservedValue(fundingIntent chanfunding.Intent,
|
||||||
|
isPublic, hasAnchors bool) error {
|
||||||
|
|
||||||
|
// Only enforce the reserve when an advertised channel is being opened
|
||||||
|
// in which we are contributing funds to. This ensures we never dip
|
||||||
|
// below the reserve.
|
||||||
|
if !isPublic || fundingIntent.LocalFundingAmt() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
numAnchors, err := l.currentNumAnchorChans()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the to-be-opened channel.
|
||||||
|
if hasAnchors {
|
||||||
|
numAnchors++
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.WithCoinSelectLock(func() error {
|
||||||
|
_, err := l.CheckReservedValue(
|
||||||
|
fundingIntent.Inputs(), fundingIntent.Outputs(),
|
||||||
|
numAnchors,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// currentNumAnchorChans returns the current number of non-private anchor
|
// currentNumAnchorChans returns the current number of non-private anchor
|
||||||
// channels the wallet should be ready to fee bump if needed.
|
// channels the wallet should be ready to fee bump if needed.
|
||||||
func (l *LightningWallet) currentNumAnchorChans() (int, error) {
|
func (l *LightningWallet) currentNumAnchorChans() (int, error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user