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 = ¶ms.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 }