lnwallet: check Reserved value on funding request
This commit is contained in:
parent
3cc31ae841
commit
eaf97418be
@ -316,7 +316,12 @@ type LightningWallet struct {
|
||||
// monotonically integer. All requests concerning the channel MUST
|
||||
// carry a valid, active funding ID.
|
||||
fundingLimbo map[uint64]*ChannelReservation
|
||||
limboMtx sync.RWMutex
|
||||
|
||||
// reservationIDs maps a pending channel ID to the reservation ID used
|
||||
// as key in the fundingLimbo map. Used to easily look up a channel
|
||||
// reservation given a pending channel ID.
|
||||
reservationIDs map[[32]byte]uint64
|
||||
limboMtx sync.RWMutex
|
||||
|
||||
// lockedOutPoints is a set of the currently locked outpoint. This
|
||||
// information is kept in order to provide an easy way to unlock all
|
||||
@ -347,6 +352,7 @@ func NewLightningWallet(Cfg Config) (*LightningWallet, error) {
|
||||
msgChan: make(chan interface{}, msgBufferSize),
|
||||
nextFundingID: 0,
|
||||
fundingLimbo: make(map[uint64]*ChannelReservation),
|
||||
reservationIDs: make(map[[32]byte]uint64),
|
||||
lockedOutPoints: make(map[wire.OutPoint]struct{}),
|
||||
fundingIntents: make(map[[32]byte]chanfunding.Intent),
|
||||
quit: make(chan struct{}),
|
||||
@ -417,6 +423,7 @@ func (l *LightningWallet) LockedOutpoints() []*wire.OutPoint {
|
||||
func (l *LightningWallet) ResetReservations() {
|
||||
l.nextFundingID = 0
|
||||
l.fundingLimbo = make(map[uint64]*ChannelReservation)
|
||||
l.reservationIDs = make(map[[32]byte]uint64)
|
||||
|
||||
for outpoint := range l.lockedOutPoints {
|
||||
l.UnlockOutpoint(outpoint)
|
||||
@ -523,16 +530,16 @@ func (l *LightningWallet) RegisterFundingIntent(expectedID [32]byte,
|
||||
// PsbtFundingVerify looks up a previously registered funding intent by its
|
||||
// pending channel ID and tries to advance the state machine by verifying the
|
||||
// passed PSBT.
|
||||
func (l *LightningWallet) PsbtFundingVerify(pid [32]byte,
|
||||
func (l *LightningWallet) PsbtFundingVerify(pendingChanID [32]byte,
|
||||
packet *psbt.Packet) error {
|
||||
|
||||
l.intentMtx.Lock()
|
||||
defer l.intentMtx.Unlock()
|
||||
|
||||
intent, ok := l.fundingIntents[pid]
|
||||
intent, ok := l.fundingIntents[pendingChanID]
|
||||
if !ok {
|
||||
return fmt.Errorf("no funding intent found for "+
|
||||
"pendingChannelID(%x)", pid[:])
|
||||
"pendingChannelID(%x)", pendingChanID[:])
|
||||
}
|
||||
psbtIntent, ok := intent.(*chanfunding.PsbtIntent)
|
||||
if !ok {
|
||||
@ -543,7 +550,49 @@ func (l *LightningWallet) PsbtFundingVerify(pid [32]byte,
|
||||
return fmt.Errorf("error verifying PSBT: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
// Get the channel reservation for that corresponds to this pending
|
||||
// channel ID.
|
||||
l.limboMtx.Lock()
|
||||
pid, ok := l.reservationIDs[pendingChanID]
|
||||
if !ok {
|
||||
l.limboMtx.Unlock()
|
||||
return fmt.Errorf("no channel reservation found for "+
|
||||
"pendingChannelID(%x)", pendingChanID[:])
|
||||
}
|
||||
|
||||
pendingReservation, ok := l.fundingLimbo[pid]
|
||||
l.limboMtx.Unlock()
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("no channel reservation found for "+
|
||||
"reservation ID %v", pid)
|
||||
}
|
||||
|
||||
// Now the the PSBT has been verified, we can again check 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.
|
||||
if pendingReservation.partialState.ChanType.HasAnchors() &&
|
||||
intent.LocalFundingAmt() > 0 {
|
||||
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
|
||||
@ -727,6 +776,46 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
|
||||
thawHeight = shimIntent.ThawHeight()
|
||||
}
|
||||
|
||||
// Now that we have a funding intent, we'll check whether funding a
|
||||
// channel using it would violate our reserved value for anchor channel
|
||||
// fee bumping. We first get our current number of anchor channels.
|
||||
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.
|
||||
if req.CommitType == CommitmentTypeAnchorsZeroFeeHtlcTx &&
|
||||
fundingIntent.LocalFundingAmt() > 0 {
|
||||
numAnchors++
|
||||
}
|
||||
|
||||
// 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
|
||||
// funding tx ready, so this will always pass. We'll do another check
|
||||
// when the PSBT has been verified.
|
||||
err = l.WithCoinSelectLock(func() error {
|
||||
_, err := l.CheckReservedValue(
|
||||
fundingIntent.Inputs(), fundingIntent.Outputs(),
|
||||
numAnchors,
|
||||
)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
fundingIntent.Cancel()
|
||||
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
|
||||
// The total channel capacity will be the size of the funding output we
|
||||
// created plus the remote contribution.
|
||||
capacity := localFundingAmt + remoteFundingAmt
|
||||
@ -761,6 +850,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
|
||||
// request.
|
||||
l.limboMtx.Lock()
|
||||
l.fundingLimbo[id] = reservation
|
||||
l.reservationIDs[req.PendingChanID] = id
|
||||
l.limboMtx.Unlock()
|
||||
|
||||
// Funding reservation request successfully handled. The funding inputs
|
||||
@ -770,6 +860,51 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
|
||||
req.err <- nil
|
||||
}
|
||||
|
||||
// currentNumAnchorChans returns the current number of anchor channels the
|
||||
// wallet should be ready to fee bump if needed.
|
||||
func (l *LightningWallet) currentNumAnchorChans() (int, error) {
|
||||
// Count all anchor channels that are open or pending
|
||||
// open, or waiting close.
|
||||
chans, err := l.Cfg.Database.FetchAllChannels()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var numAnchors int
|
||||
for _, c := range chans {
|
||||
if c.ChanType.HasAnchors() {
|
||||
numAnchors++
|
||||
}
|
||||
}
|
||||
|
||||
// We also count pending close channels.
|
||||
pendingClosed, err := l.Cfg.Database.FetchClosedChannels(
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for _, c := range pendingClosed {
|
||||
c, err := l.Cfg.Database.FetchHistoricalChannel(
|
||||
&c.ChanPoint,
|
||||
)
|
||||
if err != nil {
|
||||
// We don't have a guarantee that all channels re found
|
||||
// in the historical channels bucket, so we continue.
|
||||
walletLog.Warnf("Unable to fetch historical "+
|
||||
"channel: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if c.ChanType.HasAnchors() {
|
||||
numAnchors++
|
||||
}
|
||||
}
|
||||
|
||||
return numAnchors, nil
|
||||
}
|
||||
|
||||
// CheckReservedValue checks whether publishing a transaction with the given
|
||||
// inputs and outputs would violate the value we reserve in the wallet for
|
||||
// bumping the fee of anchor channels. The numAnchorChans argument should be
|
||||
@ -984,6 +1119,7 @@ func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMs
|
||||
delete(l.fundingLimbo, req.pendingFundingID)
|
||||
|
||||
pid := pendingReservation.pendingChanID
|
||||
delete(l.reservationIDs, pid)
|
||||
|
||||
l.intentMtx.Lock()
|
||||
if intent, ok := l.fundingIntents[pid]; ok {
|
||||
@ -1534,6 +1670,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs
|
||||
// Funding complete, this entry can be removed from limbo.
|
||||
l.limboMtx.Lock()
|
||||
delete(l.fundingLimbo, res.reservationID)
|
||||
delete(l.reservationIDs, res.pendingChanID)
|
||||
l.limboMtx.Unlock()
|
||||
|
||||
l.intentMtx.Lock()
|
||||
|
Loading…
Reference in New Issue
Block a user