OSDN Git Service

keep last irreversible block on the main chain (#245)
authorapolloww <32606824+apolloww@users.noreply.github.com>
Mon, 8 Jul 2019 05:42:55 +0000 (13:42 +0800)
committerPaladz <yzhu101@uottawa.ca>
Mon, 8 Jul 2019 05:42:55 +0000 (13:42 +0800)
* keep last irreversible block on the main chain

Only update the LIB pointer when newly irreversible block is on the main
chain. Emit warning otherwise.

* save updated block header

netsync/chainmgr/block_keeper.go
netsync/chainmgr/handle.go
protocol/bbft.go
protocol/block.go
protocol/protocol.go
test/mock/chain.go

index d1407e9..24b4ef3 100644 (file)
@@ -190,7 +190,7 @@ func (bk *blockKeeper) syncWorker() {
                                continue
                        }
 
-                       if err := bk.peers.BroadcastNewStatus(bk.chain.BestBlockHeader(), bk.chain.BestIrreversibleHeader()); err != nil {
+                       if err := bk.peers.BroadcastNewStatus(bk.chain.BestBlockHeader(), bk.chain.LastIrreversibleHeader()); err != nil {
                                log.WithFields(log.Fields{"module": logModule, "err": err}).Error("fail on syncWorker broadcast new status")
                        }
                case <-bk.quit:
index 2a77d01..6e37389 100644 (file)
@@ -25,7 +25,7 @@ const (
 // Chain is the interface for Bytom core
 type Chain interface {
        BestBlockHeader() *types.BlockHeader
-       BestIrreversibleHeader() *types.BlockHeader
+       LastIrreversibleHeader() *types.BlockHeader
        BestBlockHeight() uint64
        GetBlockByHash(*bc.Hash) (*types.Block, error)
        GetBlockByHeight(uint64) (*types.Block, error)
@@ -351,7 +351,7 @@ func (m *Manager) SendStatus(peer peers.BasePeer) error {
                return errors.New("invalid peer")
        }
 
-       if err := p.SendStatus(m.chain.BestBlockHeader(), m.chain.BestIrreversibleHeader()); err != nil {
+       if err := p.SendStatus(m.chain.BestBlockHeader(), m.chain.LastIrreversibleHeader()); err != nil {
                m.peers.RemovePeer(p.ID())
                return err
        }
index 03337a2..a34e703 100644 (file)
@@ -98,7 +98,7 @@ func (c *Chain) ProcessBlockSignature(signature, xPub []byte, blockHash *bc.Hash
 
 // validateSign verify the signatures of block, and return the number of correct signature
 // if some signature is invalid, they will be reset to nil
-// if the block has not the signature of blocker, it will return error
+// if the block does not have the signature of blocker, it will return error
 func (c *Chain) validateSign(block *types.Block) error {
        consensusNodeMap, err := c.getConsensusNodes(&block.PreviousBlockHash)
        if err != nil {
@@ -225,11 +225,25 @@ func (c *Chain) updateBlockSignature(blockHeader *types.BlockHeader, nodeOrder u
                return err
        }
 
-       if c.isIrreversible(blockHeader) && blockHeader.Height > c.bestIrrBlockHeader.Height {
+       if !c.isIrreversible(blockHeader) || blockHeader.Height <= c.lastIrrBlockHeader.Height {
+               return nil
+       }
+
+       if c.InMainChain(blockHeader.Hash()) {
                if err := c.store.SaveChainStatus(c.bestBlockHeader, blockHeader, []*types.BlockHeader{}, state.NewUtxoViewpoint(), []*state.ConsensusResult{}); err != nil {
                        return err
                }
-               c.bestIrrBlockHeader = blockHeader
+
+               c.lastIrrBlockHeader = blockHeader
+       } else {
+               // block is on a forked chain
+               log.WithFields(log.Fields{"module": logModule}).Info("majority votes received on forked chain")
+               tail, err := c.traceLongestChainTail(blockHeader)
+               if err != nil {
+                       return err
+               }
+
+               return c.reorganizeChain(tail)
        }
        return nil
 }
index d03497b..c1dc265 100644 (file)
@@ -110,7 +110,7 @@ func (c *Chain) connectBlock(block *types.Block) (err error) {
                return err
        }
 
-       irrBlockHeader := c.bestIrrBlockHeader
+       irrBlockHeader := c.lastIrrBlockHeader
        if c.isIrreversible(&block.BlockHeader) && block.Height > irrBlockHeader.Height {
                irrBlockHeader = &block.BlockHeader
        }
@@ -167,7 +167,7 @@ func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error {
                log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("detach from mainchain")
        }
 
-       irrBlockHeader := c.bestIrrBlockHeader
+       irrBlockHeader := c.lastIrrBlockHeader
        for _, attachBlockHeader := range attachBlockHeaders {
                attachHash := attachBlockHeader.Hash()
                b, err := c.store.GetBlock(&attachHash)
@@ -205,7 +205,9 @@ func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error {
                log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("attach from mainchain")
        }
 
-       if len(detachBlockHeaders) > 0 && detachBlockHeaders[len(detachBlockHeaders)-1].Height <= c.bestIrrBlockHeader.Height && irrBlockHeader.Height <= c.bestIrrBlockHeader.Height {
+       if len(detachBlockHeaders) > 0 &&
+               detachBlockHeaders[len(detachBlockHeaders)-1].Height <= c.lastIrrBlockHeader.Height &&
+               irrBlockHeader.Height <= c.lastIrrBlockHeader.Height {
                return errors.New("rollback block below the height of irreversible block")
        }
        consensusResults = append(consensusResults, consensusResult.Fork())
index 153312c..80cb53a 100644 (file)
@@ -26,8 +26,8 @@ type Chain struct {
        eventDispatcher *event.Dispatcher
 
        cond               sync.Cond
-       bestBlockHeader    *types.BlockHeader
-       bestIrrBlockHeader *types.BlockHeader
+       bestBlockHeader    *types.BlockHeader // the last block on current main chain
+       lastIrrBlockHeader *types.BlockHeader // the last irreversible block
 }
 
 // NewChain returns a new Chain using store as the underlying storage.
@@ -56,7 +56,7 @@ func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (*
                return nil, err
        }
 
-       c.bestIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
+       c.lastIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
        if err != nil {
                return nil, err
        }
@@ -102,7 +102,7 @@ func (c *Chain) BestBlockHeight() uint64 {
        return c.bestBlockHeader.Height
 }
 
-// BestBlockHash return the hash of the chain tail block
+// BestBlockHash return the hash of the main chain tail block
 func (c *Chain) BestBlockHash() *bc.Hash {
        c.cond.L.Lock()
        defer c.cond.L.Unlock()
@@ -110,11 +110,11 @@ func (c *Chain) BestBlockHash() *bc.Hash {
        return &bestHash
 }
 
-// BestIrreversibleHeader returns the chain best irreversible block header
-func (c *Chain) BestIrreversibleHeader() *types.BlockHeader {
+// LastIrreversibleHeader returns the chain last irreversible block header
+func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
        c.cond.L.Lock()
        defer c.cond.L.Unlock()
-       return c.bestIrrBlockHeader
+       return c.lastIrrBlockHeader
 }
 
 // BestBlockHeader returns the chain best block header
@@ -139,6 +139,33 @@ func (c *Chain) InMainChain(hash bc.Hash) bool {
        return *blockHash == hash
 }
 
+// trace back to the tail of the chain from the given block header
+func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) {
+       longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader}
+
+       for len(workQueue) > 0 {
+               currentHeader := workQueue[0]
+               currentHash := currentHeader.Hash()
+               workQueue = workQueue[1:]
+               hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1)
+               if err != nil {
+                       return nil, err
+               }
+
+               for _, h := range hashes {
+                       if header, err := c.store.GetBlockHeader(h); err != nil {
+                               return nil, err
+                       } else if header.PreviousBlockHash == currentHash {
+                               if longestTail.Height < header.Height {
+                                       longestTail = header
+                               }
+                               workQueue = append(workQueue, header)
+                       }
+               }
+       }
+       return longestTail, nil
+}
+
 // This function must be called with mu lock in above level
 func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, consensusResults []*state.ConsensusResult) error {
        if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, consensusResults); err != nil {
@@ -146,7 +173,7 @@ func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlo
        }
 
        c.bestBlockHeader = blockHeader
-       c.bestIrrBlockHeader = irrBlockHeader
+       c.lastIrrBlockHeader = irrBlockHeader
 
        blockHash := blockHeader.Hash()
        log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update")
index 2b85ff0..700891f 100644 (file)
@@ -37,7 +37,7 @@ func (c *Chain) BestBlockHeight() uint64 {
        return c.bestBlockHeader.Height
 }
 
-func (c *Chain) BestIrreversibleHeader() *types.BlockHeader {
+func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
        return c.bestBlockHeader
 }