From fde27716d1b1af72f2da0d5170cefc0a23bdcdf5 Mon Sep 17 00:00:00 2001 From: Andrey Samokhvalov Date: Mon, 22 May 2017 15:54:52 +0300 Subject: [PATCH] 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. --- lnwallet/channel.go | 67 +++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index d6629c72..07e06f7a 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -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{