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