diff --git a/consensus/clique/api.go b/consensus/clique/api.go
deleted file mode 100644
index 5fd30db8a9..0000000000
--- a/consensus/clique/api.go
+++ /dev/null
@@ -1,149 +0,0 @@
-// 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 .
-
-package clique
-
-import (
- "encoding/json"
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/rpc"
-)
-
-// API is a user facing RPC API to allow controlling the signer and voting
-// mechanisms of the proof-of-authority scheme.
-type API struct {
- chain consensus.ChainHeaderReader
- clique *Clique
-}
-
-// GetSnapshot retrieves the state snapshot at a given block.
-func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
- // Retrieve the requested block number (or current if none requested)
- var header *types.Header
- if number == nil || *number == rpc.LatestBlockNumber {
- header = api.chain.CurrentHeader()
- } else {
- header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
- }
- // Ensure we have an actually valid block and return its snapshot
- if header == nil {
- return nil, errUnknownBlock
- }
- return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
-}
-
-// GetSnapshotAtHash retrieves the state snapshot at a given block.
-func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
- header := api.chain.GetHeaderByHash(hash)
- if header == nil {
- return nil, errUnknownBlock
- }
- return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
-}
-
-// GetSigners retrieves the list of authorized signers at the specified block.
-func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) {
- // Retrieve the requested block number (or current if none requested)
- var header *types.Header
- if number == nil || *number == rpc.LatestBlockNumber {
- header = api.chain.CurrentHeader()
- } else {
- header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
- }
- // Ensure we have an actually valid block and return the signers from its snapshot
- if header == nil {
- return nil, errUnknownBlock
- }
- snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
- if err != nil {
- return nil, err
- }
- return snap.signers(), nil
-}
-
-// GetSignersAtHash retrieves the list of authorized signers at the specified block.
-func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) {
- header := api.chain.GetHeaderByHash(hash)
- if header == nil {
- return nil, errUnknownBlock
- }
- snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
- if err != nil {
- return nil, err
- }
- return snap.signers(), nil
-}
-
-type blockNumberOrHashOrRLP struct {
- *rpc.BlockNumberOrHash
- RLP hexutil.Bytes `json:"rlp,omitempty"`
-}
-
-func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error {
- bnOrHash := new(rpc.BlockNumberOrHash)
- // Try to unmarshal bNrOrHash
- if err := bnOrHash.UnmarshalJSON(data); err == nil {
- sb.BlockNumberOrHash = bnOrHash
- return nil
- }
- // Try to unmarshal RLP
- var input string
- if err := json.Unmarshal(data, &input); err != nil {
- return err
- }
- blob, err := hexutil.Decode(input)
- if err != nil {
- return err
- }
- sb.RLP = blob
- return nil
-}
-
-// GetSigner returns the signer for a specific clique block.
-// Can be called with a block number, a block hash or a rlp encoded blob.
-// The RLP encoded blob can either be a block or a header.
-func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (common.Address, error) {
- if len(rlpOrBlockNr.RLP) == 0 {
- blockNrOrHash := rlpOrBlockNr.BlockNumberOrHash
- var header *types.Header
- if blockNrOrHash == nil {
- header = api.chain.CurrentHeader()
- } else if hash, ok := blockNrOrHash.Hash(); ok {
- header = api.chain.GetHeaderByHash(hash)
- } else if number, ok := blockNrOrHash.Number(); ok {
- header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
- }
- if header == nil {
- return common.Address{}, fmt.Errorf("missing block %v", blockNrOrHash.String())
- }
- return api.clique.Author(header)
- }
- block := new(types.Block)
- if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, block); err == nil {
- return api.clique.Author(block.Header())
- }
- header := new(types.Header)
- if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, header); err != nil {
- return common.Address{}, err
- }
- return api.clique.Author(header)
-}
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index 980afd33e5..5dc3e6cafe 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -19,47 +19,23 @@ package clique
import (
"errors"
- "github.com/ethereum/go-ethereum/rlp"
- "golang.org/x/crypto/sha3"
"io"
"math/big"
- "sync"
- "time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/params"
-)
-
-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
- inmemorySignatures = 4096 // Number of recent block signatures to keep in memory
-
- wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/rpc"
+ "golang.org/x/crypto/sha3"
)
// Clique proof-of-authority protocol constants.
var (
- epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes
-
- extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
- extraSeal = crypto.SignatureLength // Fixed number of extra-data suffix bytes reserved for signer seal
-
- nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer
- nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer.
-
- uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
-
- diffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures
- diffNoTurn = big.NewInt(1) // Block difficulty for out-of-turn signatures
+ extraSeal = crypto.SignatureLength // Fixed number of extra-data suffix bytes reserved for signer seal
)
// Various error messages to mark blocks invalid. These should be private to
@@ -67,81 +43,16 @@ var (
// codebase, inherently breaking if the engine is swapped out. Please put common
// error types into the consensus package.
var (
- // errUnknownBlock is returned when the list of signers is requested for a block
- // that is not part of the local blockchain.
- errUnknownBlock = errors.New("unknown block")
-
- // errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition
- // block has a beneficiary set to non-zeroes.
- errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero")
-
- // errInvalidVote is returned if a nonce value is something else that the two
- // allowed constants of 0x00..0 or 0xff..f.
- errInvalidVote = errors.New("vote nonce not 0x00..0 or 0xff..f")
-
- // errInvalidCheckpointVote is returned if a checkpoint/epoch transition block
- // has a vote nonce set to non-zeroes.
- errInvalidCheckpointVote = errors.New("vote nonce in checkpoint block non-zero")
-
- // errMissingVanity is returned if a block's extra-data section is shorter than
- // 32 bytes, which is required to store the signer vanity.
- errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing")
-
// errMissingSignature is returned if a block's extra-data section doesn't seem
// to contain a 65 byte secp256k1 signature.
errMissingSignature = errors.New("extra-data 65 byte signature suffix missing")
-
- // errExtraSigners is returned if non-checkpoint block contain signer data in
- // their extra-data fields.
- errExtraSigners = errors.New("non-checkpoint block contains extra signer list")
-
- // errInvalidCheckpointSigners is returned if a checkpoint block contains an
- // invalid list of signers (i.e. non divisible by 20 bytes).
- errInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block")
-
- // errMismatchingCheckpointSigners is returned if a checkpoint block contains a
- // list of signers different than the one the local node calculated.
- errMismatchingCheckpointSigners = errors.New("mismatching signer list on checkpoint block")
-
- // errInvalidMixDigest is returned if a block's mix digest is non-zero.
- errInvalidMixDigest = errors.New("non-zero mix digest")
-
- // errInvalidUncleHash is returned if a block contains an non-empty uncle list.
- errInvalidUncleHash = errors.New("non empty uncle hash")
-
- // errInvalidDifficulty is returned if the difficulty of a block neither 1 or 2.
- errInvalidDifficulty = errors.New("invalid difficulty")
-
- // errWrongDifficulty is returned if the difficulty of a block doesn't match the
- // turn of the signer.
- errWrongDifficulty = errors.New("wrong difficulty")
-
- // errInvalidTimestamp is returned if the timestamp of a block is lower than
- // the previous block's timestamp + the minimum block period.
- errInvalidTimestamp = errors.New("invalid timestamp")
-
- // errInvalidVotingChain is returned if an authorization list is attempted to
- // be modified via out-of-range or non-contiguous headers.
- errInvalidVotingChain = errors.New("invalid voting chain")
-
- // errUnauthorizedSigner is returned if a header is signed by a non-authorized entity.
- errUnauthorizedSigner = errors.New("unauthorized signer")
-
- // errRecentlySigned is returned if a header is signed by an authorized entity
- // that already signed a header recently, thus is temporarily not allowed to.
- errRecentlySigned = errors.New("recently signed")
)
// SignerFn hashes and signs the data to be signed by a backing account.
type SignerFn func(signer accounts.Account, mimeType string, message []byte) ([]byte, error)
// ecrecover extracts the Ethereum account address from a signed header.
-func ecrecover(header *types.Header, sigcache *sigLRU) (common.Address, error) {
- // If the signature's already cached, return that
- hash := header.Hash()
- if address, known := sigcache.Get(hash); known {
- return address, nil
- }
+func ecrecover(header *types.Header) (common.Address, error) {
// Retrieve the signature from the header extra-data
if len(header.Extra) < extraSeal {
return common.Address{}, errMissingSignature
@@ -156,53 +67,23 @@ func ecrecover(header *types.Header, sigcache *sigLRU) (common.Address, error) {
var signer common.Address
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
- sigcache.Add(hash, signer)
return signer, nil
}
// Clique is the proof-of-authority consensus engine proposed to support the
// Ethereum testnet following the Ropsten attacks.
-type Clique struct {
- config *params.CliqueConfig // Consensus engine configuration parameters
- db ethdb.Database // Database to store and retrieve snapshot checkpoints
-
- recents *lru.Cache[common.Hash, *Snapshot] // Snapshots for recent block to speed up reorgs
- signatures *sigLRU // Signatures of recent blocks to speed up mining
-
- proposals map[common.Address]bool // Current list of proposals we are pushing
-
- signer common.Address // Ethereum address of the signing key
- signFn SignerFn // Signer function to authorize hashes with
- lock sync.RWMutex // Protects the signer and proposals fields
-
- // The fields below are for testing only
- fakeDiff bool // Skip difficulty verifications
-}
+type Clique struct{}
// New creates a Clique proof-of-authority consensus engine with the initial
// signers set to the ones provided by the user.
-func New(config *params.CliqueConfig, db ethdb.Database) *Clique {
- // Set any missing consensus parameters to their defaults
- conf := *config
- if conf.Epoch == 0 {
- conf.Epoch = epochLength
- }
- // Allocate the snapshot caches and create the engine
- recents := lru.NewCache[common.Hash, *Snapshot](inmemorySnapshots)
- signatures := lru.NewCache[common.Hash, common.Address](inmemorySignatures)
-
- return &Clique{
- config: &conf,
- db: db,
- recents: recents,
- signatures: signatures,
- }
+func New() *Clique {
+ return &Clique{}
}
// Author implements consensus.Engine, returning the Ethereum address recovered
// from the signature in the header's extra-data section.
func (c *Clique) Author(header *types.Header) (common.Address, error) {
- return ecrecover(header, c.signatures)
+ return ecrecover(header)
}
// VerifyHeader checks whether a header conforms to the consensus rules.
@@ -214,15 +95,19 @@ func (c *Clique) VerifyHeader(chain consensus.ChainHeaderReader, header *types.H
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. 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 (c *Clique) VerifyHeaders(_ consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) {
+func (c *Clique) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) {
abort := make(chan struct{})
results := make(chan error, len(headers))
go func() {
- select {
- case <-abort:
- return
- case results <- nil:
+ for i, header := range headers {
+ err := c.verifyHeader(chain, header, headers[:i])
+
+ select {
+ case <-abort:
+ return
+ case results <- err:
+ }
}
}()
return abort, results
@@ -350,3 +235,7 @@ func encodeSigHeader(w io.Writer, header *types.Header) {
panic("can't encode: " + err.Error())
}
}
+
+func (c *Clique) APIs(chain consensus.ChainHeaderReader) []rpc.API {
+ return []rpc.API{}
+}
diff --git a/consensus/clique/clique_test.go b/consensus/clique/clique_test.go
deleted file mode 100644
index 8ef8dbffa9..0000000000
--- a/consensus/clique/clique_test.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2019 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 .
-
-package clique
-
-import (
- "math/big"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/params"
-)
-
-// This test case is a repro of an annoying bug that took us forever to catch.
-// In Clique PoA networks (Görli, etc), consecutive blocks might have
-// the same state root (no block subsidy, empty block). If a node crashes, the
-// chain ends up losing the recent state and needs to regenerate it from blocks
-// already in the database. The bug was that processing the block *prior* to an
-// empty one **also completes** the empty one, ending up in a known-block error.
-func TestReimportMirroredState(t *testing.T) {
- // Initialize a Clique chain with a single signer
- var (
- db = rawdb.NewMemoryDatabase()
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- addr = crypto.PubkeyToAddress(key.PublicKey)
- engine = New(params.AllCliqueProtocolChanges.Clique, db)
- signer = new(types.HomesteadSigner)
- )
- genspec := &core.Genesis{
- Config: params.AllCliqueProtocolChanges,
- ExtraData: make([]byte, extraVanity+common.AddressLength+extraSeal),
- Alloc: map[common.Address]types.Account{
- addr: {Balance: big.NewInt(10000000000000000)},
- },
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
- copy(genspec.ExtraData[extraVanity:], addr[:])
-
- // Generate a batch of blocks, each properly signed
- chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genspec, nil, engine, vm.Config{}, nil, nil)
- defer chain.Stop()
-
- _, blocks, _ := core.GenerateChainWithGenesis(genspec, engine, 3, func(i int, block *core.BlockGen) {
- // The chain maker doesn't have access to a chain, so the difficulty will be
- // lets unset (nil). Set it here to the correct value.
- block.SetDifficulty(diffInTurn)
-
- // We want to simulate an empty middle block, having the same state as the
- // first one. The last is needs a state change again to force a reorg.
- if i != 1 {
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr), common.Address{0x00}, new(big.Int), params.TxGas, block.BaseFee(), nil), signer, key)
- if err != nil {
- panic(err)
- }
- block.AddTxWithChain(chain, tx)
- }
- })
- for i, block := range blocks {
- header := block.Header()
- if i > 0 {
- header.ParentHash = blocks[i-1].Hash()
- }
- header.Extra = make([]byte, extraVanity+extraSeal)
- header.Difficulty = diffInTurn
-
- sig, _ := crypto.Sign(SealHash(header).Bytes(), key)
- copy(header.Extra[len(header.Extra)-extraSeal:], sig)
- blocks[i] = block.WithSeal(header)
- }
- // Insert the first two blocks and make sure the chain is valid
- db = rawdb.NewMemoryDatabase()
- chain, _ = core.NewBlockChain(db, nil, genspec, nil, engine, vm.Config{}, nil, nil)
- defer chain.Stop()
-
- if _, err := chain.InsertChain(blocks[:2]); err != nil {
- t.Fatalf("failed to insert initial blocks: %v", err)
- }
- if head := chain.CurrentBlock().Number.Uint64(); head != 2 {
- t.Fatalf("chain head mismatch: have %d, want %d", head, 2)
- }
-
- // Simulate a crash by creating a new chain on top of the database, without
- // flushing the dirty states out. Insert the last block, triggering a sidechain
- // reimport.
- chain, _ = core.NewBlockChain(db, nil, genspec, nil, engine, vm.Config{}, nil, nil)
- defer chain.Stop()
-
- if _, err := chain.InsertChain(blocks[2:]); err != nil {
- t.Fatalf("failed to insert final block: %v", err)
- }
- if head := chain.CurrentBlock().Number.Uint64(); head != 3 {
- t.Fatalf("chain head mismatch: have %d, want %d", head, 3)
- }
-}
-
-func TestSealHash(t *testing.T) {
- have := SealHash(&types.Header{
- Difficulty: new(big.Int),
- Number: new(big.Int),
- Extra: make([]byte, 32+65),
- BaseFee: new(big.Int),
- })
- want := common.HexToHash("0xbd3d1fa43fbc4c5bfcc91b179ec92e2861df3654de60468beb908ff805359e8f")
- if have != want {
- t.Errorf("have %x, want %x", have, want)
- }
-}
diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go
deleted file mode 100644
index d0b15e9489..0000000000
--- a/consensus/clique/snapshot.go
+++ /dev/null
@@ -1,311 +0,0 @@
-// 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 .
-
-package clique
-
-import (
- "bytes"
- "encoding/json"
- "maps"
- "slices"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/lru"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "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"
-)
-
-// Vote represents a single vote that an authorized signer made to modify the
-// list of authorizations.
-type Vote struct {
- Signer common.Address `json:"signer"` // Authorized signer that cast this vote
- Block uint64 `json:"block"` // Block number the vote was cast in (expire old votes)
- Address common.Address `json:"address"` // Account being voted on to change its authorization
- Authorize bool `json:"authorize"` // Whether to authorize or deauthorize the voted account
-}
-
-// Tally is a simple vote tally to keep the current score of votes. Votes that
-// go against the proposal aren't counted since it's equivalent to not voting.
-type Tally struct {
- Authorize bool `json:"authorize"` // Whether the vote is about authorizing or kicking someone
- Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal
-}
-
-type sigLRU = lru.Cache[common.Hash, common.Address]
-
-// Snapshot is the state of the authorization voting at a given point in time.
-type Snapshot struct {
- config *params.CliqueConfig // Consensus engine parameters to fine tune behavior
- sigcache *sigLRU // Cache of recent block signatures to speed up ecrecover
-
- Number uint64 `json:"number"` // Block number where the snapshot was created
- Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
- Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment
- Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections
- Votes []*Vote `json:"votes"` // List of votes cast in chronological order
- Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating
-}
-
-// newSnapshot creates a new snapshot with the specified startup parameters. This
-// method does not initialize the set of recent signers, so only ever use if for
-// the genesis block.
-func newSnapshot(config *params.CliqueConfig, sigcache *sigLRU, number uint64, hash common.Hash, signers []common.Address) *Snapshot {
- snap := &Snapshot{
- config: config,
- sigcache: sigcache,
- Number: number,
- Hash: hash,
- Signers: make(map[common.Address]struct{}),
- Recents: make(map[uint64]common.Address),
- Tally: make(map[common.Address]Tally),
- }
- for _, signer := range signers {
- snap.Signers[signer] = struct{}{}
- }
- return snap
-}
-
-// loadSnapshot loads an existing snapshot from the database.
-func loadSnapshot(config *params.CliqueConfig, sigcache *sigLRU, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
- blob, err := db.Get(append(rawdb.CliqueSnapshotPrefix, hash[:]...))
- if err != nil {
- return nil, err
- }
- snap := new(Snapshot)
- if err := json.Unmarshal(blob, snap); err != nil {
- return nil, err
- }
- snap.config = config
- snap.sigcache = sigcache
-
- return snap, nil
-}
-
-// store inserts the snapshot into the database.
-func (s *Snapshot) store(db ethdb.Database) error {
- blob, err := json.Marshal(s)
- if err != nil {
- return err
- }
- return db.Put(append(rawdb.CliqueSnapshotPrefix, s.Hash[:]...), blob)
-}
-
-// copy creates a deep copy of the snapshot, though not the individual votes.
-func (s *Snapshot) copy() *Snapshot {
- return &Snapshot{
- config: s.config,
- sigcache: s.sigcache,
- Number: s.Number,
- Hash: s.Hash,
- Signers: maps.Clone(s.Signers),
- Recents: maps.Clone(s.Recents),
- Votes: slices.Clone(s.Votes),
- Tally: maps.Clone(s.Tally),
- }
-}
-
-// validVote returns whether it makes sense to cast the specified vote in the
-// given snapshot context (e.g. don't try to add an already authorized signer).
-func (s *Snapshot) validVote(address common.Address, authorize bool) bool {
- _, signer := s.Signers[address]
- return (signer && !authorize) || (!signer && authorize)
-}
-
-// cast adds a new vote into the tally.
-func (s *Snapshot) cast(address common.Address, authorize bool) bool {
- // Ensure the vote is meaningful
- if !s.validVote(address, authorize) {
- return false
- }
- // Cast the vote into an existing or new tally
- if old, ok := s.Tally[address]; ok {
- old.Votes++
- s.Tally[address] = old
- } else {
- s.Tally[address] = Tally{Authorize: authorize, Votes: 1}
- }
- return true
-}
-
-// uncast removes a previously cast vote from the tally.
-func (s *Snapshot) uncast(address common.Address, authorize bool) bool {
- // If there's no tally, it's a dangling vote, just drop
- tally, ok := s.Tally[address]
- if !ok {
- return false
- }
- // Ensure we only revert counted votes
- if tally.Authorize != authorize {
- return false
- }
- // Otherwise revert the vote
- if tally.Votes > 1 {
- tally.Votes--
- s.Tally[address] = tally
- } else {
- delete(s.Tally, address)
- }
- return true
-}
-
-// apply creates a new authorization snapshot by applying the given headers to
-// the original one.
-func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
- // Allow passing in no headers for cleaner code
- if len(headers) == 0 {
- return s, 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, errInvalidVotingChain
- }
- }
- if headers[0].Number.Uint64() != s.Number+1 {
- return nil, errInvalidVotingChain
- }
- // Iterate through the headers and create a new snapshot
- snap := s.copy()
-
- var (
- start = time.Now()
- logged = time.Now()
- )
- for i, header := range headers {
- // Remove any votes on checkpoint blocks
- number := header.Number.Uint64()
- if number%s.config.Epoch == 0 {
- snap.Votes = nil
- snap.Tally = make(map[common.Address]Tally)
- }
- // Delete the oldest signer from the recent list to allow it signing again
- if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
- delete(snap.Recents, number-limit)
- }
- // Resolve the authorization key and check against signers
- signer, err := ecrecover(header, s.sigcache)
- if err != nil {
- return nil, err
- }
- if _, ok := snap.Signers[signer]; !ok {
- return nil, errUnauthorizedSigner
- }
- for _, recent := range snap.Recents {
- if recent == signer {
- return nil, errRecentlySigned
- }
- }
- snap.Recents[number] = signer
-
- // Header authorized, discard any previous votes from the signer
- for i, vote := range snap.Votes {
- if vote.Signer == signer && vote.Address == header.Coinbase {
- // 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
- }
- }
- // Tally up the new vote from the signer
- var authorize bool
- switch {
- case bytes.Equal(header.Nonce[:], nonceAuthVote):
- authorize = true
- case bytes.Equal(header.Nonce[:], nonceDropVote):
- authorize = false
- default:
- return nil, errInvalidVote
- }
- if snap.cast(header.Coinbase, authorize) {
- snap.Votes = append(snap.Votes, &Vote{
- Signer: signer,
- Block: number,
- Address: header.Coinbase,
- Authorize: authorize,
- })
- }
- // If the vote passed, update the list of signers
- if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 {
- if tally.Authorize {
- snap.Signers[header.Coinbase] = struct{}{}
- } else {
- delete(snap.Signers, header.Coinbase)
-
- // Signer list shrunk, delete any leftover recent caches
- if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
- delete(snap.Recents, number-limit)
- }
- // Discard any previous votes the deauthorized signer cast
- for i := 0; i < len(snap.Votes); i++ {
- if snap.Votes[i].Signer == header.Coinbase {
- // 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 == header.Coinbase {
- snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
- i--
- }
- }
- delete(snap.Tally, header.Coinbase)
- }
- // If we're taking too much time (ecrecover), notify the user once a while
- if time.Since(logged) > 8*time.Second {
- log.Info("Reconstructing voting history", "processed", i, "total", len(headers), "elapsed", common.PrettyDuration(time.Since(start)))
- logged = time.Now()
- }
- }
- if time.Since(start) > 8*time.Second {
- log.Info("Reconstructed voting history", "processed", len(headers), "elapsed", common.PrettyDuration(time.Since(start)))
- }
- snap.Number += uint64(len(headers))
- snap.Hash = headers[len(headers)-1].Hash()
-
- return snap, nil
-}
-
-// signers retrieves the list of authorized signers in ascending order.
-func (s *Snapshot) signers() []common.Address {
- sigs := make([]common.Address, 0, len(s.Signers))
- for sig := range s.Signers {
- sigs = append(sigs, sig)
- }
- slices.SortFunc(sigs, common.Address.Cmp)
- return sigs
-}
-
-// inturn returns if a signer at a given block height is in-turn or not.
-func (s *Snapshot) inturn(number uint64, signer common.Address) bool {
- signers, offset := s.signers(), 0
- for offset < len(signers) && signers[offset] != signer {
- offset++
- }
- return (number % uint64(len(signers))) == uint64(offset)
-}
diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go
deleted file mode 100644
index 4ef7a7b3ae..0000000000
--- a/consensus/clique/snapshot_test.go
+++ /dev/null
@@ -1,508 +0,0 @@
-// 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 .
-
-package clique
-
-import (
- "bytes"
- "crypto/ecdsa"
- "fmt"
- "math/big"
- "slices"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/params"
-)
-
-// testerAccountPool is a pool to maintain currently active tester accounts,
-// mapped from textual names used in the tests below to actual Ethereum private
-// keys capable of signing transactions.
-type testerAccountPool struct {
- accounts map[string]*ecdsa.PrivateKey
-}
-
-func newTesterAccountPool() *testerAccountPool {
- return &testerAccountPool{
- accounts: make(map[string]*ecdsa.PrivateKey),
- }
-}
-
-// checkpoint creates a Clique checkpoint signer section from the provided list
-// of authorized signers and embeds it into the provided header.
-func (ap *testerAccountPool) checkpoint(header *types.Header, signers []string) {
- auths := make([]common.Address, len(signers))
- for i, signer := range signers {
- auths[i] = ap.address(signer)
- }
- slices.SortFunc(auths, common.Address.Cmp)
- for i, auth := range auths {
- copy(header.Extra[extraVanity+i*common.AddressLength:], auth.Bytes())
- }
-}
-
-// address retrieves the Ethereum address of a tester account by label, creating
-// a new account if no previous one exists yet.
-func (ap *testerAccountPool) address(account string) common.Address {
- // Return the zero account for non-addresses
- if account == "" {
- return common.Address{}
- }
- // Ensure we have a persistent key for the account
- if ap.accounts[account] == nil {
- ap.accounts[account], _ = crypto.GenerateKey()
- }
- // Resolve and return the Ethereum address
- return crypto.PubkeyToAddress(ap.accounts[account].PublicKey)
-}
-
-// sign calculates a Clique digital signature for the given block and embeds it
-// back into the header.
-func (ap *testerAccountPool) sign(header *types.Header, signer string) {
- // Ensure we have a persistent key for the signer
- if ap.accounts[signer] == nil {
- ap.accounts[signer], _ = crypto.GenerateKey()
- }
- // Sign the header and embed the signature in extra data
- sig, _ := crypto.Sign(SealHash(header).Bytes(), ap.accounts[signer])
- copy(header.Extra[len(header.Extra)-extraSeal:], sig)
-}
-
-// testerVote represents a single block signed by a particular account, where
-// the account may or may not have cast a Clique vote.
-type testerVote struct {
- signer string
- voted string
- auth bool
- checkpoint []string
- newbatch bool
-}
-
-type cliqueTest struct {
- epoch uint64
- signers []string
- votes []testerVote
- results []string
- failure error
-}
-
-// Tests that Clique signer voting is evaluated correctly for various simple and
-// complex scenarios, as well as that a few special corner cases fail correctly.
-func TestClique(t *testing.T) {
- // Define the various voting scenarios to test
- tests := []cliqueTest{
- {
- // Single signer, no votes cast
- signers: []string{"A"},
- votes: []testerVote{{signer: "A"}},
- results: []string{"A"},
- }, {
- // Single signer, voting to add two others (only accept first, second needs 2 votes)
- signers: []string{"A"},
- votes: []testerVote{
- {signer: "A", voted: "B", auth: true},
- {signer: "B"},
- {signer: "A", voted: "C", auth: true},
- },
- results: []string{"A", "B"},
- }, {
- // Two signers, voting to add three others (only accept first two, third needs 3 votes already)
- signers: []string{"A", "B"},
- votes: []testerVote{
- {signer: "A", voted: "C", auth: true},
- {signer: "B", voted: "C", auth: true},
- {signer: "A", voted: "D", auth: true},
- {signer: "B", voted: "D", auth: true},
- {signer: "C"},
- {signer: "A", voted: "E", auth: true},
- {signer: "B", voted: "E", auth: true},
- },
- results: []string{"A", "B", "C", "D"},
- }, {
- // Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this)
- signers: []string{"A"},
- votes: []testerVote{
- {signer: "A", voted: "A", auth: false},
- },
- results: []string{},
- }, {
- // Two signers, actually needing mutual consent to drop either of them (not fulfilled)
- signers: []string{"A", "B"},
- votes: []testerVote{
- {signer: "A", voted: "B", auth: false},
- },
- results: []string{"A", "B"},
- }, {
- // Two signers, actually needing mutual consent to drop either of them (fulfilled)
- signers: []string{"A", "B"},
- votes: []testerVote{
- {signer: "A", voted: "B", auth: false},
- {signer: "B", voted: "B", auth: false},
- },
- results: []string{"A"},
- }, {
- // Three signers, two of them deciding to drop the third
- signers: []string{"A", "B", "C"},
- votes: []testerVote{
- {signer: "A", voted: "C", auth: false},
- {signer: "B", voted: "C", auth: false},
- },
- results: []string{"A", "B"},
- }, {
- // Four signers, consensus of two not being enough to drop anyone
- signers: []string{"A", "B", "C", "D"},
- votes: []testerVote{
- {signer: "A", voted: "C", auth: false},
- {signer: "B", voted: "C", auth: false},
- },
- results: []string{"A", "B", "C", "D"},
- }, {
- // Four signers, consensus of three already being enough to drop someone
- signers: []string{"A", "B", "C", "D"},
- votes: []testerVote{
- {signer: "A", voted: "D", auth: false},
- {signer: "B", voted: "D", auth: false},
- {signer: "C", voted: "D", auth: false},
- },
- results: []string{"A", "B", "C"},
- }, {
- // Authorizations are counted once per signer per target
- signers: []string{"A", "B"},
- votes: []testerVote{
- {signer: "A", voted: "C", auth: true},
- {signer: "B"},
- {signer: "A", voted: "C", auth: true},
- {signer: "B"},
- {signer: "A", voted: "C", auth: true},
- },
- results: []string{"A", "B"},
- }, {
- // Authorizing multiple accounts concurrently is permitted
- signers: []string{"A", "B"},
- votes: []testerVote{
- {signer: "A", voted: "C", auth: true},
- {signer: "B"},
- {signer: "A", voted: "D", auth: true},
- {signer: "B"},
- {signer: "A"},
- {signer: "B", voted: "D", auth: true},
- {signer: "A"},
- {signer: "B", voted: "C", auth: true},
- },
- results: []string{"A", "B", "C", "D"},
- }, {
- // Deauthorizations are counted once per signer per target
- signers: []string{"A", "B"},
- votes: []testerVote{
- {signer: "A", voted: "B", auth: false},
- {signer: "B"},
- {signer: "A", voted: "B", auth: false},
- {signer: "B"},
- {signer: "A", voted: "B", auth: false},
- },
- results: []string{"A", "B"},
- }, {
- // Deauthorizing multiple accounts concurrently is permitted
- signers: []string{"A", "B", "C", "D"},
- votes: []testerVote{
- {signer: "A", voted: "C", auth: false},
- {signer: "B"},
- {signer: "C"},
- {signer: "A", voted: "D", auth: false},
- {signer: "B"},
- {signer: "C"},
- {signer: "A"},
- {signer: "B", voted: "D", auth: false},
- {signer: "C", voted: "D", auth: false},
- {signer: "A"},
- {signer: "B", voted: "C", auth: false},
- },
- results: []string{"A", "B"},
- }, {
- // Votes from deauthorized signers are discarded immediately (deauth votes)
- signers: []string{"A", "B", "C"},
- votes: []testerVote{
- {signer: "C", voted: "B", auth: false},
- {signer: "A", voted: "C", auth: false},
- {signer: "B", voted: "C", auth: false},
- {signer: "A", voted: "B", auth: false},
- },
- results: []string{"A", "B"},
- }, {
- // Votes from deauthorized signers are discarded immediately (auth votes)
- signers: []string{"A", "B", "C"},
- votes: []testerVote{
- {signer: "C", voted: "D", auth: true},
- {signer: "A", voted: "C", auth: false},
- {signer: "B", voted: "C", auth: false},
- {signer: "A", voted: "D", auth: true},
- },
- results: []string{"A", "B"},
- }, {
- // Cascading changes are not allowed, only the account being voted on may change
- signers: []string{"A", "B", "C", "D"},
- votes: []testerVote{
- {signer: "A", voted: "C", auth: false},
- {signer: "B"},
- {signer: "C"},
- {signer: "A", voted: "D", auth: false},
- {signer: "B", voted: "C", auth: false},
- {signer: "C"},
- {signer: "A"},
- {signer: "B", voted: "D", auth: false},
- {signer: "C", voted: "D", auth: false},
- },
- results: []string{"A", "B", "C"},
- }, {
- // Changes reaching consensus out of bounds (via a deauth) execute on touch
- signers: []string{"A", "B", "C", "D"},
- votes: []testerVote{
- {signer: "A", voted: "C", auth: false},
- {signer: "B"},
- {signer: "C"},
- {signer: "A", voted: "D", auth: false},
- {signer: "B", voted: "C", auth: false},
- {signer: "C"},
- {signer: "A"},
- {signer: "B", voted: "D", auth: false},
- {signer: "C", voted: "D", auth: false},
- {signer: "A"},
- {signer: "C", voted: "C", auth: true},
- },
- results: []string{"A", "B"},
- }, {
- // Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch
- signers: []string{"A", "B", "C", "D"},
- votes: []testerVote{
- {signer: "A", voted: "C", auth: false},
- {signer: "B"},
- {signer: "C"},
- {signer: "A", voted: "D", auth: false},
- {signer: "B", voted: "C", auth: false},
- {signer: "C"},
- {signer: "A"},
- {signer: "B", voted: "D", auth: false},
- {signer: "C", voted: "D", auth: false},
- {signer: "A"},
- {signer: "B", voted: "C", auth: true},
- },
- results: []string{"A", "B", "C"},
- }, {
- // Ensure that pending votes don't survive authorization status changes. This
- // corner case can only appear if a signer is quickly added, removed and then
- // re-added (or the inverse), while one of the original voters dropped. If a
- // past vote is left cached in the system somewhere, this will interfere with
- // the final signer outcome.
- signers: []string{"A", "B", "C", "D", "E"},
- votes: []testerVote{
- {signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed
- {signer: "B", voted: "F", auth: true},
- {signer: "C", voted: "F", auth: true},
- {signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged")
- {signer: "E", voted: "F", auth: false},
- {signer: "B", voted: "F", auth: false},
- {signer: "C", voted: "F", auth: false},
- {signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed
- {signer: "E", voted: "F", auth: true},
- {signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed
- {signer: "C", voted: "A", auth: false},
- {signer: "D", voted: "A", auth: false},
- {signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed
- },
- results: []string{"B", "C", "D", "E", "F"},
- }, {
- // Epoch transitions reset all votes to allow chain checkpointing
- epoch: 3,
- signers: []string{"A", "B"},
- votes: []testerVote{
- {signer: "A", voted: "C", auth: true},
- {signer: "B"},
- {signer: "A", checkpoint: []string{"A", "B"}},
- {signer: "B", voted: "C", auth: true},
- },
- results: []string{"A", "B"},
- }, {
- // An unauthorized signer should not be able to sign blocks
- signers: []string{"A"},
- votes: []testerVote{
- {signer: "B"},
- },
- failure: errUnauthorizedSigner,
- }, {
- // An authorized signer that signed recently should not be able to sign again
- signers: []string{"A", "B"},
- votes: []testerVote{
- {signer: "A"},
- {signer: "A"},
- },
- failure: errRecentlySigned,
- }, {
- // Recent signatures should not reset on checkpoint blocks imported in a batch
- epoch: 3,
- signers: []string{"A", "B", "C"},
- votes: []testerVote{
- {signer: "A"},
- {signer: "B"},
- {signer: "A", checkpoint: []string{"A", "B", "C"}},
- {signer: "A"},
- },
- failure: errRecentlySigned,
- }, {
- // Recent signatures should not reset on checkpoint blocks imported in a new
- // batch (https://github.com/ethereum/go-ethereum/issues/17593). Whilst this
- // seems overly specific and weird, it was a Rinkeby consensus split.
- epoch: 3,
- signers: []string{"A", "B", "C"},
- votes: []testerVote{
- {signer: "A"},
- {signer: "B"},
- {signer: "A", checkpoint: []string{"A", "B", "C"}},
- {signer: "A", newbatch: true},
- },
- failure: errRecentlySigned,
- },
- }
-
- // Run through the scenarios and test them
- for i, tt := range tests {
- t.Run(fmt.Sprint(i), tt.run)
- }
-}
-
-func (tt *cliqueTest) run(t *testing.T) {
- // Create the account pool and generate the initial set of signers
- accounts := newTesterAccountPool()
-
- signers := make([]common.Address, len(tt.signers))
- for j, signer := range tt.signers {
- signers[j] = accounts.address(signer)
- }
- for j := 0; j < len(signers); j++ {
- for k := j + 1; k < len(signers); k++ {
- if bytes.Compare(signers[j][:], signers[k][:]) > 0 {
- signers[j], signers[k] = signers[k], signers[j]
- }
- }
- }
- // Create the genesis block with the initial set of signers
- genesis := &core.Genesis{
- ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal),
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
- for j, signer := range signers {
- copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:])
- }
-
- // Assemble a chain of headers from the cast votes
- config := *params.TestChainConfig
- config.Clique = ¶ms.CliqueConfig{
- Period: 1,
- Epoch: tt.epoch,
- }
- genesis.Config = &config
-
- engine := New(config.Clique, rawdb.NewMemoryDatabase())
- engine.fakeDiff = true
-
- _, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, len(tt.votes), func(j int, gen *core.BlockGen) {
- // Cast the vote contained in this block
- gen.SetCoinbase(accounts.address(tt.votes[j].voted))
- if tt.votes[j].auth {
- var nonce types.BlockNonce
- copy(nonce[:], nonceAuthVote)
- gen.SetNonce(nonce)
- }
- })
- // Iterate through the blocks and seal them individually
- for j, block := range blocks {
- // Get the header and prepare it for signing
- header := block.Header()
- if j > 0 {
- header.ParentHash = blocks[j-1].Hash()
- }
- header.Extra = make([]byte, extraVanity+extraSeal)
- if auths := tt.votes[j].checkpoint; auths != nil {
- header.Extra = make([]byte, extraVanity+len(auths)*common.AddressLength+extraSeal)
- accounts.checkpoint(header, auths)
- }
- header.Difficulty = diffInTurn // Ignored, we just need a valid number
-
- // Generate the signature, embed it into the header and the block
- accounts.sign(header, tt.votes[j].signer)
- blocks[j] = block.WithSeal(header)
- }
- // Split the blocks up into individual import batches (cornercase testing)
- batches := [][]*types.Block{nil}
- for j, block := range blocks {
- if tt.votes[j].newbatch {
- batches = append(batches, nil)
- }
- batches[len(batches)-1] = append(batches[len(batches)-1], block)
- }
- // Pass all the headers through clique and ensure tallying succeeds
- chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genesis, nil, engine, vm.Config{}, nil, nil)
- if err != nil {
- t.Fatalf("failed to create test chain: %v", err)
- }
- defer chain.Stop()
-
- for j := 0; j < len(batches)-1; j++ {
- if k, err := chain.InsertChain(batches[j]); err != nil {
- t.Fatalf("failed to import batch %d, block %d: %v", j, k, err)
- break
- }
- }
- if _, err = chain.InsertChain(batches[len(batches)-1]); err != tt.failure {
- t.Errorf("failure mismatch: have %v, want %v", err, tt.failure)
- }
- if tt.failure != nil {
- return
- }
-
- // No failure was produced or requested, generate the final voting snapshot
- head := blocks[len(blocks)-1]
-
- snap, err := engine.snapshot(chain, head.NumberU64(), head.Hash(), nil)
- if err != nil {
- t.Fatalf("failed to retrieve voting snapshot: %v", err)
- }
- // Verify the final list of signers against the expected ones
- signers = make([]common.Address, len(tt.results))
- for j, signer := range tt.results {
- signers[j] = accounts.address(signer)
- }
- for j := 0; j < len(signers); j++ {
- for k := j + 1; k < len(signers); k++ {
- if bytes.Compare(signers[j][:], signers[k][:]) > 0 {
- signers[j], signers[k] = signers[k], signers[j]
- }
- }
- }
- result := snap.signers()
- if len(result) != len(signers) {
- t.Fatalf("signers mismatch: have %x, want %x", result, signers)
- }
- for j := 0; j < len(result); j++ {
- if !bytes.Equal(result[j][:], signers[j][:]) {
- t.Fatalf("signer %d: signer mismatch: have %x, want %x", j, result[j], signers[j])
- }
- }
-}
diff --git a/core/block_validator_test.go b/core/block_validator_test.go
index 2f86b2d751..3e54d95f28 100644
--- a/core/block_validator_test.go
+++ b/core/block_validator_test.go
@@ -21,15 +21,12 @@ import (
"testing"
"time"
- "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/beacon"
- "github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
@@ -95,61 +92,21 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
postBlocks []*types.Block
engine consensus.Engine
)
- if isClique {
- var (
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- addr = crypto.PubkeyToAddress(key.PublicKey)
- config = *params.AllCliqueProtocolChanges
- )
- engine = beacon.New(clique.New(params.AllCliqueProtocolChanges.Clique, rawdb.NewMemoryDatabase()))
- gspec = &Genesis{
- Config: &config,
- ExtraData: make([]byte, 32+common.AddressLength+crypto.SignatureLength),
- Alloc: map[common.Address]types.Account{
- addr: {Balance: big.NewInt(1)},
- },
- BaseFee: big.NewInt(params.InitialBaseFee),
- Difficulty: new(big.Int),
- }
- copy(gspec.ExtraData[32:], addr[:])
-
- td := 0
- genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 8, nil)
- for i, block := range blocks {
- header := block.Header()
- if i > 0 {
- header.ParentHash = blocks[i-1].Hash()
- }
- header.Extra = make([]byte, 32+crypto.SignatureLength)
- header.Difficulty = big.NewInt(2)
-
- sig, _ := crypto.Sign(engine.SealHash(header).Bytes(), key)
- copy(header.Extra[len(header.Extra)-crypto.SignatureLength:], sig)
- blocks[i] = block.WithSeal(header)
-
- // calculate td
- td += int(block.Difficulty().Uint64())
- }
- preBlocks = blocks
- gspec.Config.TerminalTotalDifficulty = big.NewInt(int64(td))
- postBlocks, _ = GenerateChain(gspec.Config, preBlocks[len(preBlocks)-1], engine, genDb, 8, nil)
- } else {
- config := *params.TestChainConfig
- gspec = &Genesis{Config: &config}
- engine = beacon.New(ethash.NewFaker())
- td := int(params.GenesisDifficulty.Uint64())
- genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 8, nil)
- for _, block := range blocks {
- // calculate td
- td += int(block.Difficulty().Uint64())
- }
- preBlocks = blocks
- gspec.Config.TerminalTotalDifficulty = big.NewInt(int64(td))
- t.Logf("Set ttd to %v\n", gspec.Config.TerminalTotalDifficulty)
- postBlocks, _ = GenerateChain(gspec.Config, preBlocks[len(preBlocks)-1], engine, genDb, 8, func(i int, gen *BlockGen) {
- gen.SetPoS()
- })
+ config := *params.TestChainConfig
+ gspec = &Genesis{Config: &config}
+ engine = beacon.New(ethash.NewFaker())
+ td := int(params.GenesisDifficulty.Uint64())
+ genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 8, nil)
+ for _, block := range blocks {
+ // calculate td
+ td += int(block.Difficulty().Uint64())
}
+ preBlocks = blocks
+ gspec.Config.TerminalTotalDifficulty = big.NewInt(int64(td))
+ t.Logf("Set ttd to %v\n", gspec.Config.TerminalTotalDifficulty)
+ postBlocks, _ = GenerateChain(gspec.Config, preBlocks[len(preBlocks)-1], engine, genDb, 8, func(i int, gen *BlockGen) {
+ gen.SetPoS()
+ })
// Assemble header batch
preHeaders := make([]*types.Header, len(preBlocks))
for i, block := range preBlocks {
diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go
index f36f212d9c..3b97db59a0 100644
--- a/eth/ethconfig/config.go
+++ b/eth/ethconfig/config.go
@@ -176,7 +176,7 @@ func CreateConsensusEngine(config *params.ChainConfig, db ethdb.Database) (conse
}
// Wrap previously supported consensus engines into their post-merge counterpart
if config.Clique != nil {
- return beacon.New(clique.New(config.Clique, db)), nil
+ return beacon.New(clique.New()), nil
}
return beacon.New(ethash.NewFaker()), nil
}
diff --git a/miner/miner_test.go b/miner/miner_test.go
index da133ad8d0..e666bbd93f 100644
--- a/miner/miner_test.go
+++ b/miner/miner_test.go
@@ -22,8 +22,10 @@ import (
"sync"
"testing"
+ "github.com/ethereum/go-ethereum/consensus/beacon"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
+
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
@@ -108,11 +110,7 @@ func TestBuildPendingBlocks(t *testing.T) {
}
func minerTestGenesisBlock(period uint64, gasLimit uint64, faucet common.Address) *core.Genesis {
- config := *params.AllCliqueProtocolChanges
- config.Clique = ¶ms.CliqueConfig{
- Period: period,
- Epoch: config.Clique.Epoch,
- }
+ config := *params.AllDevChainProtocolChanges
// Assemble and return the genesis with the precompiles and faucet pre-funded
return &core.Genesis{
@@ -150,7 +148,7 @@ func createMiner(t *testing.T) *Miner {
t.Fatalf("can't create new chain config: %v", err)
}
// Create consensus engine
- engine := clique.New(chainConfig.Clique, chainDB)
+ engine := beacon.New(ethash.NewFaker())
// Create Ethereum backend
bc, err := core.NewBlockChain(chainDB, nil, genesis, nil, engine, vm.Config{}, nil, nil)
if err != nil {
diff --git a/params/config.go b/params/config.go
index 534e57831a..cc12f077ed 100644
--- a/params/config.go
+++ b/params/config.go
@@ -188,36 +188,6 @@ var (
TerminalTotalDifficultyPassed: true,
}
- // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
- // and accepted by the Ethereum core developers into the Clique consensus.
- AllCliqueProtocolChanges = &ChainConfig{
- ChainID: big.NewInt(1337),
- HomesteadBlock: big.NewInt(0),
- DAOForkBlock: nil,
- DAOForkSupport: false,
- EIP150Block: big.NewInt(0),
- EIP155Block: big.NewInt(0),
- EIP158Block: big.NewInt(0),
- ByzantiumBlock: big.NewInt(0),
- ConstantinopleBlock: big.NewInt(0),
- PetersburgBlock: big.NewInt(0),
- IstanbulBlock: big.NewInt(0),
- MuirGlacierBlock: big.NewInt(0),
- BerlinBlock: big.NewInt(0),
- LondonBlock: big.NewInt(0),
- ArrowGlacierBlock: nil,
- GrayGlacierBlock: nil,
- MergeNetsplitBlock: nil,
- ShanghaiTime: nil,
- CancunTime: nil,
- PragueTime: nil,
- VerkleTime: nil,
- TerminalTotalDifficulty: nil,
- TerminalTotalDifficultyPassed: false,
- Ethash: nil,
- Clique: &CliqueConfig{Period: 0, Epoch: 30000},
- }
-
// TestChainConfig contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers for testing purposes.
TestChainConfig = &ChainConfig{
diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go
index f8b3c9d86d..c45256fd37 100644
--- a/signer/core/signed_data.go
+++ b/signer/core/signed_data.go
@@ -26,10 +26,7 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/consensus/clique"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
)
@@ -135,35 +132,7 @@ func (api *SignerAPI) determineSignatureFormat(ctx context.Context, contentType
}
req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash}
case apitypes.ApplicationClique.Mime:
- // Clique is the Ethereum PoA standard
- cliqueData, err := fromHex(data)
- if err != nil {
- return nil, useEthereumV, err
- }
- header := &types.Header{}
- if err := rlp.DecodeBytes(cliqueData, header); err != nil {
- return nil, useEthereumV, err
- }
- // Add space in the extradata to put the signature
- newExtra := make([]byte, len(header.Extra)+65)
- copy(newExtra, header.Extra)
- header.Extra = newExtra
-
- // Get back the rlp data, encoded by us
- sighash, cliqueRlp, err := cliqueHeaderHashAndRlp(header)
- if err != nil {
- return nil, useEthereumV, err
- }
- messages := []*apitypes.NameValueType{
- {
- Name: "Clique header",
- Typ: "clique",
- Value: fmt.Sprintf("clique header %d [%#x]", header.Number, header.Hash()),
- },
- }
- // Clique uses V on the form 0 or 1
- useEthereumV = false
- req = &SignDataRequest{ContentType: mediaType, Rawdata: cliqueRlp, Messages: messages, Hash: sighash}
+ return nil, false, errors.New("clique signing is deprecated")
case apitypes.DataTyped.Mime:
// EIP-712 conformant typed data
var err error
@@ -202,23 +171,6 @@ func SignTextValidator(validatorData apitypes.ValidatorData) (hexutil.Bytes, str
return crypto.Keccak256([]byte(msg)), msg
}
-// cliqueHeaderHashAndRlp returns the hash which is used as input for the proof-of-authority
-// signing. It is the hash of the entire header apart from the 65 byte signature
-// contained at the end of the extra data.
-//
-// The method requires the extra data to be at least 65 bytes -- the original implementation
-// in clique.go panics if this is the case, thus it's been reimplemented here to avoid the panic
-// and simply return an error instead
-func cliqueHeaderHashAndRlp(header *types.Header) (hash, rlp []byte, err error) {
- if len(header.Extra) < 65 {
- err = fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra))
- return
- }
- rlp = clique.CliqueRLP(header)
- hash = clique.SealHash(header).Bytes()
- return hash, rlp, err
-}
-
// SignTypedData signs EIP-712 conformant typed data
// hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}")
// It returns