github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/consensus/clique/snapshot_test.go (about)

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