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