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