github.com/luckypickle/go-ethereum-vet@v1.14.2/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  	"math/big"
    23  	"testing"
    24  
    25  	"github.com/luckypickle/go-ethereum-vet/common"
    26  	"github.com/luckypickle/go-ethereum-vet/core"
    27  	"github.com/luckypickle/go-ethereum-vet/core/rawdb"
    28  	"github.com/luckypickle/go-ethereum-vet/core/types"
    29  	"github.com/luckypickle/go-ethereum-vet/crypto"
    30  	"github.com/luckypickle/go-ethereum-vet/ethdb"
    31  	"github.com/luckypickle/go-ethereum-vet/params"
    32  )
    33  
    34  type testerVote struct {
    35  	signer string
    36  	voted  string
    37  	auth   bool
    38  }
    39  
    40  // testerAccountPool is a pool to maintain currently active tester accounts,
    41  // mapped from textual names used in the tests below to actual Ethereum private
    42  // keys capable of signing transactions.
    43  type testerAccountPool struct {
    44  	accounts map[string]*ecdsa.PrivateKey
    45  }
    46  
    47  func newTesterAccountPool() *testerAccountPool {
    48  	return &testerAccountPool{
    49  		accounts: make(map[string]*ecdsa.PrivateKey),
    50  	}
    51  }
    52  
    53  func (ap *testerAccountPool) sign(header *types.Header, signer string) {
    54  	// Ensure we have a persistent key for the signer
    55  	if ap.accounts[signer] == nil {
    56  		ap.accounts[signer], _ = crypto.GenerateKey()
    57  	}
    58  	// Sign the header and embed the signature in extra data
    59  	sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer])
    60  	copy(header.Extra[len(header.Extra)-65:], sig)
    61  }
    62  
    63  func (ap *testerAccountPool) address(account string) common.Address {
    64  	// Ensure we have a persistent key for the account
    65  	if ap.accounts[account] == nil {
    66  		ap.accounts[account], _ = crypto.GenerateKey()
    67  	}
    68  	// Resolve and return the Ethereum address
    69  	return crypto.PubkeyToAddress(ap.accounts[account].PublicKey)
    70  }
    71  
    72  // testerChainReader implements consensus.ChainReader to access the genesis
    73  // block. All other methods and requests will panic.
    74  type testerChainReader struct {
    75  	db ethdb.Database
    76  }
    77  
    78  func (r *testerChainReader) Config() *params.ChainConfig                 { return params.AllCliqueProtocolChanges }
    79  func (r *testerChainReader) CurrentHeader() *types.Header                { panic("not supported") }
    80  func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") }
    81  func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block   { panic("not supported") }
    82  func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header   { panic("not supported") }
    83  func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header {
    84  	if number == 0 {
    85  		return rawdb.ReadHeader(r.db, rawdb.ReadCanonicalHash(r.db, 0), 0)
    86  	}
    87  	return nil
    88  }
    89  
    90  // Tests that voting is evaluated correctly for various simple and complex scenarios.
    91  func TestVoting(t *testing.T) {
    92  	// Define the various voting scenarios to test
    93  	tests := []struct {
    94  		epoch   uint64
    95  		signers []string
    96  		votes   []testerVote
    97  		results []string
    98  	}{
    99  		{
   100  			// Single signer, no votes cast
   101  			signers: []string{"A"},
   102  			votes:   []testerVote{{signer: "A"}},
   103  			results: []string{"A"},
   104  		}, {
   105  			// Single signer, voting to add two others (only accept first, second needs 2 votes)
   106  			signers: []string{"A"},
   107  			votes: []testerVote{
   108  				{signer: "A", voted: "B", auth: true},
   109  				{signer: "B"},
   110  				{signer: "A", voted: "C", auth: true},
   111  			},
   112  			results: []string{"A", "B"},
   113  		}, {
   114  			// Two signers, voting to add three others (only accept first two, third needs 3 votes already)
   115  			signers: []string{"A", "B"},
   116  			votes: []testerVote{
   117  				{signer: "A", voted: "C", auth: true},
   118  				{signer: "B", voted: "C", auth: true},
   119  				{signer: "A", voted: "D", auth: true},
   120  				{signer: "B", voted: "D", auth: true},
   121  				{signer: "C"},
   122  				{signer: "A", voted: "E", auth: true},
   123  				{signer: "B", voted: "E", auth: true},
   124  			},
   125  			results: []string{"A", "B", "C", "D"},
   126  		}, {
   127  			// Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this)
   128  			signers: []string{"A"},
   129  			votes: []testerVote{
   130  				{signer: "A", voted: "A", auth: false},
   131  			},
   132  			results: []string{},
   133  		}, {
   134  			// Two signers, actually needing mutual consent to drop either of them (not fulfilled)
   135  			signers: []string{"A", "B"},
   136  			votes: []testerVote{
   137  				{signer: "A", voted: "B", auth: false},
   138  			},
   139  			results: []string{"A", "B"},
   140  		}, {
   141  			// Two signers, actually needing mutual consent to drop either of them (fulfilled)
   142  			signers: []string{"A", "B"},
   143  			votes: []testerVote{
   144  				{signer: "A", voted: "B", auth: false},
   145  				{signer: "B", voted: "B", auth: false},
   146  			},
   147  			results: []string{"A"},
   148  		}, {
   149  			// Three signers, two of them deciding to drop the third
   150  			signers: []string{"A", "B", "C"},
   151  			votes: []testerVote{
   152  				{signer: "A", voted: "C", auth: false},
   153  				{signer: "B", voted: "C", auth: false},
   154  			},
   155  			results: []string{"A", "B"},
   156  		}, {
   157  			// Four signers, consensus of two not being enough to drop anyone
   158  			signers: []string{"A", "B", "C", "D"},
   159  			votes: []testerVote{
   160  				{signer: "A", voted: "C", auth: false},
   161  				{signer: "B", voted: "C", auth: false},
   162  			},
   163  			results: []string{"A", "B", "C", "D"},
   164  		}, {
   165  			// Four signers, consensus of three already being enough to drop someone
   166  			signers: []string{"A", "B", "C", "D"},
   167  			votes: []testerVote{
   168  				{signer: "A", voted: "D", auth: false},
   169  				{signer: "B", voted: "D", auth: false},
   170  				{signer: "C", voted: "D", auth: false},
   171  			},
   172  			results: []string{"A", "B", "C"},
   173  		}, {
   174  			// Authorizations are counted once per signer per target
   175  			signers: []string{"A", "B"},
   176  			votes: []testerVote{
   177  				{signer: "A", voted: "C", auth: true},
   178  				{signer: "B"},
   179  				{signer: "A", voted: "C", auth: true},
   180  				{signer: "B"},
   181  				{signer: "A", voted: "C", auth: true},
   182  			},
   183  			results: []string{"A", "B"},
   184  		}, {
   185  			// Authorizing multiple accounts concurrently is permitted
   186  			signers: []string{"A", "B"},
   187  			votes: []testerVote{
   188  				{signer: "A", voted: "C", auth: true},
   189  				{signer: "B"},
   190  				{signer: "A", voted: "D", auth: true},
   191  				{signer: "B"},
   192  				{signer: "A"},
   193  				{signer: "B", voted: "D", auth: true},
   194  				{signer: "A"},
   195  				{signer: "B", voted: "C", auth: true},
   196  			},
   197  			results: []string{"A", "B", "C", "D"},
   198  		}, {
   199  			// Deauthorizations are counted once per signer per target
   200  			signers: []string{"A", "B"},
   201  			votes: []testerVote{
   202  				{signer: "A", voted: "B", auth: false},
   203  				{signer: "B"},
   204  				{signer: "A", voted: "B", auth: false},
   205  				{signer: "B"},
   206  				{signer: "A", voted: "B", auth: false},
   207  			},
   208  			results: []string{"A", "B"},
   209  		}, {
   210  			// Deauthorizing multiple accounts concurrently is permitted
   211  			signers: []string{"A", "B", "C", "D"},
   212  			votes: []testerVote{
   213  				{signer: "A", voted: "C", auth: false},
   214  				{signer: "B"},
   215  				{signer: "C"},
   216  				{signer: "A", voted: "D", auth: false},
   217  				{signer: "B"},
   218  				{signer: "C"},
   219  				{signer: "A"},
   220  				{signer: "B", voted: "D", auth: false},
   221  				{signer: "C", voted: "D", auth: false},
   222  				{signer: "A"},
   223  				{signer: "B", voted: "C", auth: false},
   224  			},
   225  			results: []string{"A", "B"},
   226  		}, {
   227  			// Votes from deauthorized signers are discarded immediately (deauth votes)
   228  			signers: []string{"A", "B", "C"},
   229  			votes: []testerVote{
   230  				{signer: "C", voted: "B", auth: false},
   231  				{signer: "A", voted: "C", auth: false},
   232  				{signer: "B", voted: "C", auth: false},
   233  				{signer: "A", voted: "B", auth: false},
   234  			},
   235  			results: []string{"A", "B"},
   236  		}, {
   237  			// Votes from deauthorized signers are discarded immediately (auth votes)
   238  			signers: []string{"A", "B", "C"},
   239  			votes: []testerVote{
   240  				{signer: "C", voted: "B", auth: false},
   241  				{signer: "A", voted: "C", auth: false},
   242  				{signer: "B", voted: "C", auth: false},
   243  				{signer: "A", voted: "B", auth: false},
   244  			},
   245  			results: []string{"A", "B"},
   246  		}, {
   247  			// Cascading changes are not allowed, only the account being voted on may change
   248  			signers: []string{"A", "B", "C", "D"},
   249  			votes: []testerVote{
   250  				{signer: "A", voted: "C", auth: false},
   251  				{signer: "B"},
   252  				{signer: "C"},
   253  				{signer: "A", voted: "D", auth: false},
   254  				{signer: "B", voted: "C", auth: false},
   255  				{signer: "C"},
   256  				{signer: "A"},
   257  				{signer: "B", voted: "D", auth: false},
   258  				{signer: "C", voted: "D", auth: false},
   259  			},
   260  			results: []string{"A", "B", "C"},
   261  		}, {
   262  			// Changes reaching consensus out of bounds (via a deauth) execute on touch
   263  			signers: []string{"A", "B", "C", "D"},
   264  			votes: []testerVote{
   265  				{signer: "A", voted: "C", auth: false},
   266  				{signer: "B"},
   267  				{signer: "C"},
   268  				{signer: "A", voted: "D", auth: false},
   269  				{signer: "B", voted: "C", auth: false},
   270  				{signer: "C"},
   271  				{signer: "A"},
   272  				{signer: "B", voted: "D", auth: false},
   273  				{signer: "C", voted: "D", auth: false},
   274  				{signer: "A"},
   275  				{signer: "C", voted: "C", auth: true},
   276  			},
   277  			results: []string{"A", "B"},
   278  		}, {
   279  			// Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch
   280  			signers: []string{"A", "B", "C", "D"},
   281  			votes: []testerVote{
   282  				{signer: "A", voted: "C", auth: false},
   283  				{signer: "B"},
   284  				{signer: "C"},
   285  				{signer: "A", voted: "D", auth: false},
   286  				{signer: "B", voted: "C", auth: false},
   287  				{signer: "C"},
   288  				{signer: "A"},
   289  				{signer: "B", voted: "D", auth: false},
   290  				{signer: "C", voted: "D", auth: false},
   291  				{signer: "A"},
   292  				{signer: "B", voted: "C", auth: true},
   293  			},
   294  			results: []string{"A", "B", "C"},
   295  		}, {
   296  			// Ensure that pending votes don't survive authorization status changes. This
   297  			// corner case can only appear if a signer is quickly added, removed and then
   298  			// readded (or the inverse), while one of the original voters dropped. If a
   299  			// past vote is left cached in the system somewhere, this will interfere with
   300  			// the final signer outcome.
   301  			signers: []string{"A", "B", "C", "D", "E"},
   302  			votes: []testerVote{
   303  				{signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed
   304  				{signer: "B", voted: "F", auth: true},
   305  				{signer: "C", voted: "F", auth: true},
   306  				{signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged")
   307  				{signer: "E", voted: "F", auth: false},
   308  				{signer: "B", voted: "F", auth: false},
   309  				{signer: "C", voted: "F", auth: false},
   310  				{signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed
   311  				{signer: "E", voted: "F", auth: true},
   312  				{signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed
   313  				{signer: "C", voted: "A", auth: false},
   314  				{signer: "D", voted: "A", auth: false},
   315  				{signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed
   316  			},
   317  			results: []string{"B", "C", "D", "E", "F"},
   318  		}, {
   319  			// Epoch transitions reset all votes to allow chain checkpointing
   320  			epoch:   3,
   321  			signers: []string{"A", "B"},
   322  			votes: []testerVote{
   323  				{signer: "A", voted: "C", auth: true},
   324  				{signer: "B"},
   325  				{signer: "A"}, // Checkpoint block, (don't vote here, it's validated outside of snapshots)
   326  				{signer: "B", voted: "C", auth: true},
   327  			},
   328  			results: []string{"A", "B"},
   329  		},
   330  	}
   331  	// Run through the scenarios and test them
   332  	for i, tt := range tests {
   333  		// Create the account pool and generate the initial set of signers
   334  		accounts := newTesterAccountPool()
   335  
   336  		signers := make([]common.Address, len(tt.signers))
   337  		for j, signer := range tt.signers {
   338  			signers[j] = accounts.address(signer)
   339  		}
   340  		for j := 0; j < len(signers); j++ {
   341  			for k := j + 1; k < len(signers); k++ {
   342  				if bytes.Compare(signers[j][:], signers[k][:]) > 0 {
   343  					signers[j], signers[k] = signers[k], signers[j]
   344  				}
   345  			}
   346  		}
   347  		// Create the genesis block with the initial set of signers
   348  		genesis := &core.Genesis{
   349  			ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal),
   350  		}
   351  		for j, signer := range signers {
   352  			copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:])
   353  		}
   354  		// Create a pristine blockchain with the genesis injected
   355  		db := ethdb.NewMemDatabase()
   356  		genesis.Commit(db)
   357  
   358  		// Assemble a chain of headers from the cast votes
   359  		headers := make([]*types.Header, len(tt.votes))
   360  		for j, vote := range tt.votes {
   361  			headers[j] = &types.Header{
   362  				Number:   big.NewInt(int64(j) + 1),
   363  				Time:     big.NewInt(int64(j) * 15),
   364  				Coinbase: accounts.address(vote.voted),
   365  				Extra:    make([]byte, extraVanity+extraSeal),
   366  			}
   367  			if j > 0 {
   368  				headers[j].ParentHash = headers[j-1].Hash()
   369  			}
   370  			if vote.auth {
   371  				copy(headers[j].Nonce[:], nonceAuthVote)
   372  			}
   373  			accounts.sign(headers[j], vote.signer)
   374  		}
   375  		// Pass all the headers through clique and ensure tallying succeeds
   376  		head := headers[len(headers)-1]
   377  
   378  		snap, err := New(&params.CliqueConfig{Epoch: tt.epoch}, db).snapshot(&testerChainReader{db: db}, head.Number.Uint64(), head.Hash(), headers)
   379  		if err != nil {
   380  			t.Errorf("test %d: failed to create voting snapshot: %v", i, err)
   381  			continue
   382  		}
   383  		// Verify the final list of signers against the expected ones
   384  		signers = make([]common.Address, len(tt.results))
   385  		for j, signer := range tt.results {
   386  			signers[j] = accounts.address(signer)
   387  		}
   388  		for j := 0; j < len(signers); j++ {
   389  			for k := j + 1; k < len(signers); k++ {
   390  				if bytes.Compare(signers[j][:], signers[k][:]) > 0 {
   391  					signers[j], signers[k] = signers[k], signers[j]
   392  				}
   393  			}
   394  		}
   395  		result := snap.signers()
   396  		if len(result) != len(signers) {
   397  			t.Errorf("test %d: signers mismatch: have %x, want %x", i, result, signers)
   398  			continue
   399  		}
   400  		for j := 0; j < len(result); j++ {
   401  			if !bytes.Equal(result[j][:], signers[j][:]) {
   402  				t.Errorf("test %d, signer %d: signer mismatch: have %x, want %x", i, j, result[j], signers[j])
   403  			}
   404  		}
   405  	}
   406  }