From: apolloww <32606824+apolloww@users.noreply.github.com> Date: Mon, 8 Jul 2019 05:42:55 +0000 (+0800) Subject: keep last irreversible block on the main chain (#245) X-Git-Tag: v1.0.5~174 X-Git-Url: http://git.osdn.net/view?p=bytom%2Fvapor.git;a=commitdiff_plain;h=3bf61490e8df4a17777877b56176ce608f4ccc26 keep last irreversible block on the main chain (#245) * 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 --- diff --git a/netsync/chainmgr/block_keeper.go b/netsync/chainmgr/block_keeper.go index d1407e95..24b4ef37 100644 --- a/netsync/chainmgr/block_keeper.go +++ b/netsync/chainmgr/block_keeper.go @@ -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: diff --git a/netsync/chainmgr/handle.go b/netsync/chainmgr/handle.go index 2a77d013..6e373899 100644 --- a/netsync/chainmgr/handle.go +++ b/netsync/chainmgr/handle.go @@ -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 } diff --git a/protocol/bbft.go b/protocol/bbft.go index 03337a2e..a34e7030 100644 --- a/protocol/bbft.go +++ b/protocol/bbft.go @@ -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 } diff --git a/protocol/block.go b/protocol/block.go index d03497b5..c1dc2651 100644 --- a/protocol/block.go +++ b/protocol/block.go @@ -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()) diff --git a/protocol/protocol.go b/protocol/protocol.go index 153312ca..80cb53a1 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -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") diff --git a/test/mock/chain.go b/test/mock/chain.go index 2b85ff03..700891f5 100644 --- a/test/mock/chain.go +++ b/test/mock/chain.go @@ -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 }