github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/backend/backend.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package backend
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"math/big"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/kisexp/xdchain/common"
    26  	"github.com/kisexp/xdchain/consensus"
    27  	"github.com/kisexp/xdchain/consensus/istanbul"
    28  	istanbulcommon "github.com/kisexp/xdchain/consensus/istanbul/common"
    29  	ibftcore "github.com/kisexp/xdchain/consensus/istanbul/ibft/core"
    30  	ibftengine "github.com/kisexp/xdchain/consensus/istanbul/ibft/engine"
    31  	qbftcore "github.com/kisexp/xdchain/consensus/istanbul/qbft/core"
    32  	qbftengine "github.com/kisexp/xdchain/consensus/istanbul/qbft/engine"
    33  	qbfttypes "github.com/kisexp/xdchain/consensus/istanbul/qbft/types"
    34  	"github.com/kisexp/xdchain/consensus/istanbul/validator"
    35  	"github.com/kisexp/xdchain/core"
    36  	"github.com/kisexp/xdchain/core/types"
    37  	"github.com/kisexp/xdchain/crypto"
    38  	"github.com/kisexp/xdchain/ethdb"
    39  	"github.com/kisexp/xdchain/event"
    40  	"github.com/kisexp/xdchain/log"
    41  	lru "github.com/hashicorp/golang-lru"
    42  )
    43  
    44  const (
    45  	// fetcherID is the ID indicates the block is from Istanbul engine
    46  	fetcherID = "istanbul"
    47  )
    48  
    49  // New creates an Ethereum backend for Istanbul core engine.
    50  func New(config *istanbul.Config, privateKey *ecdsa.PrivateKey, db ethdb.Database) *Backend {
    51  	// Allocate the snapshot caches and create the engine
    52  	recents, _ := lru.NewARC(inmemorySnapshots)
    53  	recentMessages, _ := lru.NewARC(inmemoryPeers)
    54  	knownMessages, _ := lru.NewARC(inmemoryMessages)
    55  
    56  	sb := &Backend{
    57  		config:           config,
    58  		istanbulEventMux: new(event.TypeMux),
    59  		privateKey:       privateKey,
    60  		address:          crypto.PubkeyToAddress(privateKey.PublicKey),
    61  		logger:           log.New(),
    62  		db:               db,
    63  		commitCh:         make(chan *types.Block, 1),
    64  		recents:          recents,
    65  		candidates:       make(map[common.Address]bool),
    66  		coreStarted:      false,
    67  		recentMessages:   recentMessages,
    68  		knownMessages:    knownMessages,
    69  	}
    70  
    71  	sb.qbftEngine = qbftengine.NewEngine(sb.config, sb.address, sb.Sign)
    72  	sb.ibftEngine = ibftengine.NewEngine(sb.config, sb.address, sb.Sign)
    73  
    74  	return sb
    75  }
    76  
    77  // ----------------------------------------------------------------------------
    78  
    79  type Backend struct {
    80  	config *istanbul.Config
    81  
    82  	privateKey *ecdsa.PrivateKey
    83  	address    common.Address
    84  
    85  	core istanbul.Core
    86  
    87  	ibftEngine *ibftengine.Engine
    88  	qbftEngine *qbftengine.Engine
    89  
    90  	istanbulEventMux *event.TypeMux
    91  
    92  	logger log.Logger
    93  
    94  	db ethdb.Database
    95  
    96  	chain        consensus.ChainHeaderReader
    97  	currentBlock func() *types.Block
    98  	hasBadBlock  func(hash common.Hash) bool
    99  
   100  	// the channels for istanbul engine notifications
   101  	commitCh          chan *types.Block
   102  	proposedBlockHash common.Hash
   103  	sealMu            sync.Mutex
   104  	coreStarted       bool
   105  	coreMu            sync.RWMutex
   106  
   107  	// Current list of candidates we are pushing
   108  	candidates map[common.Address]bool
   109  	// Protects the signer fields
   110  	candidatesLock sync.RWMutex
   111  	// Snapshots for recent block to speed up reorgs
   112  	recents *lru.ARCCache
   113  
   114  	// event subscription for ChainHeadEvent event
   115  	broadcaster consensus.Broadcaster
   116  
   117  	recentMessages *lru.ARCCache // the cache of peer's messages
   118  	knownMessages  *lru.ARCCache // the cache of self messages
   119  
   120  	qbftConsensusEnabled bool // qbft consensus
   121  }
   122  
   123  func (sb *Backend) Engine() istanbul.Engine {
   124  	return sb.EngineForBlockNumber(nil)
   125  }
   126  
   127  func (sb *Backend) EngineForBlockNumber(blockNumber *big.Int) istanbul.Engine {
   128  	switch {
   129  	case blockNumber != nil && sb.IsQBFTConsensusAt(blockNumber):
   130  		return sb.qbftEngine
   131  	case blockNumber == nil && sb.IsQBFTConsensus():
   132  		return sb.qbftEngine
   133  	default:
   134  		return sb.ibftEngine
   135  	}
   136  }
   137  
   138  // zekun: HACK
   139  func (sb *Backend) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
   140  	return sb.EngineForBlockNumber(parent.Number).CalcDifficulty(chain, time, parent)
   141  }
   142  
   143  // Address implements istanbul.Backend.Address
   144  func (sb *Backend) Address() common.Address {
   145  	return sb.Engine().Address()
   146  }
   147  
   148  // Validators implements istanbul.Backend.Validators
   149  func (sb *Backend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet {
   150  	return sb.getValidators(proposal.Number().Uint64(), proposal.Hash())
   151  }
   152  
   153  // Broadcast implements istanbul.Backend.Broadcast
   154  func (sb *Backend) Broadcast(valSet istanbul.ValidatorSet, code uint64, payload []byte) error {
   155  	// send to others
   156  	sb.Gossip(valSet, code, payload)
   157  	// send to self
   158  	msg := istanbul.MessageEvent{
   159  		Code:    code,
   160  		Payload: payload,
   161  	}
   162  	go sb.istanbulEventMux.Post(msg)
   163  	return nil
   164  }
   165  
   166  // Gossip implements istanbul.Backend.Gossip
   167  func (sb *Backend) Gossip(valSet istanbul.ValidatorSet, code uint64, payload []byte) error {
   168  	hash := istanbul.RLPHash(payload)
   169  	sb.knownMessages.Add(hash, true)
   170  
   171  	targets := make(map[common.Address]bool)
   172  	for _, val := range valSet.List() {
   173  		if val.Address() != sb.Address() {
   174  			targets[val.Address()] = true
   175  		}
   176  	}
   177  	if sb.broadcaster != nil && len(targets) > 0 {
   178  		ps := sb.broadcaster.FindPeers(targets)
   179  		for addr, p := range ps {
   180  			ms, ok := sb.recentMessages.Get(addr)
   181  			var m *lru.ARCCache
   182  			if ok {
   183  				m, _ = ms.(*lru.ARCCache)
   184  				if _, k := m.Get(hash); k {
   185  					// This peer had this event, skip it
   186  					continue
   187  				}
   188  			} else {
   189  				m, _ = lru.NewARC(inmemoryMessages)
   190  			}
   191  
   192  			m.Add(hash, true)
   193  			sb.recentMessages.Add(addr, m)
   194  
   195  			if sb.IsQBFTConsensus() {
   196  				var outboundCode uint64 = istanbulMsg
   197  				if _, ok := qbfttypes.MessageCodes()[code]; ok {
   198  					outboundCode = code
   199  				}
   200  				go p.SendQBFTConsensus(outboundCode, payload)
   201  			} else {
   202  				go p.SendConsensus(istanbulMsg, payload)
   203  			}
   204  		}
   205  	}
   206  	return nil
   207  }
   208  
   209  // Commit implements istanbul.Backend.Commit
   210  func (sb *Backend) Commit(proposal istanbul.Proposal, seals [][]byte, round *big.Int) (err error) {
   211  	// Check if the proposal is a valid block
   212  	block, ok := proposal.(*types.Block)
   213  	if !ok {
   214  		sb.logger.Error("BFT: invalid block proposal", "proposal", proposal)
   215  		return istanbulcommon.ErrInvalidProposal
   216  	}
   217  
   218  	// Commit header
   219  	h := block.Header()
   220  	err = sb.EngineForBlockNumber(h.Number).CommitHeader(h, seals, round)
   221  	if err != nil {
   222  		return
   223  	}
   224  
   225  	// Remove ValidatorSet added to ProposerPolicy registry, if not done, the registry keeps increasing size with each block height
   226  	sb.config.ProposerPolicy.ClearRegistry()
   227  
   228  	// update block's header
   229  	block = block.WithSeal(h)
   230  
   231  	sb.logger.Info("BFT: block proposal committed", "author", sb.Address(), "hash", proposal.Hash(), "number", proposal.Number().Uint64())
   232  
   233  	// - if the proposed and committed blocks are the same, send the proposed hash
   234  	//   to commit channel, which is being watched inside the engine.Seal() function.
   235  	// - otherwise, we try to insert the block.
   236  	// -- if success, the ChainHeadEvent event will be broadcasted, try to build
   237  	//    the next block and the previous Seal() will be stopped.
   238  	// -- otherwise, a error will be returned and a round change event will be fired.
   239  	if sb.proposedBlockHash == block.Hash() {
   240  		// feed block hash to Seal() and wait the Seal() result
   241  		sb.commitCh <- block
   242  		return nil
   243  	}
   244  
   245  	if sb.broadcaster != nil {
   246  		sb.broadcaster.Enqueue(fetcherID, block)
   247  	}
   248  
   249  	return nil
   250  }
   251  
   252  // EventMux implements istanbul.Backend.EventMux
   253  func (sb *Backend) EventMux() *event.TypeMux {
   254  	return sb.istanbulEventMux
   255  }
   256  
   257  // Verify implements istanbul.Backend.Verify
   258  func (sb *Backend) Verify(proposal istanbul.Proposal) (time.Duration, error) {
   259  	// Check if the proposal is a valid block
   260  	block, ok := proposal.(*types.Block)
   261  	if !ok {
   262  		sb.logger.Error("BFT: invalid block proposal", "proposal", proposal)
   263  		return 0, istanbulcommon.ErrInvalidProposal
   264  	}
   265  
   266  	// check bad block
   267  	if sb.HasBadProposal(block.Hash()) {
   268  		sb.logger.Warn("BFT: bad block proposal", "proposal", proposal)
   269  		return 0, core.ErrBlacklistedHash
   270  	}
   271  
   272  	header := block.Header()
   273  	snap, err := sb.snapshot(sb.chain, header.Number.Uint64()-1, header.ParentHash, nil)
   274  	if err != nil {
   275  		return 0, err
   276  	}
   277  
   278  	return sb.EngineForBlockNumber(header.Number).VerifyBlockProposal(sb.chain, block, snap.ValSet)
   279  }
   280  
   281  // Sign implements istanbul.Backend.Sign
   282  func (sb *Backend) Sign(data []byte) ([]byte, error) {
   283  	hashData := crypto.Keccak256(data)
   284  	return crypto.Sign(hashData, sb.privateKey)
   285  }
   286  
   287  // SignWithoutHashing implements istanbul.Backend.SignWithoutHashing and signs input data with the backend's private key without hashing the input data
   288  func (sb *Backend) SignWithoutHashing(data []byte) ([]byte, error) {
   289  	return crypto.Sign(data, sb.privateKey)
   290  }
   291  
   292  // CheckSignature implements istanbul.Backend.CheckSignature
   293  func (sb *Backend) CheckSignature(data []byte, address common.Address, sig []byte) error {
   294  	signer, err := istanbul.GetSignatureAddress(data, sig)
   295  	if err != nil {
   296  		return err
   297  	}
   298  	// Compare derived addresses
   299  	if signer != address {
   300  		return istanbulcommon.ErrInvalidSignature
   301  	}
   302  
   303  	return nil
   304  }
   305  
   306  // HasPropsal implements istanbul.Backend.HashBlock
   307  func (sb *Backend) HasPropsal(hash common.Hash, number *big.Int) bool {
   308  	return sb.chain.GetHeader(hash, number.Uint64()) != nil
   309  }
   310  
   311  // GetProposer implements istanbul.Backend.GetProposer
   312  func (sb *Backend) GetProposer(number uint64) common.Address {
   313  	if h := sb.chain.GetHeaderByNumber(number); h != nil {
   314  		a, _ := sb.Author(h)
   315  		return a
   316  	}
   317  	return common.Address{}
   318  }
   319  
   320  // ParentValidators implements istanbul.Backend.GetParentValidators
   321  func (sb *Backend) ParentValidators(proposal istanbul.Proposal) istanbul.ValidatorSet {
   322  	if block, ok := proposal.(*types.Block); ok {
   323  		return sb.getValidators(block.Number().Uint64()-1, block.ParentHash())
   324  	}
   325  	return validator.NewSet(nil, sb.config.ProposerPolicy)
   326  }
   327  
   328  func (sb *Backend) getValidators(number uint64, hash common.Hash) istanbul.ValidatorSet {
   329  	snap, err := sb.snapshot(sb.chain, number, hash, nil)
   330  	if err != nil {
   331  		return validator.NewSet(nil, sb.config.ProposerPolicy)
   332  	}
   333  	return snap.ValSet
   334  }
   335  
   336  func (sb *Backend) LastProposal() (istanbul.Proposal, common.Address) {
   337  	block := sb.currentBlock()
   338  
   339  	var proposer common.Address
   340  	if block.Number().Cmp(common.Big0) > 0 {
   341  		var err error
   342  		proposer, err = sb.Author(block.Header())
   343  		if err != nil {
   344  			sb.logger.Error("BFT: last block proposal invalid", "err", err)
   345  			return nil, common.Address{}
   346  		}
   347  	}
   348  
   349  	// Return header only block here since we don't need block body
   350  	return block, proposer
   351  }
   352  
   353  func (sb *Backend) HasBadProposal(hash common.Hash) bool {
   354  	if sb.hasBadBlock == nil {
   355  		return false
   356  	}
   357  	return sb.hasBadBlock(hash)
   358  }
   359  
   360  func (sb *Backend) Close() error {
   361  	return nil
   362  }
   363  
   364  // IsQBFTConsensus returns whether qbft consensus should be used
   365  func (sb *Backend) IsQBFTConsensus() bool {
   366  	if sb.qbftConsensusEnabled {
   367  		return true
   368  	}
   369  	if sb.chain != nil {
   370  		return sb.IsQBFTConsensusAt(sb.chain.CurrentHeader().Number)
   371  	}
   372  	return false
   373  }
   374  
   375  // IsQBFTConsensusForHeader checks if qbft consensus is enabled for the block height identified by the given header
   376  func (sb *Backend) IsQBFTConsensusAt(blockNumber *big.Int) bool {
   377  	return sb.config.IsQBFTConsensusAt(blockNumber)
   378  }
   379  
   380  func (sb *Backend) startIBFT() error {
   381  	sb.logger.Info("BFT: activate IBFT")
   382  	sb.logger.Trace("BFT: set ProposerPolicy sorter to ValidatorSortByStringFun")
   383  	sb.config.ProposerPolicy.Use(istanbul.ValidatorSortByString())
   384  	sb.qbftConsensusEnabled = false
   385  
   386  	sb.core = ibftcore.New(sb, sb.config)
   387  	if err := sb.core.Start(); err != nil {
   388  		sb.logger.Error("BFT: failed to activate IBFT", "err", err)
   389  		return err
   390  	}
   391  
   392  	return nil
   393  }
   394  
   395  func (sb *Backend) startQBFT() error {
   396  	sb.logger.Info("BFT: activate QBFT")
   397  	sb.logger.Trace("BFT: set ProposerPolicy sorter to ValidatorSortByByteFunc")
   398  	sb.config.ProposerPolicy.Use(istanbul.ValidatorSortByByte())
   399  	sb.qbftConsensusEnabled = true
   400  
   401  	sb.core = qbftcore.New(sb, sb.config)
   402  	if err := sb.core.Start(); err != nil {
   403  		sb.logger.Error("BFT: failed to activate QBFT", "err", err)
   404  		return err
   405  	}
   406  
   407  	return nil
   408  }
   409  
   410  func (sb *Backend) stop() error {
   411  	core := sb.core
   412  	sb.core = nil
   413  
   414  	if core != nil {
   415  		sb.logger.Info("BFT: deactivate")
   416  		if err := core.Stop(); err != nil {
   417  			sb.logger.Error("BFT: failed to deactivate", "err", err)
   418  			return err
   419  		}
   420  	}
   421  
   422  	sb.qbftConsensusEnabled = false
   423  
   424  	return nil
   425  }
   426  
   427  // StartQBFTConsensus stops existing legacy ibft consensus and starts the new qbft consensus
   428  func (sb *Backend) StartQBFTConsensus() error {
   429  	sb.logger.Info("BFT: switch from IBFT to QBFT")
   430  	if err := sb.stop(); err != nil {
   431  		return err
   432  	}
   433  
   434  	return sb.startQBFT()
   435  }