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