123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610 |
- // Copyright 2017 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package backend
- import (
- "fmt"
- "math/big"
- "math/rand"
- "time"
- "github.com/ethereum/go-ethereum/accounts/abi/bind"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/consensus/istanbul"
- "github.com/ethereum/go-ethereum/consensus/istanbul/backend/contract"
- istanbulcommon "github.com/ethereum/go-ethereum/consensus/istanbul/common"
- "github.com/ethereum/go-ethereum/consensus/istanbul/validator"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rpc"
- )
- const (
- checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database
- inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
- inmemoryPeers = 40
- inmemoryMessages = 1024
- )
- // Author retrieves the Ethereum address of the account that minted the given
- // block, which may be different from the header's coinbase if a consensus
- // engine is based on signatures.
- func (sb *Backend) Author(header *types.Header) (common.Address, error) {
- return sb.EngineForBlockNumber(header.Number).Author(header)
- }
- // Signers extracts all the addresses who have signed the given header
- // It will extract for each seal who signed it, regardless of if the seal is
- // repeated
- func (sb *Backend) Signers(header *types.Header) ([]common.Address, error) {
- return sb.EngineForBlockNumber(header.Number).Signers(header)
- }
- // VerifyHeader checks whether a header conforms to the consensus rules of a
- // given engine. Verifying the seal may be done optionally here, or explicitly
- // via the VerifySeal method.
- func (sb *Backend) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
- return sb.verifyHeader(chain, header, nil)
- }
- func (sb *Backend) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
- // Assemble the voting snapshot
- snap, err := sb.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, parents)
- if err != nil {
- return err
- }
- return sb.EngineForBlockNumber(header.Number).VerifyHeader(chain, header, parents, snap.ValSet)
- }
- // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
- // concurrently. The method returns a quit channel to abort the operations and
- // a results channel to retrieve the async verifications (the order is that of
- // the input slice).
- func (sb *Backend) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
- abort := make(chan struct{})
- results := make(chan error, len(headers))
- go func() {
- errored := false
- for i, header := range headers {
- var err error
- if errored {
- err = consensus.ErrUnknownAncestor
- } else {
- err = sb.verifyHeader(chain, header, headers[:i])
- }
- if err != nil {
- errored = true
- }
- select {
- case <-abort:
- return
- case results <- err:
- }
- }
- }()
- return abort, results
- }
- // VerifyUncles verifies that the given block's uncles conform to the consensus
- // rules of a given engine.
- func (sb *Backend) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
- return sb.EngineForBlockNumber(block.Header().Number).VerifyUncles(chain, block)
- }
- // VerifySeal checks whether the crypto seal on a header is valid according to
- // the consensus rules of the given engine.
- func (sb *Backend) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
- // get parent header and ensure the signer is in parent's validator set
- number := header.Number.Uint64()
- if number == 0 {
- return istanbulcommon.ErrUnknownBlock
- }
- // Assemble the voting snapshot
- snap, err := sb.snapshot(chain, number-1, header.ParentHash, nil)
- if err != nil {
- return err
- }
- return sb.EngineForBlockNumber(header.Number).VerifySeal(chain, header, snap.ValSet)
- }
- // Prepare initializes the consensus fields of a block header according to the
- // rules of a particular engine. The changes are executed inline.
- func (sb *Backend) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
- // Assemble the voting snapshot
- snap, err := sb.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, nil)
- if err != nil {
- return err
- }
- err = sb.EngineForBlockNumber(header.Number).Prepare(chain, header, snap.ValSet)
- if err != nil {
- return err
- }
- // get valid candidate list
- sb.candidatesLock.RLock()
- var addresses []common.Address
- var authorizes []bool
- for address, authorize := range sb.candidates {
- if snap.checkVote(address, authorize) {
- addresses = append(addresses, address)
- authorizes = append(authorizes, authorize)
- }
- }
- sb.candidatesLock.RUnlock()
- if len(addresses) > 0 {
- index := rand.Intn(len(addresses))
- err = sb.EngineForBlockNumber(header.Number).WriteVote(header, addresses[index], authorizes[index])
- if err != nil {
- log.Error("BFT: error writing validator vote", "err", err)
- return err
- }
- }
- return nil
- }
- // Finalize runs any post-transaction state modifications (e.g. block rewards)
- // and assembles the final block.
- //
- // Note, the block header and state database might be updated to reflect any
- // consensus rules that happen at finalization (e.g. block rewards).
- func (sb *Backend) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
- sb.EngineForBlockNumber(header.Number).Finalize(chain, header, state, txs, uncles)
- }
- // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
- // nor block rewards given, and returns the final block.
- func (sb *Backend) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
- return sb.EngineForBlockNumber(header.Number).FinalizeAndAssemble(chain, header, state, txs, uncles, receipts)
- }
- // Seal generates a new block for the given input block with the local miner's
- // seal place on top.
- func (sb *Backend) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
- // update the block header timestamp and signature and propose the block to core engine
- header := block.Header()
- number := header.Number.Uint64()
- // Bail out if we're unauthorized to sign a block
- snap, err := sb.snapshot(chain, number-1, header.ParentHash, nil)
- if err != nil {
- return err
- }
- block, err = sb.EngineForBlockNumber(header.Number).Seal(chain, block, snap.ValSet)
- if err != nil {
- return err
- }
- delay := time.Until(time.Unix(int64(block.Header().Time), 0))
- go func() {
- // wait for the timestamp of header, use this to adjust the block period
- select {
- case <-time.After(delay):
- case <-stop:
- results <- nil
- return
- }
- // get the proposed block hash and clear it if the seal() is completed.
- sb.sealMu.Lock()
- sb.proposedBlockHash = block.Hash()
- defer func() {
- sb.proposedBlockHash = common.Hash{}
- sb.sealMu.Unlock()
- }()
- // post block into Istanbul engine
- go sb.EventMux().Post(istanbul.RequestEvent{
- Proposal: block,
- })
- for {
- select {
- case result := <-sb.commitCh:
- // if the block hash and the hash from channel are the same,
- // return the result. Otherwise, keep waiting the next hash.
- if result != nil && block.Hash() == result.Hash() {
- results <- result
- return
- }
- case <-stop:
- results <- nil
- return
- }
- }
- }()
- return nil
- }
- // APIs returns the RPC APIs this consensus engine provides.
- func (sb *Backend) APIs(chain consensus.ChainHeaderReader) []rpc.API {
- return []rpc.API{{
- Namespace: "istanbul",
- Version: "1.0",
- Service: &API{chain: chain, backend: sb},
- Public: true,
- }}
- }
- // Start implements consensus.Istanbul.Start
- func (sb *Backend) Start(chain consensus.ChainHeaderReader, currentBlock func() *types.Block, hasBadBlock func(db ethdb.Reader, hash common.Hash) bool) error {
- sb.coreMu.Lock()
- defer sb.coreMu.Unlock()
- if sb.coreStarted {
- return istanbul.ErrStartedEngine
- }
- // clear previous data
- sb.proposedBlockHash = common.Hash{}
- if sb.commitCh != nil {
- close(sb.commitCh)
- }
- sb.commitCh = make(chan *types.Block, 1)
- sb.chain = chain
- sb.currentBlock = currentBlock
- sb.hasBadBlock = hasBadBlock
- // Check if qbft Consensus needs to be used after chain is set
- var err error
- if sb.IsQBFTConsensus() {
- log.Info("start QBFT")
- err = sb.startQBFT()
- } else {
- log.Info("start IBFT")
- err = sb.startIBFT()
- }
- if err != nil {
- return err
- }
- sb.coreStarted = true
- return nil
- }
- // Stop implements consensus.Istanbul.Stop
- func (sb *Backend) Stop() error {
- sb.coreMu.Lock()
- defer sb.coreMu.Unlock()
- if !sb.coreStarted {
- return istanbul.ErrStoppedEngine
- }
- if err := sb.stop(); err != nil {
- return err
- }
- sb.coreStarted = false
- return nil
- }
- func addrsToString(addrs []common.Address) []string {
- strs := make([]string, len(addrs))
- for i, addr := range addrs {
- strs[i] = addr.String()
- }
- return strs
- }
- func (sb *Backend) snapLogger(snap *Snapshot) log.Logger {
- return sb.logger.New(
- "snap.number", snap.Number,
- "snap.hash", snap.Hash.String(),
- "snap.epoch", snap.Epoch,
- "snap.validators", addrsToString(snap.validators()),
- "snap.votes", snap.Votes,
- )
- }
- func (sb *Backend) storeSnap(snap *Snapshot) error {
- logger := sb.snapLogger(snap)
- logger.Debug("BFT: store snapshot to database")
- if err := snap.store(sb.db); err != nil {
- logger.Error("BFT: failed to store snapshot to database", "err", err)
- return err
- }
- return nil
- }
- // snapshot retrieves the authorization snapshot at a given point in time.
- func (sb *Backend) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
- // Search for a snapshot in memory or on disk for checkpoints
- var (
- headers []*types.Header
- snap *Snapshot
- )
- for snap == nil {
- // If an in-memory snapshot was found, use that
- if s, ok := sb.recents.Get(hash); ok {
- snap = s.(*Snapshot)
- sb.snapLogger(snap).Trace("BFT: loaded voting snapshot from cache")
- break
- }
- // If an on-disk checkpoint snapshot can be found, use that
- if number%checkpointInterval == 0 {
- if s, err := loadSnapshot(sb.config.GetConfig(new(big.Int).SetUint64(number)).Epoch, sb.db, hash); err == nil {
- snap = s
- sb.snapLogger(snap).Trace("BFT: loaded voting snapshot from database")
- break
- }
- }
- // If we're at block zero, make a snapshot
- if number == 0 {
- genesis := chain.GetHeaderByNumber(0)
- if err := sb.EngineForBlockNumber(big.NewInt(0)).VerifyHeader(chain, genesis, nil, nil); err != nil {
- sb.logger.Error("BFT: invalid genesis block", "err", err)
- return nil, err
- }
- var validators []common.Address
- validatorContract := sb.config.GetValidatorContractAddress(big.NewInt(0))
- if validatorContract != (common.Address{}) && sb.config.GetValidatorSelectionMode(big.NewInt(0)) == params.ContractMode {
- validatorContractCaller, err := contract.NewValidatorContractInterfaceCaller(validatorContract, sb.config.Client)
- if err != nil {
- return nil, fmt.Errorf("invalid smart contract in genesis alloc: %w", err)
- }
- opts := bind.CallOpts{
- Pending: false,
- BlockNumber: big.NewInt(0),
- }
- validators, err = validatorContractCaller.GetValidators(&opts)
- log.Trace("BFT: Initialising snap with contract validators", "address", validatorContract, "validators", validators)
- if err != nil {
- log.Error("BFT: invalid smart contract in genesis alloc", "err", err)
- return nil, err
- }
- } else {
- validatorsFromConfig := sb.config.GetValidatorsAt(big.NewInt(0))
- if len(validatorsFromConfig) > 0 {
- validators = validatorsFromConfig
- log.Info("BFT: Initialising snap with config validators", "validators", validators)
- } else {
- var err error
- validators, err = sb.EngineForBlockNumber(big.NewInt(0)).ExtractGenesisValidators(genesis)
- log.Info("BFT: Initialising snap with extradata", "validators", validators)
- if err != nil {
- log.Error("BFT: invalid genesis block", "err", err)
- return nil, err
- }
- }
- }
- snap = newSnapshot(sb.config.GetConfig(new(big.Int).SetUint64(number)).Epoch, 0, genesis.Hash(), validator.NewSet(validators, sb.config.ProposerPolicy))
- if err := sb.storeSnap(snap); err != nil {
- return nil, err
- }
- break
- }
- // No snapshot for this header, gather the header and move backward
- var header *types.Header
- if len(parents) > 0 {
- // If we have explicit parents, pick from there (enforced)
- header = parents[len(parents)-1]
- if header.Hash() != hash || header.Number.Uint64() != number {
- return nil, consensus.ErrUnknownAncestor
- }
- parents = parents[:len(parents)-1]
- } else {
- // No explicit parents (or no more left), reach out to the database
- header = chain.GetHeader(hash, number)
- if header == nil {
- return nil, consensus.ErrUnknownAncestor
- }
- }
- headers = append(headers, header)
- number, hash = number-1, header.ParentHash
- }
- // Previous snapshot found, apply any pending headers on top of it
- for i := 0; i < len(headers)/2; i++ {
- headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
- }
- snap, err := sb.snapApply(snap, headers)
- if err != nil {
- return nil, err
- }
- sb.recents.Add(snap.Hash, snap)
- targetBlockHeight := new(big.Int).SetUint64(number)
- validatorContract := sb.config.GetValidatorContractAddress(targetBlockHeight)
- // we only need to update the validator set if it's a new block
- if len(headers) == 0 && validatorContract != (common.Address{}) && sb.config.GetValidatorSelectionMode(targetBlockHeight) == params.ContractMode {
- sb.logger.Trace("Applying snap with smart contract validators", "address", validatorContract, "client", sb.config.Client)
- validatorContractCaller, err := contract.NewValidatorContractInterfaceCaller(validatorContract, sb.config.Client)
- if err != nil {
- return nil, fmt.Errorf("BFT: invalid smart contract in genesis alloc: %w", err)
- }
- opts := bind.CallOpts{
- Pending: false,
- BlockNumber: targetBlockHeight,
- }
- validators, err := validatorContractCaller.GetValidators(&opts)
- if err != nil {
- log.Error("BFT: invalid validator smart contract", "err", err)
- return nil, err
- }
- sb.logger.Trace("Fetched validators from smart contract", "validators", validators)
- valSet := validator.NewSet(validators, sb.config.ProposerPolicy)
- snap.ValSet = valSet
- } else if validatorsFromTransitions := sb.config.GetValidatorsAt(targetBlockHeight); len(validatorsFromTransitions) > 0 && sb.config.GetValidatorSelectionMode(targetBlockHeight) == params.BlockHeaderMode {
- //Note! we only want to set this once at this block height. Subsequent blocks will be propagated with the same
- // validator as they are copied into the block header on the next block. Then normal voting can take place
- // again.
- valSet := validator.NewSet(validatorsFromTransitions, sb.config.ProposerPolicy)
- snap.ValSet = valSet
- }
- // If we've generated a new checkpoint snapshot, save to disk
- if snap.Number%checkpointInterval == 0 && len(headers) > 0 {
- if err = sb.storeSnap(snap); err != nil {
- return nil, err
- }
- }
- return snap, err
- }
- // SealHash returns the hash of a block prior to it being sealed.
- func (sb *Backend) SealHash(header *types.Header) common.Hash {
- return sb.EngineForBlockNumber(header.Number).SealHash(header)
- }
- func (sb *Backend) snapApply(snap *Snapshot, headers []*types.Header) (*Snapshot, error) {
- // Allow passing in no headers for cleaner code
- if len(headers) == 0 {
- return snap, nil
- }
- // Sanity check that the headers can be applied
- for i := 0; i < len(headers)-1; i++ {
- if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
- return nil, istanbulcommon.ErrInvalidVotingChain
- }
- }
- if headers[0].Number.Uint64() != snap.Number+1 {
- return nil, istanbulcommon.ErrInvalidVotingChain
- }
- // Iterate through the headers and create a new snapshot
- snapCpy := snap.copy()
- for _, header := range headers {
- err := sb.snapApplyHeader(snapCpy, header)
- if err != nil {
- return nil, err
- }
- }
- snapCpy.Number += uint64(len(headers))
- snapCpy.Hash = headers[len(headers)-1].Hash()
- return snapCpy, nil
- }
- func (sb *Backend) snapApplyHeader(snap *Snapshot, header *types.Header) error {
- logger := sb.snapLogger(snap).New("header.number", header.Number.Uint64(), "header.hash", header.Hash().String())
- logger.Trace("BFT: apply header to voting snapshot")
- // Remove any votes on checkpoint blocks
- number := header.Number.Uint64()
- if number%snap.Epoch == 0 {
- snap.Votes = nil
- snap.Tally = make(map[common.Address]Tally)
- }
- // Resolve the authorization key and check against validators
- validator, err := sb.EngineForBlockNumber(header.Number).Author(header)
- if err != nil {
- logger.Error("BFT: invalid header author", "err", err)
- return err
- }
- logger = logger.New("header.author", validator)
- if _, v := snap.ValSet.GetByAddress(validator); v == nil {
- logger.Error("BFT: header author is not a validator", "Validators", snap.ValSet, "Author", validator)
- return istanbulcommon.ErrUnauthorized
- }
- // Read vote from header
- candidate, authorize, err := sb.EngineForBlockNumber(header.Number).ReadVote(header)
- if err != nil {
- logger.Error("BFT: invalid header vote", "err", err)
- return err
- }
- logger = logger.New("candidate", candidate.String(), "authorize", authorize)
- // Header authorized, discard any previous votes from the validator
- for i, vote := range snap.Votes {
- if vote.Validator == validator && vote.Address == candidate {
- logger.Trace("BFT: discard previous vote from tally", "old.authorize", vote.Authorize)
- // Uncast the vote from the cached tally
- snap.uncast(vote.Address, vote.Authorize)
- // Uncast the vote from the chronological list
- snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
- break // only one vote allowed
- }
- }
- logger.Debug("BFT: add vote to tally")
- if snap.cast(candidate, authorize) {
- snap.Votes = append(snap.Votes, &Vote{
- Validator: validator,
- Block: number,
- Address: candidate,
- Authorize: authorize,
- })
- }
- // If the vote passed, update the list of validators
- if tally := snap.Tally[candidate]; tally.Votes > snap.ValSet.Size()/2 {
- if tally.Authorize {
- logger.Info("BFT: reached majority to add validator")
- snap.ValSet.AddValidator(candidate)
- } else {
- logger.Info("BFT: reached majority to remove validator")
- snap.ValSet.RemoveValidator(candidate)
- // Discard any previous votes the deauthorized validator cast
- for i := 0; i < len(snap.Votes); i++ {
- if snap.Votes[i].Validator == candidate {
- // Uncast the vote from the cached tally
- snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize)
- // Uncast the vote from the chronological list
- snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
- i--
- }
- }
- }
- // Discard any previous votes around the just changed account
- for i := 0; i < len(snap.Votes); i++ {
- if snap.Votes[i].Address == candidate {
- snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
- i--
- }
- }
- delete(snap.Tally, candidate)
- }
- return nil
- }
|