| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- // Copyright 2021 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 beacon
- import (
- "errors"
- "fmt"
- "math/big"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/consensus/misc"
- "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/rpc"
- "github.com/ethereum/go-ethereum/trie"
- )
- // Proof-of-stake protocol constants.
- var (
- beaconDifficulty = common.Big0 // The default block difficulty in the beacon consensus
- beaconNonce = types.EncodeNonce(0) // The default block nonce in the beacon consensus
- )
- // Various error messages to mark blocks invalid. These should be private to
- // prevent engine specific errors from being referenced in the remainder of the
- // codebase, inherently breaking if the engine is swapped out. Please put common
- // error types into the consensus package.
- var (
- errTooManyUncles = errors.New("too many uncles")
- errInvalidNonce = errors.New("invalid nonce")
- errInvalidUncleHash = errors.New("invalid uncle hash")
- errInvalidTimestamp = errors.New("invalid timestamp")
- errNotSupportPosHeader = errors.New("not support pos header")
- )
- // Beacon is a consensus engine that combines the eth1 consensus and proof-of-stake
- // algorithm. There is a special flag inside to decide whether to use legacy consensus
- // rules or new rules. The transition rule is described in the eth1/2 merge spec.
- // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3675.md
- //
- // The beacon here is a half-functional consensus engine with partial functions which
- // is only used for necessary consensus checks. The legacy consensus engine can be any
- // engine implements the consensus interface (except the beacon itself).
- type Beacon struct {
- ethone consensus.Engine // Original consensus engine used in eth1, e.g. ethash or clique
- }
- // New creates a consensus engine with the given embedded eth1 engine.
- func New(ethone consensus.Engine) *Beacon {
- if _, ok := ethone.(*Beacon); ok {
- panic("nested consensus engine")
- }
- return &Beacon{ethone: ethone}
- }
- // Author implements consensus.Engine, returning the verified author of the block.
- func (beacon *Beacon) Author(header *types.Header) (common.Address, error) {
- if !beacon.IsPoSHeader(header) {
- return beacon.ethone.Author(header)
- }
- return header.Coinbase, nil
- }
- // VerifyHeader checks whether a header conforms to the consensus rules of the
- // stock Ethereum consensus engine.
- func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
- if chain.Config().EthPoWForkSupport {
- return beacon.ethone.VerifyHeader(chain, header, seal)
- }
- reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1)
- if err != nil {
- return err
- }
- if !reached {
- return beacon.ethone.VerifyHeader(chain, header, seal)
- }
- // Short circuit if the parent is not known
- parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
- if parent == nil {
- return consensus.ErrUnknownAncestor
- }
- // Sanity checks passed, do a proper verification
- return beacon.verifyHeader(chain, header, parent)
- }
- // 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.
- // VerifyHeaders expect the headers to be ordered and continuous.
- func (beacon *Beacon) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
- if !beacon.IsPoSHeader(headers[len(headers)-1]) {
- return beacon.ethone.VerifyHeaders(chain, headers, seals)
- }
- if chain.Config().EthPoWForkSupport {
- err := errNotSupportPosHeader
- results := make(chan error, len(headers))
- for i := 0; i < len(headers); i++ {
- results <- err
- }
- return make(chan struct{}), results
- }
- var (
- preHeaders []*types.Header
- postHeaders []*types.Header
- preSeals []bool
- )
- for index, header := range headers {
- if beacon.IsPoSHeader(header) {
- preHeaders = headers[:index]
- postHeaders = headers[index:]
- preSeals = seals[:index]
- break
- }
- }
- if len(preHeaders) == 0 {
- // All the headers are pos headers. Verify that the parent block reached total terminal difficulty.
- if reached, err := IsTTDReached(chain, headers[0].ParentHash, headers[0].Number.Uint64()-1); !reached {
- // TTD not reached for the first block, mark subsequent with invalid terminal block
- if err == nil {
- err = consensus.ErrInvalidTerminalBlock
- }
- results := make(chan error, len(headers))
- for i := 0; i < len(headers); i++ {
- results <- err
- }
- return make(chan struct{}), results
- }
- return beacon.verifyHeaders(chain, headers, nil)
- }
- // The transition point exists in the middle, separate the headers
- // into two batches and apply different verification rules for them.
- var (
- abort = make(chan struct{})
- results = make(chan error, len(headers))
- )
- go func() {
- var (
- old, new, out = 0, len(preHeaders), 0
- errors = make([]error, len(headers))
- done = make([]bool, len(headers))
- oldDone, oldResult = beacon.ethone.VerifyHeaders(chain, preHeaders, preSeals)
- newDone, newResult = beacon.verifyHeaders(chain, postHeaders, preHeaders[len(preHeaders)-1])
- )
- // Verify that pre-merge headers don't overflow the TTD
- if index, err := verifyTerminalPoWBlock(chain, preHeaders); err != nil {
- // Mark all subsequent pow headers with the error.
- for i := index; i < len(preHeaders); i++ {
- errors[i], done[i] = err, true
- }
- }
- // Collect the results
- for {
- for ; done[out]; out++ {
- results <- errors[out]
- if out == len(headers)-1 {
- return
- }
- }
- select {
- case err := <-oldResult:
- if !done[old] { // skip TTD-verified failures
- errors[old], done[old] = err, true
- }
- old++
- case err := <-newResult:
- errors[new], done[new] = err, true
- new++
- case <-abort:
- close(oldDone)
- close(newDone)
- return
- }
- }
- }()
- return abort, results
- }
- // verifyTerminalPoWBlock verifies that the preHeaders conform to the specification
- // wrt. their total difficulty.
- // It expects:
- // - preHeaders to be at least 1 element
- // - the parent of the header element to be stored in the chain correctly
- // - the preHeaders to have a set difficulty
- // - the last element to be the terminal block
- func verifyTerminalPoWBlock(chain consensus.ChainHeaderReader, preHeaders []*types.Header) (int, error) {
- td := chain.GetTd(preHeaders[0].ParentHash, preHeaders[0].Number.Uint64()-1)
- if td == nil {
- return 0, consensus.ErrUnknownAncestor
- }
- td = new(big.Int).Set(td)
- // Check that all blocks before the last one are below the TTD
- for i, head := range preHeaders {
- if td.Cmp(chain.Config().TerminalTotalDifficulty) >= 0 {
- return i, consensus.ErrInvalidTerminalBlock
- }
- td.Add(td, head.Difficulty)
- }
- // Check that the last block is the terminal block
- if td.Cmp(chain.Config().TerminalTotalDifficulty) < 0 {
- return len(preHeaders) - 1, consensus.ErrInvalidTerminalBlock
- }
- return 0, nil
- }
- // VerifyUncles verifies that the given block's uncles conform to the consensus
- // rules of the Ethereum consensus engine.
- func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
- if !beacon.IsPoSHeader(block.Header()) {
- return beacon.ethone.VerifyUncles(chain, block)
- }
- if chain.Config().EthPoWForkSupport {
- return errNotSupportPosHeader
- }
- // Verify that there is no uncle block. It's explicitly disabled in the beacon
- if len(block.Uncles()) > 0 {
- return errTooManyUncles
- }
- return nil
- }
- // verifyHeader checks whether a header conforms to the consensus rules of the
- // stock Ethereum consensus engine. The difference between the beacon and classic is
- // (a) The following fields are expected to be constants:
- // - difficulty is expected to be 0
- // - nonce is expected to be 0
- // - unclehash is expected to be Hash(emptyHeader)
- // to be the desired constants
- //
- // (b) we don't verify if a block is in the future anymore
- // (c) the extradata is limited to 32 bytes
- func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error {
- // Ensure that the header's extra-data section is of a reasonable size
- if len(header.Extra) > 32 {
- return fmt.Errorf("extra-data longer than 32 bytes (%d)", len(header.Extra))
- }
- // Verify the seal parts. Ensure the nonce and uncle hash are the expected value.
- if header.Nonce != beaconNonce {
- return errInvalidNonce
- }
- if header.UncleHash != types.EmptyUncleHash {
- return errInvalidUncleHash
- }
- // Verify the timestamp
- if header.Time <= parent.Time {
- return errInvalidTimestamp
- }
- // Verify the block's difficulty to ensure it's the default constant
- if beaconDifficulty.Cmp(header.Difficulty) != 0 {
- return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, beaconDifficulty)
- }
- // Verify that the gas limit is <= 2^63-1
- if header.GasLimit > params.MaxGasLimit {
- return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
- }
- // Verify that the gasUsed is <= gasLimit
- if header.GasUsed > header.GasLimit {
- return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
- }
- // Verify that the block number is parent's +1
- if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(common.Big1) != 0 {
- return consensus.ErrInvalidNumber
- }
- // Verify the header's EIP-1559 attributes.
- return misc.VerifyEip1559Header(chain.Config(), parent, header)
- }
- // 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. An additional parent
- // header will be passed if the relevant header is not in the database yet.
- func (beacon *Beacon) verifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, ancestor *types.Header) (chan<- struct{}, <-chan error) {
- var (
- abort = make(chan struct{})
- results = make(chan error, len(headers))
- )
- go func() {
- for i, header := range headers {
- var parent *types.Header
- if i == 0 {
- if ancestor != nil {
- parent = ancestor
- } else {
- parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1)
- }
- } else if headers[i-1].Hash() == headers[i].ParentHash {
- parent = headers[i-1]
- }
- if parent == nil {
- select {
- case <-abort:
- return
- case results <- consensus.ErrUnknownAncestor:
- }
- continue
- }
- err := beacon.verifyHeader(chain, header, parent)
- select {
- case <-abort:
- return
- case results <- err:
- }
- }
- }()
- return abort, results
- }
- // Prepare implements consensus.Engine, initializing the difficulty field of a
- // header to conform to the beacon protocol. The changes are done inline.
- func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
- if chain.Config().EthPoWForkSupport {
- return beacon.ethone.Prepare(chain, header)
- }
- // Transition isn't triggered yet, use the legacy rules for preparation.
- reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1)
- if err != nil {
- return err
- }
- if !reached {
- return beacon.ethone.Prepare(chain, header)
- }
- header.Difficulty = beaconDifficulty
- return nil
- }
- // Finalize implements consensus.Engine, setting the final state on the header
- func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
- // Finalize is different with Prepare, it can be used in both block generation
- // and verification. So determine the consensus rules by header type.
- if !beacon.IsPoSHeader(header) {
- beacon.ethone.Finalize(chain, header, state, txs, uncles)
- return
- }
- if chain.Config().EthPoWForkSupport {
- return
- }
- // The block reward is no longer handled here. It's done by the
- // external consensus engine.
- header.Root = state.IntermediateRoot(true)
- }
- // FinalizeAndAssemble implements consensus.Engine, setting the final state and
- // assembling the block.
- func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
- // FinalizeAndAssemble is different with Prepare, it can be used in both block
- // generation and verification. So determine the consensus rules by header type.
- if !beacon.IsPoSHeader(header) {
- return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts)
- }
- if chain.Config().EthPoWForkSupport {
- return nil, errNotSupportPosHeader
- }
- // Finalize and assemble the block
- beacon.Finalize(chain, header, state, txs, uncles)
- return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil
- }
- // Seal generates a new sealing request for the given input block and pushes
- // the result into the given channel.
- //
- // Note, the method returns immediately and will send the result async. More
- // than one result may also be returned depending on the consensus algorithm.
- func (beacon *Beacon) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
- if !beacon.IsPoSHeader(block.Header()) {
- return beacon.ethone.Seal(chain, block, results, stop)
- }
- // The seal verification is done by the external consensus engine,
- // return directly without pushing any block back. In another word
- // beacon won't return any result by `results` channel which may
- // blocks the receiver logic forever.
- return nil
- }
- // SealHash returns the hash of a block prior to it being sealed.
- func (beacon *Beacon) SealHash(header *types.Header) common.Hash {
- return beacon.ethone.SealHash(header)
- }
- // CalcDifficulty is the difficulty adjustment algorithm. It returns
- // the difficulty that a new block should have when created at time
- // given the parent block's time and difficulty.
- func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
- // Transition isn't triggered yet, use the legacy rules for calculation
- if chain.Config().EthPoWForkSupport {
- return beacon.ethone.CalcDifficulty(chain, time, parent)
- }
- if reached, _ := IsTTDReached(chain, parent.Hash(), parent.Number.Uint64()); !reached {
- return beacon.ethone.CalcDifficulty(chain, time, parent)
- }
- return beaconDifficulty
- }
- // APIs implements consensus.Engine, returning the user facing RPC APIs.
- func (beacon *Beacon) APIs(chain consensus.ChainHeaderReader) []rpc.API {
- return beacon.ethone.APIs(chain)
- }
- // Close shutdowns the consensus engine
- func (beacon *Beacon) Close() error {
- return beacon.ethone.Close()
- }
- // IsPoSHeader reports the header belongs to the PoS-stage with some special fields.
- // This function is not suitable for a part of APIs like Prepare or CalcDifficulty
- // because the header difficulty is not set yet.
- func (beacon *Beacon) IsPoSHeader(header *types.Header) bool {
- if header.Difficulty == nil {
- panic("IsPoSHeader called with invalid difficulty")
- }
- return header.Difficulty.Cmp(beaconDifficulty) == 0
- }
- // InnerEngine returns the embedded eth1 consensus engine.
- func (beacon *Beacon) InnerEngine() consensus.Engine {
- return beacon.ethone
- }
- // SetThreads updates the mining threads. Delegate the call
- // to the eth1 engine if it's threaded.
- func (beacon *Beacon) SetThreads(threads int) {
- type threaded interface {
- SetThreads(threads int)
- }
- if th, ok := beacon.ethone.(threaded); ok {
- th.SetThreads(threads)
- }
- }
- // IsTTDReached checks if the TotalTerminalDifficulty has been surpassed on the `parentHash` block.
- // It depends on the parentHash already being stored in the database.
- // If the parentHash is not stored in the database a UnknownAncestor error is returned.
- func IsTTDReached(chain consensus.ChainHeaderReader, parentHash common.Hash, number uint64) (bool, error) {
- if chain.Config().EthPoWForkSupport {
- return false, nil
- }
- if chain.Config().TerminalTotalDifficulty == nil {
- return false, nil
- }
- td := chain.GetTd(parentHash, number)
- if td == nil {
- return false, consensus.ErrUnknownAncestor
- }
- return td.Cmp(chain.Config().TerminalTotalDifficulty) >= 0, nil
- }
|