github.com/klaytn/klaytn@v1.12.1/consensus/clique/snapshot_test.go (about)

     1  // Copyright 2019 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn 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 klaytn 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 klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package clique
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"sort"
    23  	"testing"
    24  
    25  	"github.com/klaytn/klaytn/blockchain"
    26  	"github.com/klaytn/klaytn/blockchain/types"
    27  	"github.com/klaytn/klaytn/blockchain/vm"
    28  	"github.com/klaytn/klaytn/common"
    29  	"github.com/klaytn/klaytn/crypto"
    30  	"github.com/klaytn/klaytn/governance"
    31  	"github.com/klaytn/klaytn/params"
    32  	"github.com/klaytn/klaytn/rlp"
    33  	"github.com/klaytn/klaytn/storage/database"
    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 Klaytn 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 Klaytn 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 Klaytn 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(sigHash(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 parcitular 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  // Tests that Clique signer voting is evaluated correctly for various simple and
   100  // complex scenarios, as well as that a few special corner cases fail correctly.
   101  func TestClique(t *testing.T) {
   102  	// Define the various voting scenarios to test
   103  	tests := []struct {
   104  		epoch   uint64
   105  		signers []string
   106  		votes   []testerVote
   107  		results []string
   108  		failure error
   109  	}{
   110  		{
   111  			// Single signer, no votes cast
   112  			signers: []string{"A"},
   113  			votes:   []testerVote{{signer: "A"}},
   114  			results: []string{"A"},
   115  		}, {
   116  			// Single signer, voting to add two others (only accept first, second needs 2 votes)
   117  			signers: []string{"A"},
   118  			votes: []testerVote{
   119  				{signer: "A", voted: "B", auth: true},
   120  				{signer: "B"},
   121  				{signer: "A", voted: "C", auth: true},
   122  			},
   123  			results: []string{"A", "B"},
   124  		}, {
   125  			// Two signers, voting to add three others (only accept first two, third needs 3 votes already)
   126  			signers: []string{"A", "B"},
   127  			votes: []testerVote{
   128  				{signer: "A", voted: "C", auth: true},
   129  				{signer: "B", voted: "C", auth: true},
   130  				{signer: "A", voted: "D", auth: true},
   131  				{signer: "B", voted: "D", auth: true},
   132  				{signer: "C"},
   133  				{signer: "A", voted: "E", auth: true},
   134  				{signer: "B", voted: "E", auth: true},
   135  			},
   136  			results: []string{"A", "B", "C", "D"},
   137  		}, {
   138  			// Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this)
   139  			signers: []string{"A"},
   140  			votes: []testerVote{
   141  				{signer: "A", voted: "A", auth: false},
   142  			},
   143  			results: []string{},
   144  		}, {
   145  			// Two signers, actually needing mutual consent to drop either of them (not fulfilled)
   146  			signers: []string{"A", "B"},
   147  			votes: []testerVote{
   148  				{signer: "A", voted: "B", auth: false},
   149  			},
   150  			results: []string{"A", "B"},
   151  		}, {
   152  			// Two signers, actually needing mutual consent to drop either of them (fulfilled)
   153  			signers: []string{"A", "B"},
   154  			votes: []testerVote{
   155  				{signer: "A", voted: "B", auth: false},
   156  				{signer: "B", voted: "B", auth: false},
   157  			},
   158  			results: []string{"A"},
   159  		}, {
   160  			// Three signers, two of them deciding to drop the third
   161  			signers: []string{"A", "B", "C"},
   162  			votes: []testerVote{
   163  				{signer: "A", voted: "C", auth: false},
   164  				{signer: "B", voted: "C", auth: false},
   165  			},
   166  			results: []string{"A", "B"},
   167  		}, {
   168  			// Four signers, consensus of two not being enough to drop anyone
   169  			signers: []string{"A", "B", "C", "D"},
   170  			votes: []testerVote{
   171  				{signer: "A", voted: "C", auth: false},
   172  				{signer: "B", voted: "C", auth: false},
   173  			},
   174  			results: []string{"A", "B", "C", "D"},
   175  		}, {
   176  			// Four signers, consensus of three already being enough to drop someone
   177  			signers: []string{"A", "B", "C", "D"},
   178  			votes: []testerVote{
   179  				{signer: "A", voted: "D", auth: false},
   180  				{signer: "B", voted: "D", auth: false},
   181  				{signer: "C", voted: "D", auth: false},
   182  			},
   183  			results: []string{"A", "B", "C"},
   184  		}, {
   185  			// Authorizations are counted once per signer per target
   186  			signers: []string{"A", "B"},
   187  			votes: []testerVote{
   188  				{signer: "A", voted: "C", auth: true},
   189  				{signer: "B"},
   190  				{signer: "A", voted: "C", auth: true},
   191  				{signer: "B"},
   192  				{signer: "A", voted: "C", auth: true},
   193  			},
   194  			results: []string{"A", "B"},
   195  		}, {
   196  			// Authorizing multiple accounts concurrently is permitted
   197  			signers: []string{"A", "B"},
   198  			votes: []testerVote{
   199  				{signer: "A", voted: "C", auth: true},
   200  				{signer: "B"},
   201  				{signer: "A", voted: "D", auth: true},
   202  				{signer: "B"},
   203  				{signer: "A"},
   204  				{signer: "B", voted: "D", auth: true},
   205  				{signer: "A"},
   206  				{signer: "B", voted: "C", auth: true},
   207  			},
   208  			results: []string{"A", "B", "C", "D"},
   209  		}, {
   210  			// Deauthorizations are counted once per signer per target
   211  			signers: []string{"A", "B"},
   212  			votes: []testerVote{
   213  				{signer: "A", voted: "B", auth: false},
   214  				{signer: "B"},
   215  				{signer: "A", voted: "B", auth: false},
   216  				{signer: "B"},
   217  				{signer: "A", voted: "B", auth: false},
   218  			},
   219  			results: []string{"A", "B"},
   220  		}, {
   221  			// Deauthorizing multiple accounts concurrently is permitted
   222  			signers: []string{"A", "B", "C", "D"},
   223  			votes: []testerVote{
   224  				{signer: "A", voted: "C", auth: false},
   225  				{signer: "B"},
   226  				{signer: "C"},
   227  				{signer: "A", voted: "D", auth: false},
   228  				{signer: "B"},
   229  				{signer: "C"},
   230  				{signer: "A"},
   231  				{signer: "B", voted: "D", auth: false},
   232  				{signer: "C", voted: "D", auth: false},
   233  				{signer: "A"},
   234  				{signer: "B", voted: "C", auth: false},
   235  			},
   236  			results: []string{"A", "B"},
   237  		}, {
   238  			// Votes from deauthorized signers are discarded immediately (deauth votes)
   239  			signers: []string{"A", "B", "C"},
   240  			votes: []testerVote{
   241  				{signer: "C", voted: "B", auth: false},
   242  				{signer: "A", voted: "C", auth: false},
   243  				{signer: "B", voted: "C", auth: false},
   244  				{signer: "A", voted: "B", auth: false},
   245  			},
   246  			results: []string{"A", "B"},
   247  		}, {
   248  			// Votes from deauthorized signers are discarded immediately (auth votes)
   249  			signers: []string{"A", "B", "C"},
   250  			votes: []testerVote{
   251  				{signer: "C", voted: "B", auth: false},
   252  				{signer: "A", voted: "C", auth: false},
   253  				{signer: "B", voted: "C", auth: false},
   254  				{signer: "A", voted: "B", auth: false},
   255  			},
   256  			results: []string{"A", "B"},
   257  		}, {
   258  			// Cascading changes are not allowed, only the account being voted on may change
   259  			signers: []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", voted: "C", auth: false},
   266  				{signer: "C"},
   267  				{signer: "A"},
   268  				{signer: "B", voted: "D", auth: false},
   269  				{signer: "C", voted: "D", auth: false},
   270  			},
   271  			results: []string{"A", "B", "C"},
   272  		}, {
   273  			// Changes reaching consensus out of bounds (via a deauth) execute on touch
   274  			signers: []string{"A", "B", "C", "D"},
   275  			votes: []testerVote{
   276  				{signer: "A", voted: "C", auth: false},
   277  				{signer: "B"},
   278  				{signer: "C"},
   279  				{signer: "A", voted: "D", auth: false},
   280  				{signer: "B", voted: "C", auth: false},
   281  				{signer: "C"},
   282  				{signer: "A"},
   283  				{signer: "B", voted: "D", auth: false},
   284  				{signer: "C", voted: "D", auth: false},
   285  				{signer: "A"},
   286  				{signer: "C", voted: "C", auth: true},
   287  			},
   288  			results: []string{"A", "B"},
   289  		}, {
   290  			// Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch
   291  			signers: []string{"A", "B", "C", "D"},
   292  			votes: []testerVote{
   293  				{signer: "A", voted: "C", auth: false},
   294  				{signer: "B"},
   295  				{signer: "C"},
   296  				{signer: "A", voted: "D", auth: false},
   297  				{signer: "B", voted: "C", auth: false},
   298  				{signer: "C"},
   299  				{signer: "A"},
   300  				{signer: "B", voted: "D", auth: false},
   301  				{signer: "C", voted: "D", auth: false},
   302  				{signer: "A"},
   303  				{signer: "B", voted: "C", auth: true},
   304  			},
   305  			results: []string{"A", "B", "C"},
   306  		}, {
   307  			// Ensure that pending votes don't survive authorization status changes. This
   308  			// corner case can only appear if a signer is quickly added, removed and then
   309  			// readded (or the inverse), while one of the original voters dropped. If a
   310  			// past vote is left cached in the system somewhere, this will interfere with
   311  			// the final signer outcome.
   312  			signers: []string{"A", "B", "C", "D", "E"},
   313  			votes: []testerVote{
   314  				{signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed
   315  				{signer: "B", voted: "F", auth: true},
   316  				{signer: "C", voted: "F", auth: true},
   317  				{signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged")
   318  				{signer: "E", voted: "F", auth: false},
   319  				{signer: "B", voted: "F", auth: false},
   320  				{signer: "C", voted: "F", auth: false},
   321  				{signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed
   322  				{signer: "E", voted: "F", auth: true},
   323  				{signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed
   324  				{signer: "C", voted: "A", auth: false},
   325  				{signer: "D", voted: "A", auth: false},
   326  				{signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed
   327  			},
   328  			results: []string{"B", "C", "D", "E", "F"},
   329  		}, {
   330  			// Epoch transitions reset all votes to allow chain checkpointing
   331  			epoch:   3,
   332  			signers: []string{"A", "B"},
   333  			votes: []testerVote{
   334  				{signer: "A", voted: "C", auth: true},
   335  				{signer: "B"},
   336  				{signer: "A", checkpoint: []string{"A", "B"}},
   337  				{signer: "B", voted: "C", auth: true},
   338  			},
   339  			results: []string{"A", "B"},
   340  		}, {
   341  			// An unauthorized signer should not be able to sign blocks
   342  			signers: []string{"A"},
   343  			votes: []testerVote{
   344  				{signer: "B"},
   345  			},
   346  			failure: errUnauthorizedSigner,
   347  		}, {
   348  			// An authorized signer that signed recenty should not be able to sign again
   349  			signers: []string{"A", "B"},
   350  			votes: []testerVote{
   351  				{signer: "A"},
   352  				{signer: "A"},
   353  			},
   354  			failure: errRecentlySigned,
   355  		}, {
   356  			// Recent signatures should not reset on checkpoint blocks imported in a batch
   357  			epoch:   3,
   358  			signers: []string{"A", "B", "C"},
   359  			votes: []testerVote{
   360  				{signer: "A"},
   361  				{signer: "B"},
   362  				{signer: "A", checkpoint: []string{"A", "B", "C"}},
   363  				{signer: "A"},
   364  			},
   365  			failure: errRecentlySigned,
   366  		}, {
   367  			// Recent signatures should not reset on checkpoint blocks imported in a new
   368  			// batch (https://github.com/ethereum/go-ethereum/issues/17593). Whilst this
   369  			// seems overly specific and weird, it was a Rinkeby consensus split.
   370  			epoch:   3,
   371  			signers: []string{"A", "B", "C"},
   372  			votes: []testerVote{
   373  				{signer: "A"},
   374  				{signer: "B"},
   375  				{signer: "A", checkpoint: []string{"A", "B", "C"}},
   376  				{signer: "A", newbatch: true},
   377  			},
   378  			failure: errRecentlySigned,
   379  		},
   380  	}
   381  	// Run through the scenarios and test them
   382  	for i, tt := range tests {
   383  		// Create the account pool and generate the initial set of signers
   384  		accounts := newTesterAccountPool()
   385  
   386  		signers := make([]common.Address, len(tt.signers))
   387  		for j, signer := range tt.signers {
   388  			signers[j] = accounts.address(signer)
   389  		}
   390  		for j := 0; j < len(signers); j++ {
   391  			for k := j + 1; k < len(signers); k++ {
   392  				if bytes.Compare(signers[j][:], signers[k][:]) > 0 {
   393  					signers[j], signers[k] = signers[k], signers[j]
   394  				}
   395  			}
   396  		}
   397  
   398  		// Create the genesis block with the initial set of signers
   399  		genesis := &blockchain.Genesis{
   400  			ExtraData: make([]byte, ExtraVanity+common.AddressLength*len(signers)+ExtraSeal),
   401  		}
   402  		for j, signer := range signers {
   403  			copy(genesis.ExtraData[ExtraVanity+j*common.AddressLength:], signer[:])
   404  		}
   405  		// Assemble a chain of headers from the cast votes
   406  		config := params.TestChainConfig.Copy()
   407  		config.Clique = &params.CliqueConfig{
   408  			Period: 1,
   409  			Epoch:  tt.epoch,
   410  		}
   411  		blockchain.InitDeriveSha(config)
   412  
   413  		// Create a pristine blockchain with the genesis injected
   414  		db := database.NewMemoryDBManager()
   415  		genesis.Commit(common.Hash{}, db)
   416  
   417  		engine := New(config.Clique, db)
   418  		engine.fakeBlockScore = true
   419  
   420  		blocks, _ := blockchain.GenerateChain(config, genesis.ToBlock(common.Hash{}, db), engine, db, len(tt.votes), func(j int, gen *blockchain.BlockGen) {
   421  			vote := new(governance.GovernanceVote)
   422  			if tt.votes[j].auth {
   423  				vote.Key = "addvalidator"
   424  			} else {
   425  				vote.Key = "removevalidator"
   426  			}
   427  
   428  			if len(tt.votes[j].voted) > 0 {
   429  				vote.Value = accounts.address(tt.votes[j].voted)
   430  				encoded, _ := rlp.EncodeToBytes(vote)
   431  				gen.SetVoteData(encoded)
   432  			}
   433  		})
   434  		// Iterate through the blocks and seal them individually
   435  		for j, block := range blocks {
   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.BlockScore = scoreInTurn // 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 := blockchain.NewBlockChain(db, nil, config, engine, vm.Config{})
   461  		if err != nil {
   462  			t.Errorf("test %d: failed to create test chain: %v", i, err)
   463  			continue
   464  		}
   465  		failed := false
   466  		for j := 0; j < len(batches)-1; j++ {
   467  			if k, err := chain.InsertChain(batches[j]); err != nil {
   468  				t.Errorf("test %d: failed to import batch %d, block %d: %v", i, j, k, err)
   469  				failed = true
   470  				break
   471  			}
   472  		}
   473  		if failed {
   474  			continue
   475  		}
   476  		if _, err = chain.InsertChain(batches[len(batches)-1]); err != tt.failure {
   477  			t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
   478  		}
   479  		if tt.failure != nil {
   480  			continue
   481  		}
   482  		// No failure was produced or requested, generate the final voting snapshot
   483  		head := blocks[len(blocks)-1]
   484  
   485  		snap, err := engine.snapshot(chain, head.NumberU64(), head.Hash(), nil)
   486  		if err != nil {
   487  			t.Errorf("test %d: failed to retrieve voting snapshot: %v", i, err)
   488  			continue
   489  		}
   490  		// Verify the final list of signers against the expected ones
   491  		signers = make([]common.Address, len(tt.results))
   492  		for j, signer := range tt.results {
   493  			signers[j] = accounts.address(signer)
   494  		}
   495  		for j := 0; j < len(signers); j++ {
   496  			for k := j + 1; k < len(signers); k++ {
   497  				if bytes.Compare(signers[j][:], signers[k][:]) > 0 {
   498  					signers[j], signers[k] = signers[k], signers[j]
   499  				}
   500  			}
   501  		}
   502  		result := snap.signers()
   503  		if len(result) != len(signers) {
   504  			t.Errorf("test %d: signers mismatch: have %x, want %x", i, result, signers)
   505  			continue
   506  		}
   507  		for j := 0; j < len(result); j++ {
   508  			if !bytes.Equal(result[j][:], signers[j][:]) {
   509  				t.Errorf("test %d, signer %d: signer mismatch: have %x, want %x", i, j, result[j], signers[j])
   510  			}
   511  		}
   512  	}
   513  }