123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- package ibftengine
- import (
- "bytes"
- "math/big"
- "time"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/consensus/istanbul"
- istanbulcommon "github.com/ethereum/go-ethereum/consensus/istanbul/common"
- ibfttypes "github.com/ethereum/go-ethereum/consensus/istanbul/ibft/types"
- "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/params"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
- "golang.org/x/crypto/sha3"
- )
- var (
- nilUncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
- nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new validator
- nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a validator.
- )
- type SignerFn func(data []byte) ([]byte, error)
- type Engine struct {
- cfg *istanbul.Config
- signer common.Address // Ethereum address of the signing key
- sign SignerFn // Signer function to authorize hashes with
- }
- func NewEngine(cfg *istanbul.Config, signer common.Address, sign SignerFn) *Engine {
- return &Engine{
- cfg: cfg,
- signer: signer,
- sign: sign,
- }
- }
- func (e *Engine) Author(header *types.Header) (common.Address, error) {
- // Retrieve the signature from the header extra-data
- extra, err := types.ExtractIstanbulExtra(header)
- if err != nil {
- return common.Address{}, err
- }
- addr, err := istanbul.GetSignatureAddress(sigHash(header).Bytes(), extra.Seal)
- if err != nil {
- return addr, err
- }
- return addr, nil
- }
- func (e *Engine) CommitHeader(header *types.Header, seals [][]byte, round *big.Int) error {
- // Append seals into extra-data
- return writeCommittedSeals(header, seals)
- }
- func (e *Engine) VerifyBlockProposal(chain consensus.ChainHeaderReader, block *types.Block, validators istanbul.ValidatorSet) (time.Duration, error) {
- // check block body
- txnHash := types.DeriveSha(block.Transactions(), new(trie.Trie))
- if txnHash != block.Header().TxHash {
- return 0, istanbulcommon.ErrMismatchTxhashes
- }
- uncleHash := types.CalcUncleHash(block.Uncles())
- if uncleHash != nilUncleHash {
- return 0, istanbulcommon.ErrInvalidUncleHash
- }
- // verify the header of proposed block
- err := e.VerifyHeader(chain, block.Header(), nil, validators)
- if err == nil || err == istanbulcommon.ErrEmptyCommittedSeals {
- // ignore errEmptyCommittedSeals error because we don't have the committed seals yet
- return 0, nil
- } else if err == consensus.ErrFutureBlock {
- return time.Until(time.Unix(int64(block.Header().Time), 0)), consensus.ErrFutureBlock
- }
- return 0, err
- }
- func (e *Engine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header, validators istanbul.ValidatorSet) error {
- return e.verifyHeader(chain, header, parents, validators)
- }
- // verifyHeader checks whether a header conforms to the consensus rules.The
- // caller may optionally pass in a batch of parents (ascending order) to avoid
- // looking those up from the database. This is useful for concurrently verifying
- // a batch of new headers.
- func (e *Engine) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header, validators istanbul.ValidatorSet) error {
- if header.Number == nil {
- return istanbulcommon.ErrUnknownBlock
- }
- // Don't waste time checking blocks from the future (adjusting for allowed threshold)
- adjustedTimeNow := time.Now().Add(time.Duration(e.cfg.AllowedFutureBlockTime) * time.Second).Unix()
- if header.Time > uint64(adjustedTimeNow) {
- return consensus.ErrFutureBlock
- }
- if _, err := types.ExtractIstanbulExtra(header); err != nil {
- return istanbulcommon.ErrInvalidExtraDataFormat
- }
- if header.Nonce != (istanbulcommon.EmptyBlockNonce) && !bytes.Equal(header.Nonce[:], nonceAuthVote) && !bytes.Equal(header.Nonce[:], nonceDropVote) {
- return istanbulcommon.ErrInvalidNonce
- }
- // Ensure that the mix digest is zero as we don't have fork protection currently
- if header.MixDigest != types.IstanbulDigest {
- return istanbulcommon.ErrInvalidMixDigest
- }
- // Ensure that the block doesn't contain any uncles which are meaningless in Istanbul
- if header.UncleHash != nilUncleHash {
- return istanbulcommon.ErrInvalidUncleHash
- }
- // Ensure that the block's difficulty is meaningful (may not be correct at this point)
- if header.Difficulty == nil || header.Difficulty.Cmp(istanbulcommon.DefaultDifficulty) != 0 {
- return istanbulcommon.ErrInvalidDifficulty
- }
- return e.verifyCascadingFields(chain, header, validators, parents)
- }
- // verifyCascadingFields verifies all the header fields that are not standalone,
- // rather depend on a batch of previous headers. The caller may optionally pass
- // in a batch of parents (ascending order) to avoid looking those up from the
- // database. This is useful for concurrently verifying a batch of new headers.
- func (e *Engine) verifyCascadingFields(chain consensus.ChainHeaderReader, header *types.Header, validators istanbul.ValidatorSet, parents []*types.Header) error {
- // The genesis block is the always valid dead-end
- number := header.Number.Uint64()
- if number == 0 {
- return nil
- }
- // Check parent
- var parent *types.Header
- if len(parents) > 0 {
- parent = parents[len(parents)-1]
- } else {
- parent = chain.GetHeader(header.ParentHash, number-1)
- }
- // Ensure that the block's parent has right number and hash
- if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
- return consensus.ErrUnknownAncestor
- }
- // Ensure that the block's timestamp isn't too close to it's parent
- if parent.Time+e.cfg.GetConfig(header.Number).BlockPeriod > header.Time {
- return istanbulcommon.ErrInvalidTimestamp
- }
- // Verify signer
- if err := e.verifySigner(chain, header, parents, validators); err != nil {
- return err
- }
- return e.verifyCommittedSeals(chain, header, parents, validators)
- }
- func (e *Engine) verifySigner(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header, validators istanbul.ValidatorSet) error {
- // Verifying the genesis block is not supported
- number := header.Number.Uint64()
- if number == 0 {
- return istanbulcommon.ErrUnknownBlock
- }
- // Resolve the authorization key and check against signers
- signer, err := e.Author(header)
- if err != nil {
- return err
- }
- // Signer should be in the validator set of previous block's extraData.
- if _, v := validators.GetByAddress(signer); v == nil {
- return istanbulcommon.ErrUnauthorized
- }
- return nil
- }
- // verifyCommittedSeals checks whether every committed seal is signed by one of the parent's validators
- func (e *Engine) verifyCommittedSeals(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header, validators istanbul.ValidatorSet) error {
- number := header.Number.Uint64()
- if number == 0 {
- // We don't need to verify committed seals in the genesis block
- return nil
- }
- extra, err := types.ExtractIstanbulExtra(header)
- if err != nil {
- return err
- }
- committedSeal := extra.CommittedSeal
- // The length of Committed seals should be larger than 0
- if len(committedSeal) == 0 {
- return istanbulcommon.ErrEmptyCommittedSeals
- }
- validatorsCpy := validators.Copy()
- // Check whether the committed seals are generated by validators
- validSeal := 0
- committers, err := e.Signers(header)
- if err != nil {
- return err
- }
- for _, addr := range committers {
- if validatorsCpy.RemoveValidator(addr) {
- validSeal++
- continue
- }
- return istanbulcommon.ErrInvalidCommittedSeals
- }
- // The length of validSeal should be larger than number of faulty node + 1
- if validSeal <= validators.F() {
- return istanbulcommon.ErrInvalidCommittedSeals
- }
- return nil
- }
- // VerifyUncles verifies that the given block's uncles conform to the consensus
- // rules of a given engine.
- func (e *Engine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
- if len(block.Uncles()) > 0 {
- return istanbulcommon.ErrInvalidUncleHash
- }
- return nil
- }
- // VerifySeal checks whether the crypto seal on a header is valid according to
- // the consensus rules of the given engine.
- func (e *Engine) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header, validators istanbul.ValidatorSet) error {
- // get parent header and ensure the signer is in parent's validator set
- number := header.Number.Uint64()
- if number == 0 {
- return istanbulcommon.ErrUnknownBlock
- }
- // ensure that the difficulty equals to istanbulcommon.DefaultDifficulty
- if header.Difficulty.Cmp(istanbulcommon.DefaultDifficulty) != 0 {
- return istanbulcommon.ErrInvalidDifficulty
- }
- return e.verifySigner(chain, header, nil, validators)
- }
- func (e *Engine) Prepare(chain consensus.ChainHeaderReader, header *types.Header, validators istanbul.ValidatorSet) error {
- header.Coinbase = common.Address{}
- header.Nonce = istanbulcommon.EmptyBlockNonce
- header.MixDigest = types.IstanbulDigest
- // copy the parent extra data as the header extra data
- number := header.Number.Uint64()
- parent := chain.GetHeader(header.ParentHash, number-1)
- if parent == nil {
- return consensus.ErrUnknownAncestor
- }
- // use the same difficulty for all blocks
- header.Difficulty = istanbulcommon.DefaultDifficulty
- // set header's timestamp
- header.Time = parent.Time + e.cfg.GetConfig(header.Number).BlockPeriod
- if header.Time < uint64(time.Now().Unix()) {
- header.Time = uint64(time.Now().Unix())
- }
- num := big.NewInt(0).SetUint64(number - 1)
- validatorContract := e.cfg.GetValidatorContractAddress(num)
- var addresses []common.Address
- if validatorContract != (common.Address{}) && e.cfg.GetValidatorSelectionMode(num) == params.ContractMode {
- addresses = []common.Address{}
- } else {
- // add validators in snapshot to extraData's validators section
- addresses = validator.SortedAddresses(validators.List())
- }
- extra, err := prepareExtra(header, addresses)
- if err != nil {
- return err
- }
- header.Extra = extra
- 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 (e *Engine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
- // No block rewards in Istanbul, so the state remains as is and uncles are dropped
- header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
- header.UncleHash = nilUncleHash
- }
- // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
- // nor block rewards given, and returns the final block.
- func (e *Engine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
- /// No block rewards in Istanbul, so the state remains as is and uncles are dropped
- header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
- header.UncleHash = nilUncleHash
- // Assemble and return the final block for sealing
- return types.NewBlock(header, txs, nil, receipts, new(trie.Trie)), nil
- }
- // Seal generates a new block for the given input block with the local miner's
- // seal place on top.
- func (e *Engine) Seal(chain consensus.ChainHeaderReader, block *types.Block, validators istanbul.ValidatorSet) (*types.Block, error) {
- // update the block header timestamp and signature and propose the block to core engine
- header := block.Header()
- number := header.Number.Uint64()
- if _, v := validators.GetByAddress(e.signer); v == nil {
- return block, istanbulcommon.ErrUnauthorized
- }
- parent := chain.GetHeader(header.ParentHash, number-1)
- if parent == nil {
- return block, consensus.ErrUnknownAncestor
- }
- return e.updateBlock(parent, block)
- }
- // update timestamp and signature of the block based on its number of transactions
- func (e *Engine) updateBlock(parent *types.Header, block *types.Block) (*types.Block, error) {
- // sign the hash
- header := block.Header()
- seal, err := e.sign(sigHash(header).Bytes())
- if err != nil {
- return nil, err
- }
- err = writeSeal(header, seal)
- if err != nil {
- return nil, err
- }
- return block.WithSeal(header), nil
- }
- // writeSeal writes the extra-data field of the given header with the given seals.
- // suggest to rename to writeSeal.
- func writeSeal(h *types.Header, seal []byte) error {
- if len(seal)%types.IstanbulExtraSeal != 0 {
- return istanbulcommon.ErrInvalidSignature
- }
- istanbulExtra, err := types.ExtractIstanbulExtra(h)
- if err != nil {
- return err
- }
- istanbulExtra.Seal = seal
- payload, err := rlp.EncodeToBytes(&istanbulExtra)
- if err != nil {
- return err
- }
- h.Extra = append(h.Extra[:types.IstanbulExtraVanity], payload...)
- return nil
- }
- func (e *Engine) SealHash(header *types.Header) common.Hash {
- return sigHash(header)
- }
- func (e *Engine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
- return new(big.Int)
- }
- func (e *Engine) ExtractGenesisValidators(header *types.Header) ([]common.Address, error) {
- extra, err := types.ExtractIstanbulExtra(header)
- if err != nil {
- return nil, err
- }
- return extra.Validators, nil
- }
- func (e *Engine) Signers(header *types.Header) ([]common.Address, error) {
- extra, err := types.ExtractIstanbulExtra(header)
- if err != nil {
- return []common.Address{}, err
- }
- committedSeal := extra.CommittedSeal
- proposalSeal := PrepareCommittedSeal(header.Hash())
- var addrs []common.Address
- // 1. Get committed seals from current header
- for _, seal := range committedSeal {
- // 2. Get the original address by seal and parent block hash
- addr, err := istanbulcommon.GetSignatureAddress(proposalSeal, seal)
- if err != nil {
- return nil, istanbulcommon.ErrInvalidSignature
- }
- addrs = append(addrs, addr)
- }
- return addrs, nil
- }
- func (e *Engine) Address() common.Address {
- return e.signer
- }
- func (e *Engine) WriteVote(header *types.Header, candidate common.Address, authorize bool) error {
- header.Coinbase = candidate
- if authorize {
- copy(header.Nonce[:], nonceAuthVote)
- } else {
- copy(header.Nonce[:], nonceDropVote)
- }
- return nil
- }
- func (e *Engine) ReadVote(header *types.Header) (candidate common.Address, authorize bool, err error) {
- switch {
- case bytes.Equal(header.Nonce[:], nonceAuthVote):
- authorize = true
- case bytes.Equal(header.Nonce[:], nonceDropVote):
- authorize = false
- default:
- return common.Address{}, false, istanbulcommon.ErrInvalidVote
- }
- return header.Coinbase, authorize, nil
- }
- // FIXME: Need to update this for Istanbul
- // sigHash returns the hash which is used as input for the Istanbul
- // signing. It is the hash of the entire header apart from the 65 byte signature
- // contained at the end of the extra data.
- //
- // Note, the method requires the extra data to be at least 65 bytes, otherwise it
- // panics. This is done to avoid accidentally using both forms (signature present
- // or not), which could be abused to produce different hashes for the same header.
- func sigHash(header *types.Header) (hash common.Hash) {
- hasher := sha3.NewLegacyKeccak256()
- rlp.Encode(hasher, types.IstanbulFilteredHeader(header, false))
- hasher.Sum(hash[:0])
- return hash
- }
- // prepareExtra returns a extra-data of the given header and validators
- func prepareExtra(header *types.Header, vals []common.Address) ([]byte, error) {
- var buf bytes.Buffer
- // compensate the lack bytes if header.Extra is not enough IstanbulExtraVanity bytes.
- if len(header.Extra) < types.IstanbulExtraVanity {
- header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity-len(header.Extra))...)
- }
- buf.Write(header.Extra[:types.IstanbulExtraVanity])
- ist := &types.IstanbulExtra{
- Validators: vals,
- Seal: []byte{},
- CommittedSeal: [][]byte{},
- }
- payload, err := rlp.EncodeToBytes(&ist)
- if err != nil {
- return nil, err
- }
- return append(buf.Bytes(), payload...), nil
- }
- func writeCommittedSeals(h *types.Header, committedSeals [][]byte) error {
- if len(committedSeals) == 0 {
- return istanbulcommon.ErrInvalidCommittedSeals
- }
- for _, seal := range committedSeals {
- if len(seal) != types.IstanbulExtraSeal {
- return istanbulcommon.ErrInvalidCommittedSeals
- }
- }
- istanbulExtra, err := types.ExtractIstanbulExtra(h)
- if err != nil {
- return err
- }
- istanbulExtra.CommittedSeal = make([][]byte, len(committedSeals))
- copy(istanbulExtra.CommittedSeal, committedSeals)
- payload, err := rlp.EncodeToBytes(&istanbulExtra)
- if err != nil {
- return err
- }
- h.Extra = append(h.Extra[:types.IstanbulExtraVanity], payload...)
- return nil
- }
- // PrepareCommittedSeal returns a committed seal for the given hash
- func PrepareCommittedSeal(hash common.Hash) []byte {
- var buf bytes.Buffer
- buf.Write(hash.Bytes())
- buf.Write([]byte{byte(ibfttypes.MsgCommit)})
- return buf.Bytes()
- }
|