github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/consensus/bft/backend/backend.go (about)

     1  package backend
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"math/big"
     6  	"sync"
     7  	"time"
     8  
     9  	lru "github.com/hashicorp/golang-lru"
    10  	"github.com/quickchainproject/quickchain/common"
    11  	"github.com/quickchainproject/quickchain/consensus"
    12  	"github.com/quickchainproject/quickchain/consensus/bft"
    13  	bftCore "github.com/quickchainproject/quickchain/consensus/bft/core"
    14  	"github.com/quickchainproject/quickchain/consensus/bft/validator"
    15  	"github.com/quickchainproject/quickchain/core"
    16  	"github.com/quickchainproject/quickchain/core/types"
    17  	"github.com/quickchainproject/quickchain/crypto"
    18  	"github.com/quickchainproject/quickchain/event"
    19  	"github.com/quickchainproject/quickchain/qctdb"
    20  	"github.com/quickchainproject/quickchain/log"
    21  )
    22  
    23  const (
    24  	// fetcherID is the ID indicates the block is from BFT engine
    25  	fetcherID = "bft"
    26  )
    27  
    28  // New creates an Ethereum backend for BFT core engine.
    29  func New(config *bft.Config, privateKey *ecdsa.PrivateKey, db qctdb.Database) consensus.BFT {
    30  	// Allocate the snapshot caches and create the engine
    31  	recents, _ := lru.NewARC(inmemorySnapshots)
    32  	recentMessages, _ := lru.NewARC(inmemoryPeers)
    33  	knownMessages, _ := lru.NewARC(inmemoryMessages)
    34  	backend := &backend{
    35  		config:           config,
    36  		bftEventMux: new(event.TypeMux),
    37  		privateKey:       privateKey,
    38  		address:          crypto.PubkeyToAddress(privateKey.PublicKey),
    39  		logger:           log.New(),
    40  		db:               db,
    41  		commitCh:         make(chan *types.Block, 1),
    42  		recents:          recents,
    43  		candidates:       make(map[common.Address]bool),
    44  		coreStarted:      false,
    45  		recentMessages:   recentMessages,
    46  		knownMessages:    knownMessages,
    47  	}
    48  	backend.core = bftCore.New(backend, backend.config)
    49  	return backend
    50  }
    51  
    52  // ----------------------------------------------------------------------------
    53  
    54  type backend struct {
    55  	config           *bft.Config
    56  	bftEventMux *event.TypeMux
    57  	privateKey       *ecdsa.PrivateKey
    58  	address          common.Address
    59  	core             bftCore.Engine
    60  	logger           log.Logger
    61  	db               qctdb.Database
    62  	chain            consensus.ChainReader
    63  	currentBlock     func() *types.Block
    64  	hasBadBlock      func(hash common.Hash) bool
    65  
    66  	// the channels for bft engine notifications
    67  	commitCh          chan *types.Block
    68  	proposedBlockHash common.Hash
    69  	sealMu            sync.Mutex
    70  	coreStarted       bool
    71  	coreMu            sync.RWMutex
    72  
    73  	// Current list of candidates we are pushing
    74  	candidates map[common.Address]bool
    75  	// Protects the signer fields
    76  	candidatesLock sync.RWMutex
    77  	// Snapshots for recent block to speed up reorgs
    78  	recents *lru.ARCCache
    79  
    80  	// event subscription for ChainHeadEvent event
    81  	broadcaster consensus.Broadcaster
    82  
    83  	recentMessages *lru.ARCCache // the cache of peer's messages
    84  	knownMessages  *lru.ARCCache // the cache of self messages
    85  }
    86  
    87  // Address implements bft.Backend.Address
    88  func (sb *backend) Address() common.Address {
    89  	return sb.address
    90  }
    91  
    92  // Validators implements bft.Backend.Validators
    93  func (sb *backend) Validators(proposal bft.Proposal) bft.ValidatorSet {
    94  	return sb.getValidators(proposal.Number().Uint64(), proposal.Hash())
    95  }
    96  
    97  // Broadcast implements bft.Backend.Broadcast
    98  func (sb *backend) Broadcast(valSet bft.ValidatorSet, payload []byte) error {
    99  	// send to others
   100  	sb.Gossip(valSet, payload)
   101  	// send to self
   102  	msg := bft.MessageEvent{
   103  		Payload: payload,
   104  	}
   105  	go sb.bftEventMux.Post(msg)
   106  	return nil
   107  }
   108  
   109  // Broadcast implements bft.Backend.Gossip
   110  func (sb *backend) Gossip(valSet bft.ValidatorSet, payload []byte) error {
   111  	hash := bft.RLPHash(payload)
   112  	sb.knownMessages.Add(hash, true)
   113  
   114  	targets := make(map[common.Address]bool)
   115  	for _, val := range valSet.List() {
   116  		if val.Address() != sb.Address() {
   117  			targets[val.Address()] = true
   118  		}
   119  	}
   120  
   121  	if sb.broadcaster != nil && len(targets) > 0 {
   122  		ps := sb.broadcaster.FindPeers(targets)
   123  		for addr, p := range ps {
   124  			ms, ok := sb.recentMessages.Get(addr)
   125  			var m *lru.ARCCache
   126  			if ok {
   127  				m, _ = ms.(*lru.ARCCache)
   128  				if _, k := m.Get(hash); k {
   129  					// This peer had this event, skip it
   130  					continue
   131  				}
   132  			} else {
   133  				m, _ = lru.NewARC(inmemoryMessages)
   134  			}
   135  
   136  			m.Add(hash, true)
   137  			sb.recentMessages.Add(addr, m)
   138  
   139  			go p.Send(bftMsg, payload)
   140  		}
   141  	}
   142  	return nil
   143  }
   144  
   145  // Commit implements bft.Backend.Commit
   146  func (sb *backend) Commit(proposal bft.Proposal, seals [][]byte) error {
   147  	// Check if the proposal is a valid block
   148  	block := &types.Block{}
   149  	block, ok := proposal.(*types.Block)
   150  	if !ok {
   151  		sb.logger.Error("Invalid proposal, %v", proposal)
   152  		return errInvalidProposal
   153  	}
   154  
   155  	h := block.Header()
   156  	// Append seals into extra-data
   157  	err := writeCommittedSeals(h, seals)
   158  	if err != nil {
   159  		return err
   160  	}
   161  	// update block's header
   162  	block = block.WithSeal(h)
   163  
   164  	sb.logger.Info("Committed", "address", sb.Address(), "hash", proposal.Hash(), "number", proposal.Number().Uint64())
   165  	// - if the proposed and committed blocks are the same, send the proposed hash
   166  	//   to commit channel, which is being watched inside the engine.Seal() function.
   167  	// - otherwise, we try to insert the block.
   168  	// -- if success, the ChainHeadEvent event will be broadcasted, try to build
   169  	//    the next block and the previous Seal() will be stopped.
   170  	// -- otherwise, a error will be returned and a round change event will be fired.
   171  	if sb.proposedBlockHash == block.Hash() {
   172  		// feed block hash to Seal() and wait the Seal() result
   173  		sb.commitCh <- block
   174  		return nil
   175  	}
   176  
   177  	if sb.broadcaster != nil {
   178  		sb.broadcaster.Enqueue(fetcherID, block)
   179  	}
   180  	return nil
   181  }
   182  
   183  // EventMux implements bft.Backend.EventMux
   184  func (sb *backend) EventMux() *event.TypeMux {
   185  	return sb.bftEventMux
   186  }
   187  
   188  // Verify implements bft.Backend.Verify
   189  func (sb *backend) Verify(proposal bft.Proposal) (time.Duration, error) {
   190  	// Check if the proposal is a valid block
   191  	block := &types.Block{}
   192  	block, ok := proposal.(*types.Block)
   193  	if !ok {
   194  		sb.logger.Error("Invalid proposal, %v", proposal)
   195  		return 0, errInvalidProposal
   196  	}
   197  
   198  	// check bad block
   199  	if sb.HasBadProposal(block.Hash()) {
   200  		return 0, core.ErrBlacklistedHash
   201  	}
   202  
   203  	// check block body
   204  	txnHash := types.DeriveSha(block.Transactions())
   205  	uncleHash := types.CalcUncleHash(block.Uncles())
   206  	if txnHash != block.Header().TxHash {
   207  		return 0, errMismatchTxhashes
   208  	}
   209  	if uncleHash != nilUncleHash {
   210  		return 0, errInvalidUncleHash
   211  	}
   212  
   213  	// verify the header of proposed block
   214  	err := sb.VerifyHeader(sb.chain, block.Header(), false)
   215  	// ignore errEmptyCommittedSeals error because we don't have the committed seals yet
   216  	if err == nil || err == errEmptyCommittedSeals {
   217  		return 0, nil
   218  	} else if err == consensus.ErrFutureBlock {
   219  		return time.Unix(block.Header().Time.Int64(), 0).Sub(now()), consensus.ErrFutureBlock
   220  	}
   221  	return 0, err
   222  }
   223  
   224  // Sign implements bft.Backend.Sign
   225  func (sb *backend) Sign(data []byte) ([]byte, error) {
   226  	hashData := crypto.Keccak256([]byte(data))
   227  	return crypto.Sign(hashData, sb.privateKey)
   228  }
   229  
   230  // CheckSignature implements bft.Backend.CheckSignature
   231  func (sb *backend) CheckSignature(data []byte, address common.Address, sig []byte) error {
   232  	signer, err := bft.GetSignatureAddress(data, sig)
   233  	if err != nil {
   234  		log.Error("Failed to get signer address", "err", err)
   235  		return err
   236  	}
   237  	// Compare derived addresses
   238  	if signer != address {
   239  		return errInvalidSignature
   240  	}
   241  	return nil
   242  }
   243  
   244  // HasPropsal implements bft.Backend.HashBlock
   245  func (sb *backend) HasPropsal(hash common.Hash, number *big.Int) bool {
   246  	return sb.chain.GetHeader(hash, number.Uint64()) != nil
   247  }
   248  
   249  // GetProposer implements bft.Backend.GetProposer
   250  func (sb *backend) GetProposer(number uint64) common.Address {
   251  	if h := sb.chain.GetHeaderByNumber(number); h != nil {
   252  		a, _ := sb.Author(h)
   253  		return a
   254  	}
   255  	return common.Address{}
   256  }
   257  
   258  // ParentValidators implements bft.Backend.GetParentValidators
   259  func (sb *backend) ParentValidators(proposal bft.Proposal) bft.ValidatorSet {
   260  	if block, ok := proposal.(*types.Block); ok {
   261  		return sb.getValidators(block.Number().Uint64()-1, block.ParentHash())
   262  	}
   263  	return validator.NewSet(nil, sb.config.ProposerPolicy)
   264  }
   265  
   266  func (sb *backend) getValidators(number uint64, hash common.Hash) bft.ValidatorSet {
   267  	snap, err := sb.snapshot(sb.chain, number, hash, nil)
   268  	if err != nil {
   269  		return validator.NewSet(nil, sb.config.ProposerPolicy)
   270  	}
   271  	return snap.ValSet
   272  }
   273  
   274  func (sb *backend) LastProposal() (bft.Proposal, common.Address) {
   275  	block := sb.currentBlock()
   276  
   277  	var proposer common.Address
   278  	if block.Number().Cmp(common.Big0) > 0 {
   279  		var err error
   280  		proposer, err = sb.Author(block.Header())
   281  		if err != nil {
   282  			sb.logger.Error("Failed to get block proposer", "err", err)
   283  			return nil, common.Address{}
   284  		}
   285  	}
   286  
   287  	// Return header only block here since we don't need block body
   288  	return block, proposer
   289  }
   290  
   291  func (sb *backend) HasBadProposal(hash common.Hash) bool {
   292  	if sb.hasBadBlock == nil {
   293  		return false
   294  	}
   295  	return sb.hasBadBlock(hash)
   296  }