mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
lnwallet: remove availableLocalBalance field, add new AvailableBalance method
In this commit we do away with the existing availableLocalBalance attribute and instead add a new, more accurate AvailableBalance method. The new method will compute the available balance within the channel , assuming a new state was created at the instance the method was called. This new method will now properly account for HTLC fees. AvailableBalance is now called within AddHTLC in order to ensure we don’t add any HTLC’s that are unable to be paid for from the PoV of the fees on the commitment transaction.
This commit is contained in:
parent
ded7c11bfe
commit
c8479410d2
@ -1304,10 +1304,6 @@ func NewLightningChannel(signer Signer, events chainntnfs.ChainNotifier,
|
|||||||
go lc.closeObserver(channelCloseNtfn)
|
go lc.closeObserver(channelCloseNtfn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the available local balance
|
|
||||||
s := lc.StateSnapshot()
|
|
||||||
lc.availableLocalBalance = s.LocalBalance
|
|
||||||
|
|
||||||
// Finally, we'll kick of the signature job pool to handle any upcoming
|
// Finally, we'll kick of the signature job pool to handle any upcoming
|
||||||
// commitment state generation and validation.
|
// commitment state generation and validation.
|
||||||
if lc.sigPool.Start(); err != nil {
|
if lc.sigPool.Start(); err != nil {
|
||||||
@ -3146,6 +3142,8 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
|
|||||||
htlcCount++
|
htlcCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(roasbeef): call availableBalance in here re-using htlcView
|
||||||
|
|
||||||
// Run through all the HTLCs that will be covered by this transaction
|
// Run through all the HTLCs that will be covered by this transaction
|
||||||
// in order to calculate theirs count.
|
// in order to calculate theirs count.
|
||||||
view := lc.fetchHTLCView(theirLogCounter, ourLogCounter)
|
view := lc.fetchHTLCView(theirLogCounter, ourLogCounter)
|
||||||
@ -3662,10 +3660,33 @@ func (lc *LightningChannel) AddHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, error)
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if lc.availableLocalBalance < htlc.Amount {
|
// To ensure that we can actually fully accept this new HTLC, we'll
|
||||||
|
// calculate the current available bandwidth, and subtract the value
|
||||||
|
// ofthe HTLC from it.
|
||||||
|
availableBalance := lc.availableBalance()
|
||||||
|
availableBalance -= htlc.Amount
|
||||||
|
|
||||||
|
feePerKw := lc.channelState.LocalCommitment.FeePerKw
|
||||||
|
dustLimit := lc.channelState.LocalChanCfg.DustLimit
|
||||||
|
htlcIsDust := htlcIsDust(
|
||||||
|
false, true, feePerKw, htlc.Amount.ToSatoshis(), dustLimit,
|
||||||
|
)
|
||||||
|
|
||||||
|
// If this HTLC is not dust, and we're the initiator, then we'll also
|
||||||
|
// subtract the amount we'll need to pay in fees for this HTLC.
|
||||||
|
if !htlcIsDust && lc.channelState.IsInitiator {
|
||||||
|
htlcFee := lnwire.NewMSatFromSatoshis(
|
||||||
|
btcutil.Amount((int64(feePerKw) * HtlcWeight) / 1000),
|
||||||
|
)
|
||||||
|
availableBalance -= htlcFee
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this value is negative, then we can't accept the HTLC, so we'll
|
||||||
|
// reject it with an error.
|
||||||
|
if availableBalance < 0 {
|
||||||
|
// TODO(roasbeef): also needs to respect reservation
|
||||||
return 0, ErrInsufficientBalance
|
return 0, ErrInsufficientBalance
|
||||||
}
|
}
|
||||||
lc.availableLocalBalance -= htlc.Amount
|
|
||||||
|
|
||||||
pd := &PaymentDescriptor{
|
pd := &PaymentDescriptor{
|
||||||
EntryType: Add,
|
EntryType: Add,
|
||||||
@ -3745,7 +3766,6 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte) (uint64,
|
|||||||
delete(lc.rHashMap, paymentHash)
|
delete(lc.rHashMap, paymentHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
lc.availableLocalBalance += pd.Amount
|
|
||||||
return targetHTLC.HtlcIndex, targetHTLC.Amount, nil
|
return targetHTLC.HtlcIndex, targetHTLC.Amount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3879,7 +3899,6 @@ func (lc *LightningChannel) ReceiveFailHTLC(htlcIndex uint64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
lc.remoteUpdateLog.appendUpdate(pd)
|
lc.remoteUpdateLog.appendUpdate(pd)
|
||||||
lc.availableLocalBalance += pd.Amount
|
|
||||||
|
|
||||||
return htlc.Amount, nil
|
return htlc.Amount, nil
|
||||||
}
|
}
|
||||||
@ -4532,6 +4551,141 @@ func (lc *LightningChannel) DeleteState(c *channeldb.ChannelCloseSummary) error
|
|||||||
return lc.channelState.CloseChannel(c)
|
return lc.channelState.CloseChannel(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AvailableBalance returns the current available balance within the channel.
|
||||||
|
// By available balance, we mean that if at this very instance s new commitment
|
||||||
|
// were to be created which evals all the log entries, what would our available
|
||||||
|
// balance me. This method is useful when deciding if a given channel can
|
||||||
|
// accept an HTLC in the multi-hop forwarding scenario.
|
||||||
|
func (lc *LightningChannel) AvailableBalance() lnwire.MilliSatoshi {
|
||||||
|
lc.RLock()
|
||||||
|
defer lc.RUnlock()
|
||||||
|
|
||||||
|
return lc.availableBalance()
|
||||||
|
}
|
||||||
|
|
||||||
|
// availableBalance is the private, non mutexed version of AvailableBalance.
|
||||||
|
// This method is provided so methods that already hold the lock can access
|
||||||
|
// this method.
|
||||||
|
func (lc *LightningChannel) availableBalance() lnwire.MilliSatoshi {
|
||||||
|
// First, we'll grab the current local balance. If we're the initiator
|
||||||
|
// of the channel then we paid the fees on the last commitment state,
|
||||||
|
// so we'll re apply those.
|
||||||
|
settledBalance := lc.channelState.LocalCommitment.LocalBalance
|
||||||
|
if lc.channelState.IsInitiator {
|
||||||
|
settledBalance += lnwire.NewMSatFromSatoshis(
|
||||||
|
lc.localCommitChain.tip().fee,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next we'll grab the current set of log updates that are still active
|
||||||
|
// and haven't been garbage collected.
|
||||||
|
htlcView := lc.fetchHTLCView(lc.remoteUpdateLog.logIndex,
|
||||||
|
lc.localUpdateLog.logIndex)
|
||||||
|
feePerKw := lc.channelState.LocalCommitment.FeePerKw
|
||||||
|
dustLimit := lc.channelState.LocalChanCfg.DustLimit
|
||||||
|
|
||||||
|
// We'll now re-compute the current weight of all the active HTLC's. We
|
||||||
|
// make sure to skip any HTLC's that would be dust on our version of
|
||||||
|
// the commitment transaction.
|
||||||
|
var totalHtlcWeight int64
|
||||||
|
for _, htlc := range lc.channelState.LocalCommitment.Htlcs {
|
||||||
|
if htlcIsDust(false, true, feePerKw, htlc.Amt.ToSatoshis(),
|
||||||
|
dustLimit) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
totalHtlcWeight += HtlcWeight
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next we'll run through our set of updates and modify the
|
||||||
|
// settledBalance and totalHtlcWeight fields accordingly.
|
||||||
|
for _, entry := range htlcView.ourUpdates {
|
||||||
|
switch {
|
||||||
|
|
||||||
|
// For any new HTLC's added as a part of this state, we'll
|
||||||
|
// subtract the total balance, and tally the weight increase if
|
||||||
|
// it isn't dust.
|
||||||
|
case entry.EntryType == Add && entry.addCommitHeightLocal == 0:
|
||||||
|
settledBalance -= entry.Amount
|
||||||
|
|
||||||
|
if htlcIsDust(false, true, feePerKw, entry.Amount.ToSatoshis(),
|
||||||
|
dustLimit) {
|
||||||
|
continue
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
totalHtlcWeight += HtlcWeight
|
||||||
|
|
||||||
|
// For any new HTLC's we newly settled as part of this state,
|
||||||
|
// we'll subtract the HTLC weight and increase our balance
|
||||||
|
// accordingly.
|
||||||
|
case entry.EntryType == Settle && entry.removeCommitHeightLocal == 0:
|
||||||
|
totalHtlcWeight -= HtlcWeight
|
||||||
|
|
||||||
|
settledBalance += entry.Amount
|
||||||
|
|
||||||
|
// For any new fails added as a part of this state, we'll
|
||||||
|
// subtract the weight of the HTLC we're failing.
|
||||||
|
case entry.EntryType == Fail && entry.removeCommitHeightLocal == 0:
|
||||||
|
fallthrough
|
||||||
|
case entry.EntryType == MalformedFail && entry.removeCommitHeightLocal == 0:
|
||||||
|
totalHtlcWeight -= HtlcWeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, entry := range htlcView.theirUpdates {
|
||||||
|
switch {
|
||||||
|
// If the remote party has an HTLC that will be included as
|
||||||
|
// part of this state, then we'll account for the additional
|
||||||
|
// weight of the HTLC.
|
||||||
|
case entry.EntryType == Add && entry.addCommitHeightLocal == 0:
|
||||||
|
if htlcIsDust(true, true, feePerKw, entry.Amount.ToSatoshis(),
|
||||||
|
dustLimit) {
|
||||||
|
continue
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
totalHtlcWeight += HtlcWeight
|
||||||
|
|
||||||
|
// If the remote party is settling one of our HTLC's for the
|
||||||
|
// first time as part of this state, then we'll subtract the
|
||||||
|
// weight of the HTLC.
|
||||||
|
case entry.EntryType == Settle && entry.removeCommitHeightLocal == 0:
|
||||||
|
totalHtlcWeight -= HtlcWeight
|
||||||
|
|
||||||
|
// For any HTLC's that they're failing as a part of the next,
|
||||||
|
// state, we'll subtract the weight of the HTLC and also credit
|
||||||
|
// ourselves back the value of the HTLC.
|
||||||
|
case entry.EntryType == Fail && entry.removeCommitHeightLocal == 0:
|
||||||
|
fallthrough
|
||||||
|
case entry.EntryType == MalformedFail && entry.removeCommitHeightLocal == 0:
|
||||||
|
totalHtlcWeight -= HtlcWeight
|
||||||
|
|
||||||
|
settledBalance += entry.Amount
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we subtracted dust HTLC's, then we'll need to reset the weight of
|
||||||
|
// the HTLCs back to zero.
|
||||||
|
if totalHtlcWeight < 0 {
|
||||||
|
totalHtlcWeight = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're the initiator then we need to pay fees for this state, so
|
||||||
|
// taking into account the number of active HTLC's we'll calculate the
|
||||||
|
// fee that must be paid.
|
||||||
|
if lc.channelState.IsInitiator {
|
||||||
|
totalCommitWeight := CommitWeight + totalHtlcWeight
|
||||||
|
additionalFee := lnwire.NewMSatFromSatoshis(
|
||||||
|
btcutil.Amount((int64(feePerKw) * totalCommitWeight) / 1000),
|
||||||
|
)
|
||||||
|
|
||||||
|
settledBalance -= additionalFee
|
||||||
|
}
|
||||||
|
|
||||||
|
return settledBalance
|
||||||
|
}
|
||||||
|
|
||||||
// StateSnapshot returns a snapshot of the current fully committed state within
|
// StateSnapshot returns a snapshot of the current fully committed state within
|
||||||
// the channel.
|
// the channel.
|
||||||
func (lc *LightningChannel) StateSnapshot() *channeldb.ChannelSnapshot {
|
func (lc *LightningChannel) StateSnapshot() *channeldb.ChannelSnapshot {
|
||||||
|
Loading…
Reference in New Issue
Block a user