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