channeldb: add restriction to to use only half of the capacity locally

In this commit severe bug have been fixed which allows the state of the
nodes to be desychnorinesed in the moments of high htlc flow. We limit
the number of the htlc which we can add to commitment transaction
to half of the available capcity. This change fixes the bug when
commimtment transaction on the verge of being full, in this case race
condition might occures and remote htlc will be rejected, but at the
same time they will be added on remote side, the same situiation will
happen with htlc we have added, which cause the commitment transactions
to be different.
This commit is contained in:
Andrey Samokhvalov 2017-05-22 15:54:52 +03:00 committed by Olaoluwa Osuntokun
parent f9b0626f8a
commit fde27716d1

@ -15,6 +15,8 @@ import (
"github.com/roasbeef/btcd/blockchain"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"encoding/hex"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/txscript"
"github.com/roasbeef/btcd/wire"
@ -1615,7 +1617,7 @@ func (lc *LightningChannel) SignNextCommitment() ([]byte, error) {
// party set up when we initially set up the channel. If we are, then
// we'll abort this state transition.
err := lc.validateCommitmentSanity(lc.remoteUpdateLog.ackedIndex,
lc.localUpdateLog.logIndex, false)
lc.localUpdateLog.logIndex, false, true, true)
if err != nil {
return nil, err
}
@ -1684,38 +1686,61 @@ func (lc *LightningChannel) SignNextCommitment() ([]byte, error) {
// also that all outputs are meet Bitcoin spec requirements and they are
// spendable.
func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
ourLogCounter uint64, prediction bool) error {
ourLogCounter uint64, prediction bool, local bool, remote bool) error {
htlcCount := 0
// If we adding or receiving the htlc we increase the number of htlcs
// by one in order to not overflow the commitment transasction by
// insertion.
if prediction {
htlcCount++
}
// Run through all the HTLCs that will be covered by this transaction
// in order to calculate theirs count.
htlcView := lc.fetchHTLCView(theirLogCounter, ourLogCounter)
view := lc.fetchHTLCView(theirLogCounter, ourLogCounter)
for _, entry := range htlcView.ourUpdates {
if entry.EntryType == Add {
htlcCount++
} else {
htlcCount--
if remote {
for _, entry := range view.theirUpdates {
if entry.EntryType == Add {
htlcCount++
}
}
for _, entry := range view.ourUpdates {
if entry.EntryType != Add {
htlcCount--
}
}
}
for _, entry := range htlcView.theirUpdates {
if entry.EntryType == Add {
htlcCount++
} else {
htlcCount--
if local {
for _, entry := range view.ourUpdates {
if entry.EntryType == Add {
htlcCount++
}
}
for _, entry := range view.theirUpdates {
if entry.EntryType != Add {
htlcCount--
}
}
}
if htlcCount > MaxHTLCNumber {
// In case of addition of htlc add update we should use the half
// of the capacity of the commitment transaction, if we use the full
// capacity it will lead to situioaton when we might reject the
// remote htlc update which will lead desynchronization of state.
var maxHTLCNumber int
if local && remote {
maxHTLCNumber = MaxHTLCNumber
} else {
maxHTLCNumber = MaxHTLCNumber / 2
}
if htlcCount > maxHTLCNumber {
return ErrMaxHTLCNumber
}
return nil
}
@ -1735,7 +1760,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(rawSig []byte) error {
// the constraints we specified during initial channel setup. If not,
// then we'll abort the channel as they've violated our constraints.
err := lc.validateCommitmentSanity(lc.remoteUpdateLog.logIndex,
lc.localUpdateLog.ackedIndex, false)
lc.localUpdateLog.ackedIndex, false, true, true)
if err != nil {
return err
}
@ -2092,7 +2117,7 @@ func (lc *LightningChannel) AddHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, error)
defer lc.Unlock()
if err := lc.validateCommitmentSanity(lc.remoteUpdateLog.logIndex,
lc.localUpdateLog.logIndex, true); err != nil {
lc.localUpdateLog.logIndex, true, true, false); err != nil {
return 0, err
}
@ -2119,7 +2144,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, err
defer lc.Unlock()
if err := lc.validateCommitmentSanity(lc.remoteUpdateLog.logIndex,
lc.localUpdateLog.logIndex, true); err != nil {
lc.localUpdateLog.logIndex, true, false, true); err != nil {
return 0, err
}
@ -2151,7 +2176,8 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte) (uint64, error) {
paymentHash := sha256.Sum256(preimage[:])
targetHTLCs, ok := lc.rHashMap[paymentHash]
if !ok {
return 0, fmt.Errorf("invalid payment hash")
return 0, fmt.Errorf("invalid payment hash(%v)",
hex.EncodeToString(paymentHash[:]))
}
targetHTLC := targetHTLCs[0]
@ -2189,7 +2215,8 @@ func (lc *LightningChannel) ReceiveHTLCSettle(preimage [32]byte, logIndex uint64
}
if !bytes.Equal(htlc.RHash[:], paymentHash[:]) {
return fmt.Errorf("invalid payment hash")
return fmt.Errorf("invalid payment hash(%v)",
hex.EncodeToString(paymentHash[:]))
}
pd := &PaymentDescriptor{