github.com/ethereum/go-ethereum@v1.16.1/consensus/clique/snapshot_test.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package clique
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"fmt"
    23  	"math/big"
    24  	"slices"
    25  	"testing"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/core"
    29  	"github.com/ethereum/go-ethereum/core/rawdb"
    30  	"github.com/ethereum/go-ethereum/core/types"
    31  	"github.com/ethereum/go-ethereum/crypto"
    32  	"github.com/ethereum/go-ethereum/params"
    33  )
    34  
    35  // testerAccountPool is a pool to maintain currently active tester accounts,
    36  // mapped from textual names used in the tests below to actual Ethereum private
    37  // keys capable of signing transactions.
    38  type testerAccountPool struct {
    39  	accounts map[string]*ecdsa.PrivateKey
    40  }
    41  
    42  func newTesterAccountPool() *testerAccountPool {
    43  	return &testerAccountPool{
    44  		accounts: make(map[string]*ecdsa.PrivateKey),
    45  	}
    46  }
    47  
    48  // checkpoint creates a Clique checkpoint signer section from the provided list
    49  // of authorized signers and embeds it into the provided header.
    50  func (ap *testerAccountPool) checkpoint(header *types.Header, signers []string) {
    51  	auths := make([]common.Address, len(signers))
    52  	for i, signer := range signers {
    53  		auths[i] = ap.address(signer)
    54  	}
    55  	slices.SortFunc(auths, common.Address.Cmp)
    56  	for i, auth := range auths {
    57  		copy(header.Extra[extraVanity+i*common.AddressLength:], auth.Bytes())
    58  	}
    59  }
    60  
    61  // address retrieves the Ethereum address of a tester account by label, creating
    62  // a new account if no previous one exists yet.
    63  func (ap *testerAccountPool) address(account string) common.Address {
    64  	// Return the zero account for non-addresses
    65  	if account == "" {
    66  		return common.Address{}
    67  	}
    68  	// Ensure we have a persistent key for the account
    69  	if ap.accounts[account] == nil {
    70  		ap.accounts[account], _ = crypto.GenerateKey()
    71  	}
    72  	// Resolve and return the Ethereum address
    73  	return crypto.PubkeyToAddress(ap.accounts[account].PublicKey)
    74  }
    75  
    76  // sign calculates a Clique digital signature for the given block and embeds it
    77  // back into the header.
    78  func (ap *testerAccountPool) sign(header *types.Header, signer string) {
    79  	// Ensure we have a persistent key for the signer
    80  	if ap.accounts[signer] == nil {
    81  		ap.accounts[signer], _ = crypto.GenerateKey()
    82  	}
    83  	// Sign the header and embed the signature in extra data
    84  	sig, _ := crypto.Sign(SealHash(header).Bytes(), ap.accounts[signer])
    85  	copy(header.Extra[len(header.Extra)-extraSeal:], sig)
    86  }
    87  
    88  // testerVote represents a single block signed by a particular account, where
    89  // the account may or may not have cast a Clique vote.
    90  type testerVote struct {
    91  	signer     string
    92  	voted      string
    93  	auth       bool
    94  	checkpoint []string
    95  	newbatch   bool
    96  }
    97  
    98  type cliqueTest struct {
    99  	epoch   uint64
   100  	signers []string
   101  	votes   []testerVote
   102  	results []string
   103  	failure error
   104  }
   105  
   106  // Tests that Clique signer voting is evaluated correctly for various simple and
   107  // complex scenarios, as well as that a few special corner cases fail correctly.
   108  func TestClique(t *testing.T) {
   109  	// Define the various voting scenarios to test
   110  	tests := []cliqueTest{
   111  		{
   112  			// Single signer, no votes cast
   113  			signers: []string{"A"},
   114  			votes:   []testerVote{{signer: "A"}},
   115  			results: []string{"A"},
   116  		}, {
   117  			// Single signer, voting to add two others (only accept first, second needs 2 votes)
   118  			signers: []string{"A"},
   119  			votes: []testerVote{
   120  				{signer: "A", voted: "B", auth: true},
   121  				{signer: "B"},
   122  				{signer: "A", voted: "C", auth: true},
   123  			},
   124  			results: []string{"A", "B"},
   125  		}, {
   126  			// Two signers, voting to add three others (only accept first two, third needs 3 votes already)
   127  			signers: []string{"A", "B"},
   128  			votes: []testerVote{
   129  				{signer: "A", voted: "C", auth: true},
   130  				{signer: "B", voted: "C", auth: true},
   131  				{signer: "A", voted: "D", auth: true},
   132  				{signer: "B", voted: "D", auth: true},
   133  				{signer: "C"},
   134  				{signer: "A", voted: "E", auth: true},
   135  				{signer: "B", voted: "E", auth: true},
   136  			},
   137  			results: []string{"A", "B", "C", "D"},
   138  		}, {
   139  			// Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this)
   140  			signers: []string{"A"},
   141  			votes: []testerVote{
   142  				{signer: "A", voted: "A", auth: false},
   143  			},
   144  			results: []string{},
   145  		}, {
   146  			// Two signers, actually needing mutual consent to drop either of them (not fulfilled)
   147  			signers: []string{"A", "B"},
   148  			votes: []testerVote{
   149  				{signer: "A", voted: "B", auth: false},
   150  			},
   151  			results: []string{"A", "B"},
   152  		}, {
   153  			// Two signers, actually needing mutual consent to drop either of them (fulfilled)
   154  			signers: []string{"A", "B"},
   155  			votes: []testerVote{
   156  				{signer: "A", voted: "B", auth: false},
   157  				{signer: "B", voted: "B", auth: false},
   158  			},
   159  			results: []string{"A"},
   160  		}, {
   161  			// Three signers, two of them deciding to drop the third
   162  			signers: []string{"A", "B", "C"},
   163  			votes: []testerVote{
   164  				{signer: "A", voted: "C", auth: false},
   165  				{signer: "B", voted: "C", auth: false},
   166  			},
   167  			results: []string{"A", "B"},
   168  		}, {
   169  			// Four signers, consensus of two not being enough to drop anyone
   170  			signers: []string{"A", "B", "C", "D"},
   171  			votes: []testerVote{
   172  				{signer: "A", voted: "C", auth: false},
   173  				{signer: "B", voted: "C", auth: false},
   174  			},
   175  			results: []string{"A", "B", "C", "D"},
   176  		}, {
   177  			// Four signers, consensus of three already being enough to drop someone
   178  			signers: []string{"A", "B", "C", "D"},
   179  			votes: []testerVote{
   180  				{signer: "A", voted: "D", auth: false},
   181  				{signer: "B", voted: "D", auth: false},
   182  				{signer: "C", voted: "D", auth: false},
   183  			},
   184  			results: []string{"A", "B", "C"},
   185  		}, {
   186  			// Authorizations are counted once per signer per target
   187  			signers: []string{"A", "B"},
   188  			votes: []testerVote{
   189  				{signer: "A", voted: "C", auth: true},
   190  				{signer: "B"},
   191  				{signer: "A", voted: "C", auth: true},
   192  				{signer: "B"},
   193  				{signer: "A", voted: "C", auth: true},
   194  			},
   195  			results: []string{"A", "B"},
   196  		}, {
   197  			// Authorizing multiple accounts concurrently is permitted
   198  			signers: []string{"A", "B"},
   199  			votes: []testerVote{
   200  				{signer: "A", voted: "C", auth: true},
   201  				{signer: "B"},
   202  				{signer: "A", voted: "D", auth: true},
   203  				{signer: "B"},
   204  				{signer: "A"},
   205  				{signer: "B", voted: "D", auth: true},
   206  				{signer: "A"},
   207  				{signer: "B", voted: "C", auth: true},
   208  			},
   209  			results: []string{"A", "B", "C", "D"},
   210  		}, {
   211  			// Deauthorizations are counted once per signer per target
   212  			signers: []string{"A", "B"},
   213  			votes: []testerVote{
   214  				{signer: "A", voted: "B", auth: false},
   215  				{signer: "B"},
   216  				{signer: "A", voted: "B", auth: false},
   217  				{signer: "B"},
   218  				{signer: "A", voted: "B", auth: false},
   219  			},
   220  			results: []string{"A", "B"},
   221  		}, {
   222  			// Deauthorizing multiple accounts concurrently is permitted
   223  			signers: []string{"A", "B", "C", "D"},
   224  			votes: []testerVote{
   225  				{signer: "A", voted: "C", auth: false},
   226  				{signer: "B"},
   227  				{signer: "C"},
   228  				{signer: "A", voted: "D", auth: false},
   229  				{signer: "B"},
   230  				{signer: "C"},
   231  				{signer: "A"},
   232  				{signer: "B", voted: "D", auth: false},
   233  				{signer: "C", voted: "D", auth: false},
   234  				{signer: "A"},
   235  				{signer: "B", voted: "C", auth: false},
   236  			},
   237  			results: []string{"A", "B"},
   238  		}, {
   239  			// Votes from deauthorized signers are discarded immediately (deauth votes)
   240  			signers: []string{"A", "B", "C"},
   241  			votes: []testerVote{
   242  				{signer: "C", voted: "B", auth: false},
   243  				{signer: "A", voted: "C", auth: false},
   244  				{signer: "B", voted: "C", auth: false},
   245  				{signer: "A", voted: "B", auth: false},
   246  			},
   247  			results: []string{"A", "B"},
   248  		}, {
   249  			// Votes from deauthorized signers are discarded immediately (auth votes)
   250  			signers: []string{"A", "B", "C"},
   251  			votes: []testerVote{
   252  				{signer: "C", voted: "D", auth: true},
   253  				{signer: "A", voted: "C", auth: false},
   254  				{signer: "B", voted: "C", auth: false},
   255  				{signer: "A", voted: "D", auth: true},
   256  			},
   257  			results: []string{"A", "B"},
   258  		}, {
   259  			// Cascading changes are not allowed, only the account being voted on may change
   260  			signers: []string{"A", "B", "C", "D"},
   261  			votes: []testerVote{
   262  				{signer: "A", voted: "C", auth: false},
   263  				{signer: "B"},
   264  				{signer: "C"},
   265  				{signer: "A", voted: "D", auth: false},
   266  				{signer: "B", voted: "C", auth: false},
   267  				{signer: "C"},
   268  				{signer: "A"},
   269  				{signer: "B", voted: "D", auth: false},
   270  				{signer: "C", voted: "D", auth: false},
   271  			},
   272  			results: []string{"A", "B", "C"},
   273  		}, {
   274  			// Changes reaching consensus out of bounds (via a deauth) execute on touch
   275  			signers: []string{"A", "B", "C", "D"},
   276  			votes: []testerVote{
   277  				{signer: "A", voted: "C", auth: false},
   278  				{signer: "B"},
   279  				{signer: "C"},
   280  				{signer: "A", voted: "D", auth: false},
   281  				{signer: "B", voted: "C", auth: false},
   282  				{signer: "C"},
   283  				{signer: "A"},
   284  				{signer: "B", voted: "D", auth: false},
   285  				{signer: "C", voted: "D", auth: false},
   286  				{signer: "A"},
   287  				{signer: "C", voted: "C", auth: true},
   288  			},
   289  			results: []string{"A", "B"},
   290  		}, {
   291  			// Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch
   292  			signers: []string{"A", "B", "C", "D"},
   293  			votes: []testerVote{
   294  				{signer: "A", voted: "C", auth: false},
   295  				{signer: "B"},
   296  				{signer: "C"},
   297  				{signer: "A", voted: "D", auth: false},
   298  				{signer: "B", voted: "C", auth: false},
   299  				{signer: "C"},
   300  				{signer: "A"},
   301  				{signer: "B", voted: "D", auth: false},
   302  				{signer: "C", voted: "D", auth: false},
   303  				{signer: "A"},
   304  				{signer: "B", voted: "C", auth: true},
   305  			},
   306  			results: []string{"A", "B", "C"},
   307  		}, {
   308  			// Ensure that pending votes don't survive authorization status changes. This
   309  			// corner case can only appear if a signer is quickly added, removed and then
   310  			// re-added (or the inverse), while one of the original voters dropped. If a
   311  			// past vote is left cached in the system somewhere, this will interfere with
   312  			// the final signer outcome.
   313  			signers: []string{"A", "B", "C", "D", "E"},
   314  			votes: []testerVote{
   315  				{signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed
   316  				{signer: "B", voted: "F", auth: true},
   317  				{signer: "C", voted: "F", auth: true},
   318  				{signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged")
   319  				{signer: "E", voted: "F", auth: false},
   320  				{signer: "B", voted: "F", auth: false},
   321  				{signer: "C", voted: "F", auth: false},
   322  				{signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed
   323  				{signer: "E", voted: "F", auth: true},
   324  				{signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed
   325  				{signer: "C", voted: "A", auth: false},
   326  				{signer: "D", voted: "A", auth: false},
   327  				{signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed
   328  			},
   329  			results: []string{"B", "C", "D", "E", "F"},
   330  		}, {
   331  			// Epoch transitions reset all votes to allow chain checkpointing
   332  			epoch:   3,
   333  			signers: []string{"A", "B"},
   334  			votes: []testerVote{
   335  				{signer: "A", voted: "C", auth: true},
   336  				{signer: "B"},
   337  				{signer: "A", checkpoint: []string{"A", "B"}},
   338  				{signer: "B", voted: "C", auth: true},
   339  			},
   340  			results: []string{"A", "B"},
   341  		}, {
   342  			// An unauthorized signer should not be able to sign blocks
   343  			signers: []string{"A"},
   344  			votes: []testerVote{
   345  				{signer: "B"},
   346  			},
   347  			failure: errUnauthorizedSigner,
   348  		}, {
   349  			// An authorized signer that signed recently should not be able to sign again
   350  			signers: []string{"A", "B"},
   351  			votes: []testerVote{
   352  				{signer: "A"},
   353  				{signer: "A"},
   354  			},
   355  			failure: errRecentlySigned,
   356  		}, {
   357  			// Recent signatures should not reset on checkpoint blocks imported in a batch
   358  			epoch:   3,
   359  			signers: []string{"A", "B", "C"},
   360  			votes: []testerVote{
   361  				{signer: "A"},
   362  				{signer: "B"},
   363  				{signer: "A", checkpoint: []string{"A", "B", "C"}},
   364  				{signer: "A"},
   365  			},
   366  			failure: errRecentlySigned,
   367  		}, {
   368  			// Recent signatures should not reset on checkpoint blocks imported in a new
   369  			// batch (https://github.com/ethereum/go-ethereum/issues/17593). Whilst this
   370  			// seems overly specific and weird, it was a Rinkeby consensus split.
   371  			epoch:   3,
   372  			signers: []string{"A", "B", "C"},
   373  			votes: []testerVote{
   374  				{signer: "A"},
   375  				{signer: "B"},
   376  				{signer: "A", checkpoint: []string{"A", "B", "C"}},
   377  				{signer: "A", newbatch: true},
   378  			},
   379  			failure: errRecentlySigned,
   380  		},
   381  	}
   382  
   383  	// Run through the scenarios and test them
   384  	for i, tt := range tests {
   385  		t.Run(fmt.Sprint(i), tt.run)
   386  	}
   387  }
   388  
   389  func (tt *cliqueTest) run(t *testing.T) {
   390  	// Create the account pool and generate the initial set of signers
   391  	accounts := newTesterAccountPool()
   392  
   393  	signers := make([]common.Address, len(tt.signers))
   394  	for j, signer := range tt.signers {
   395  		signers[j] = accounts.address(signer)
   396  	}
   397  	for j := 0; j < len(signers); j++ {
   398  		for k := j + 1; k < len(signers); k++ {
   399  			if bytes.Compare(signers[j][:], signers[k][:]) > 0 {
   400  				signers[j], signers[k] = signers[k], signers[j]
   401  			}
   402  		}
   403  	}
   404  	// Create the genesis block with the initial set of signers
   405  	genesis := &core.Genesis{
   406  		ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal),
   407  		BaseFee:   big.NewInt(params.InitialBaseFee),
   408  	}
   409  	for j, signer := range signers {
   410  		copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:])
   411  	}
   412  
   413  	// Assemble a chain of headers from the cast votes
   414  	config := *params.TestChainConfig
   415  	config.Clique = &params.CliqueConfig{
   416  		Period: 1,
   417  		Epoch:  tt.epoch,
   418  	}
   419  	genesis.Config = &config
   420  
   421  	engine := New(config.Clique, rawdb.NewMemoryDatabase())
   422  	engine.fakeDiff = true
   423  
   424  	_, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, len(tt.votes), func(j int, gen *core.BlockGen) {
   425  		// Cast the vote contained in this block
   426  		gen.SetCoinbase(accounts.address(tt.votes[j].voted))
   427  		if tt.votes[j].auth {
   428  			var nonce types.BlockNonce
   429  			copy(nonce[:], nonceAuthVote)
   430  			gen.SetNonce(nonce)
   431  		}
   432  	})
   433  	// Iterate through the blocks and seal them individually
   434  	for j, block := range blocks {
   435  		// Get the header and prepare it for signing
   436  		header := block.Header()
   437  		if j > 0 {
   438  			header.ParentHash = blocks[j-1].Hash()
   439  		}
   440  		header.Extra = make([]byte, extraVanity+extraSeal)
   441  		if auths := tt.votes[j].checkpoint; auths != nil {
   442  			header.Extra = make([]byte, extraVanity+len(auths)*common.AddressLength+extraSeal)
   443  			accounts.checkpoint(header, auths)
   444  		}
   445  		header.Difficulty = diffInTurn // Ignored, we just need a valid number
   446  
   447  		// Generate the signature, embed it into the header and the block
   448  		accounts.sign(header, tt.votes[j].signer)
   449  		blocks[j] = block.WithSeal(header)
   450  	}
   451  	// Split the blocks up into individual import batches (cornercase testing)
   452  	batches := [][]*types.Block{nil}
   453  	for j, block := range blocks {
   454  		if tt.votes[j].newbatch {
   455  			batches = append(batches, nil)
   456  		}
   457  		batches[len(batches)-1] = append(batches[len(batches)-1], block)
   458  	}
   459  	// Pass all the headers through clique and ensure tallying succeeds
   460  	chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), genesis, engine, nil)
   461  	if err != nil {
   462  		t.Fatalf("failed to create test chain: %v", err)
   463  	}
   464  	defer chain.Stop()
   465  
   466  	for j := 0; j < len(batches)-1; j++ {
   467  		if k, err := chain.InsertChain(batches[j]); err != nil {
   468  			t.Fatalf("failed to import batch %d, block %d: %v", j, k, err)
   469  		}
   470  	}
   471  	if _, err = chain.InsertChain(batches[len(batches)-1]); err != tt.failure {
   472  		t.Errorf("failure mismatch: have %v, want %v", err, tt.failure)
   473  	}
   474  	if tt.failure != nil {
   475  		return
   476  	}
   477  
   478  	// No failure was produced or requested, generate the final voting snapshot
   479  	head := blocks[len(blocks)-1]
   480  
   481  	snap, err := engine.snapshot(chain, head.NumberU64(), head.Hash(), nil)
   482  	if err != nil {
   483  		t.Fatalf("failed to retrieve voting snapshot: %v", err)
   484  	}
   485  	// Verify the final list of signers against the expected ones
   486  	signers = make([]common.Address, len(tt.results))
   487  	for j, signer := range tt.results {
   488  		signers[j] = accounts.address(signer)
   489  	}
   490  	for j := 0; j < len(signers); j++ {
   491  		for k := j + 1; k < len(signers); k++ {
   492  			if bytes.Compare(signers[j][:], signers[k][:]) > 0 {
   493  				signers[j], signers[k] = signers[k], signers[j]
   494  			}
   495  		}
   496  	}
   497  	result := snap.signers()
   498  	if len(result) != len(signers) {
   499  		t.Fatalf("signers mismatch: have %x, want %x", result, signers)
   500  	}
   501  	for j := 0; j < len(result); j++ {
   502  		if !bytes.Equal(result[j][:], signers[j][:]) {
   503  			t.Fatalf("signer %d: signer mismatch: have %x, want %x", j, result[j], signers[j])
   504  		}
   505  	}
   506  }