github.com/beyonderyue/gochain@v2.2.26+incompatible/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 "context" 22 "crypto/ecdsa" 23 "math/big" 24 "testing" 25 26 "github.com/gochain-io/gochain/common" 27 "github.com/gochain-io/gochain/core" 28 "github.com/gochain-io/gochain/core/rawdb" 29 "github.com/gochain-io/gochain/core/types" 30 "github.com/gochain-io/gochain/crypto" 31 "github.com/gochain-io/gochain/ethdb" 32 "github.com/gochain-io/gochain/params" 33 ) 34 35 type testerVote struct { 36 signer string 37 voted string 38 auth bool 39 voterElection bool 40 } 41 42 // testerAccountPool is a pool to maintain currently active tester accounts, 43 // mapped from textual names used in the tests below to actual Ethereum private 44 // keys capable of signing transactions. 45 type testerAccountPool struct { 46 accounts map[string]*ecdsa.PrivateKey 47 } 48 49 func newTesterAccountPool() *testerAccountPool { 50 return &testerAccountPool{ 51 accounts: make(map[string]*ecdsa.PrivateKey), 52 } 53 } 54 55 func (ap *testerAccountPool) sign(header *types.Header, signer string) { 56 // Ensure we have a persistent key for the signer 57 if ap.accounts[signer] == nil { 58 ap.accounts[signer], _ = crypto.GenerateKey() 59 } 60 // Sign the header and embed the signature in extra data 61 sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer]) 62 header.Signer = sig 63 } 64 65 func (ap *testerAccountPool) address(account string) common.Address { 66 // Ensure we have a persistent key for the account 67 if ap.accounts[account] == nil { 68 ap.accounts[account], _ = crypto.GenerateKey() 69 } 70 // Resolve and return the Ethereum address 71 return crypto.PubkeyToAddress(ap.accounts[account].PublicKey) 72 } 73 74 // testerChainReader implements consensus.ChainReader to access the genesis 75 // block. All other methods and requests will panic. 76 type testerChainReader struct { 77 db common.Database 78 } 79 80 func (r *testerChainReader) Config() *params.ChainConfig { return params.AllCliqueProtocolChanges } 81 func (r *testerChainReader) CurrentHeader() *types.Header { panic("not supported") } 82 func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") } 83 func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic("not supported") } 84 func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") } 85 func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header { 86 if number == 0 { 87 return rawdb.ReadHeader(r.db.HeaderTable(), rawdb.ReadCanonicalHash(r.db, 0), 0) 88 } 89 panic("not supported") 90 } 91 92 // Tests that voting is evaluated correctly for various simple and complex scenarios. 93 func TestVoting(t *testing.T) { 94 // Define the various voting scenarios to test 95 tests := []votingTest{ 96 { 97 // 0: Single signer, no votes cast 98 name: "1-no-votes", 99 signers: []string{"A"}, 100 voters: []string{"A"}, 101 votes: []testerVote{{signer: "A"}}, 102 signersResults: []string{"A"}, 103 votersResults: []string{"A"}, 104 }, 105 { 106 // 1: Single signer, voting to add two others (only accept first) 107 name: "1-vote-2", 108 signers: []string{"A"}, 109 voters: []string{"A"}, 110 votes: []testerVote{ 111 {signer: "A", voted: "B", auth: true}, 112 {signer: "B"}, 113 {signer: "A", voted: "C", auth: false}, 114 {signer: "B", voted: "C", auth: true}, 115 }, 116 signersResults: []string{"A", "B"}, 117 votersResults: []string{"A"}, 118 }, 119 { 120 // 2: Two signers, voting to add three others (only accept first two) 121 name: "1-vote-3", 122 signers: []string{"A", "B"}, 123 voters: []string{"A", "B"}, 124 votes: []testerVote{ 125 {signer: "A", voted: "C", auth: true}, 126 {signer: "B", voted: "C", auth: true}, 127 {signer: "A", voted: "D", auth: true}, 128 {signer: "B", voted: "D", auth: true}, 129 {signer: "C"}, 130 {signer: "A", voted: "E", auth: false}, 131 {signer: "B", voted: "E", auth: false}, 132 }, 133 signersResults: []string{"A", "B", "C", "D"}, 134 votersResults: []string{"A", "B"}, 135 }, 136 { 137 // 3: Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this) 138 name: "1-drop", 139 signers: []string{"A"}, 140 voters: []string{"A"}, 141 votes: []testerVote{ 142 {signer: "A", voted: "A", auth: false}, 143 }, 144 signersResults: []string{"A"}, 145 votersResults: []string{}, 146 }, 147 { 148 // 4: Two signers, actually needing mutual consent to drop either of them (not fulfilled) 149 name: "2-drop-fail", 150 signers: []string{"A", "B"}, 151 voters: []string{"A", "B"}, 152 votes: []testerVote{ 153 {signer: "A", voted: "B", auth: false}, 154 }, 155 signersResults: []string{"A", "B"}, 156 votersResults: []string{"A", "B"}, 157 }, 158 { 159 // 5: Two signers, actually needing mutual consent to drop either of them (fulfilled) 160 name: "2-drop", 161 signers: []string{"A", "B"}, 162 voters: []string{"A", "B"}, 163 votes: []testerVote{ 164 {signer: "A", voted: "B", auth: false}, 165 {signer: "B", voted: "B", auth: false}, 166 }, 167 signersResults: []string{"A", "B"}, 168 votersResults: []string{"A"}, 169 }, 170 { 171 // 6: Three signers, two of them deciding to drop the third 172 name: "3-drop", 173 signers: []string{"A", "B", "C"}, 174 voters: []string{"A", "B", "C"}, 175 votes: []testerVote{ 176 {signer: "A", voted: "C", auth: false}, 177 {signer: "B", voted: "C", auth: false}, 178 }, 179 signersResults: []string{"A", "B", "C"}, 180 votersResults: []string{"A", "B"}, 181 }, 182 { 183 // 7: Four signers, consensus of two not being enough to drop anyone 184 name: "4-drop-fail", 185 signers: []string{"A", "B", "C", "D"}, 186 voters: []string{"A", "B", "C", "D"}, 187 votes: []testerVote{ 188 {signer: "A", voted: "C", auth: false}, 189 {signer: "B", voted: "C", auth: false}, 190 }, 191 signersResults: []string{"A", "B", "C", "D"}, 192 votersResults: []string{"A", "B", "C", "D"}, 193 }, 194 { 195 // 8: Four signers, consensus of three already being enough to drop someone 196 name: "4-drop", 197 signers: []string{"A", "B", "C", "D"}, 198 voters: []string{"A", "B", "C", "D"}, 199 votes: []testerVote{ 200 {signer: "A", voted: "D", auth: false}, 201 {signer: "B", voted: "D", auth: false}, 202 {signer: "C", voted: "D", auth: false}, 203 }, 204 signersResults: []string{"A", "B", "C", "D"}, 205 votersResults: []string{"A", "B", "C"}, 206 }, 207 { 208 // 9: Authorizations are counted once per signer per target 209 name: "auth-count", 210 signers: []string{"A", "B"}, 211 voters: []string{"A", "B"}, 212 votes: []testerVote{ 213 {signer: "A", voted: "C", auth: true}, 214 {signer: "B"}, 215 {signer: "A", voted: "C", auth: true}, 216 {signer: "B"}, 217 {signer: "A", voted: "C", auth: true}, 218 }, 219 signersResults: []string{"A", "B"}, 220 votersResults: []string{"A", "B"}, 221 }, 222 { 223 // 10: Authorizing multiple accounts concurrently is permitted 224 name: "auth-mult", 225 signers: []string{"A", "B"}, 226 voters: []string{"A", "B"}, 227 votes: []testerVote{ 228 {signer: "A", voted: "C", auth: true}, 229 {signer: "B"}, 230 {signer: "A", voted: "D", auth: true}, 231 {signer: "B"}, 232 {signer: "A"}, 233 {signer: "B", voted: "D", auth: true}, 234 {signer: "A"}, 235 {signer: "B", voted: "C", auth: true}, 236 }, 237 signersResults: []string{"A", "B", "C", "D"}, 238 votersResults: []string{"A", "B"}, 239 }, 240 { 241 // 11: Deauthorizations are counted once per signer per target 242 name: "deauth-count", 243 signers: []string{"A", "B"}, 244 voters: []string{"A", "B"}, 245 votes: []testerVote{ 246 {signer: "A", voted: "B", auth: false}, 247 {signer: "B"}, 248 {signer: "A", voted: "B", auth: false}, 249 {signer: "B"}, 250 {signer: "A", voted: "B", auth: false}, 251 }, 252 signersResults: []string{"A", "B"}, 253 votersResults: []string{"A", "B"}, 254 }, 255 { 256 // 12: Deauthorizing multiple accounts concurrently is permitted 257 name: "deauth-mult", 258 signers: []string{"A", "B", "C", "D"}, 259 voters: []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"}, 266 {signer: "C"}, 267 {signer: "A"}, 268 {signer: "B", voted: "D", auth: false}, 269 {signer: "C", voted: "D", auth: false}, 270 {signer: "A"}, 271 {signer: "B", voted: "C", auth: false}, 272 }, 273 signersResults: []string{"A", "B", "C", "D"}, 274 votersResults: []string{"A", "B"}, 275 }, 276 { 277 // 13: Votes from deauthorized signers are discarded immediately (deauth votes) 278 name: "deauth-discard-deauth", 279 signers: []string{"A", "B", "C"}, 280 voters: []string{"A", "B", "C"}, 281 votes: []testerVote{ 282 {signer: "C", voted: "B", auth: false}, 283 {signer: "A", voted: "C", auth: false}, 284 {signer: "B", voted: "C", auth: false}, 285 {signer: "A", voted: "B", auth: false}, 286 }, 287 signersResults: []string{"A", "B", "C"}, 288 votersResults: []string{"A", "B"}, 289 }, 290 { 291 // 14: Votes from deauthorized signers are discarded immediately (auth votes) 292 name: "deauth-discard-auth", 293 signers: []string{"A", "B", "C"}, 294 voters: []string{"A", "B", "C"}, 295 votes: []testerVote{ 296 {signer: "C", voted: "D", auth: true}, 297 {signer: "A", voted: "C", auth: false}, 298 {signer: "B", voted: "C", auth: false}, 299 {signer: "A", voted: "D", auth: true}, 300 }, 301 signersResults: []string{"A", "B", "C"}, 302 votersResults: []string{"A", "B"}, 303 }, 304 { 305 // 15: Cascading changes are not allowed, only the account being voted on may change 306 name: "no-cascade", 307 signers: []string{"A", "B", "C", "D"}, 308 voters: []string{"A", "B", "C", "D"}, 309 votes: []testerVote{ 310 {signer: "A", voted: "C", auth: false}, 311 {signer: "B"}, 312 {signer: "C"}, 313 {signer: "A", voted: "D", auth: false}, 314 {signer: "B", voted: "C", auth: false}, 315 {signer: "C"}, 316 {signer: "A"}, 317 {signer: "B", voted: "D", auth: false}, 318 {signer: "C", voted: "D", auth: false}, 319 }, 320 signersResults: []string{"A", "B", "C", "D"}, 321 votersResults: []string{"A", "B", "C"}, 322 }, 323 { 324 // 16: Changes reaching consensus out of bounds (via a deauth) execute on touch 325 name: "out-of-bounds", 326 signers: []string{"A", "B", "C", "D"}, 327 voters: []string{"A", "B", "C", "D"}, 328 votes: []testerVote{ 329 {signer: "A", voted: "C", auth: false}, 330 {signer: "B"}, 331 {signer: "C"}, 332 {signer: "A", voted: "D", auth: false}, 333 {signer: "B", voted: "C", auth: false}, 334 {signer: "C"}, 335 {signer: "A"}, 336 {signer: "B", voted: "D", auth: false}, 337 {signer: "C", voted: "D", auth: false}, 338 {signer: "A"}, 339 {signer: "B"}, 340 {signer: "C", voted: "C", auth: true}, 341 }, 342 signersResults: []string{"A", "B", "C", "D"}, 343 votersResults: []string{"A", "B"}, 344 }, 345 { 346 // 17: Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch 347 name: "out-of-bounds-out", 348 signers: []string{"A", "B", "C", "D"}, 349 voters: []string{"A", "B", "C", "D"}, 350 votes: []testerVote{ 351 {signer: "A", voted: "C", auth: false, voterElection: true}, 352 {signer: "B"}, 353 {signer: "C"}, 354 {signer: "A", voted: "D", auth: false, voterElection: true}, 355 {signer: "B", voted: "C", auth: false, voterElection: true}, 356 {signer: "C"}, 357 {signer: "A"}, 358 {signer: "B", voted: "D", auth: false, voterElection: true}, 359 {signer: "C", voted: "D", auth: false, voterElection: true}, 360 {signer: "A"}, 361 {signer: "B", voted: "C", auth: true, voterElection: true}, 362 }, 363 signersResults: []string{"A", "B", "C", "D"}, 364 votersResults: []string{"A", "B", "C"}, 365 }, 366 { 367 // 18: Ensure that pending votes don't survive authorization status changes. This 368 // corner case can only appear if a signer is quickly added, removed and then 369 // readded (or the inverse), while one of the original voters dropped. If a 370 // past vote is left cached in the system somewhere, this will interfere with 371 // the final signer outcome. 372 name: "discard-pending", 373 signers: []string{"A", "B", "C", "D", "E", "F"}, 374 voters: []string{"A", "B", "C", "D", "E"}, 375 votes: []testerVote{ 376 {signer: "A", voted: "F", auth: true, voterElection: true}, // Authorize F, 3 votes needed 377 {signer: "B", voted: "F", auth: true, voterElection: true}, 378 {signer: "C", voted: "F", auth: true, voterElection: true}, 379 {signer: "D", voted: "F", auth: false, voterElection: true}, // Deauthorize F, 3 votes needed (leave A's previous vote "unchanged") 380 {signer: "E", voted: "F", auth: false, voterElection: true}, 381 {signer: "B", voted: "F", auth: false, voterElection: true}, 382 {signer: "C", voted: "F", auth: false, voterElection: true}, 383 {signer: "D", voted: "F", auth: true, voterElection: true}, // Almost authorize F as a voter, 2/3 votes needed 384 {signer: "E", voted: "F", auth: true, voterElection: true}, 385 {signer: "B", voted: "A", auth: false, voterElection: true}, // Deauthorize A as a voter, 3 votes needed 386 {signer: "C", voted: "A", auth: false, voterElection: true}, 387 {signer: "D", voted: "A", auth: false, voterElection: true}, 388 {signer: "E"}, 389 {signer: "B", voted: "F", auth: true, voterElection: true}, // Finish authorizing F as a voter, 3/3 votes needed 390 }, 391 //results: []string{"B", "C", "D", "E", "F"}, 392 signersResults: []string{"A", "B", "C", "D", "E", "F"}, 393 votersResults: []string{"B", "C", "D", "E", "F"}, 394 }, 395 { 396 // 19: Epoch transitions reset all votes to allow chain checkpointing 397 name: "epoch-reset", 398 epoch: 3, 399 signers: []string{"A", "B"}, 400 voters: []string{"A", "B"}, 401 votes: []testerVote{ 402 {signer: "A", voted: "C", auth: true}, 403 {signer: "B"}, 404 {signer: "A"}, // Checkpoint block, (don't vote here, it's validated outside of snapshots) 405 {signer: "B", voted: "C", auth: true}, 406 }, 407 signersResults: []string{"A", "B"}, 408 votersResults: []string{"A", "B"}, 409 }, 410 } 411 // Run through the scenarios and test them 412 for _, tt := range tests { 413 t.Run(tt.name, tt.run) 414 } 415 } 416 417 type votingTest struct { 418 name string 419 epoch uint64 420 signers []string 421 voters []string 422 votes []testerVote 423 signersResults []string 424 votersResults []string 425 } 426 427 func (tt *votingTest) run(t *testing.T) { 428 // Create the account pool and generate the initial set of signers 429 accounts := newTesterAccountPool() 430 431 signers := make([]common.Address, len(tt.signers)) 432 voters := make([]common.Address, len(tt.voters)) 433 for j, signer := range tt.signers { 434 signers[j] = accounts.address(signer) 435 } 436 for j, voter := range tt.voters { 437 voters[j] = accounts.address(voter) 438 } 439 for j := 0; j < len(signers); j++ { 440 for k := j + 1; k < len(signers); k++ { 441 if bytes.Compare(signers[j][:], signers[k][:]) > 0 { 442 signers[j], signers[k] = signers[k], signers[j] 443 } 444 } 445 } 446 for j := 0; j < len(voters); j++ { 447 for k := j + 1; k < len(voters); k++ { 448 if bytes.Compare(voters[j][:], voters[k][:]) > 0 { 449 voters[j], voters[k] = voters[k], voters[j] 450 } 451 } 452 } 453 // Create the genesis block with the initial set of signers 454 genesis := &core.Genesis{ 455 ExtraData: make([]byte, extraVanity), 456 Signers: signers, 457 Voters: voters, 458 Signer: make([]byte, signatureLength), 459 } 460 // Create a pristine blockchain with the genesis injected 461 db := ethdb.NewMemDatabase() 462 genesis.Commit(db) 463 464 // Assemble a chain of headers from the cast votes 465 headers := make([]*types.Header, len(tt.votes)) 466 for j, vote := range tt.votes { 467 headers[j] = &types.Header{ 468 Number: big.NewInt(int64(j) + 1), 469 Time: big.NewInt(int64(j) * int64(params.DefaultCliquePeriod)), 470 Signer: make([]byte, signatureLength), 471 Extra: make([]byte, extraVanity), 472 } 473 if j > 0 { 474 headers[j].ParentHash = headers[j-1].Hash() 475 } 476 if vote.auth { 477 copy(headers[j].Nonce[:], nonceAuthVote) 478 } 479 headers[j].Extra = ExtraAppendVote(headers[j].Extra, accounts.address(vote.voted), vote.voterElection) 480 accounts.sign(headers[j], vote.signer) 481 } 482 // Pass all the headers through clique and ensure tallying succeeds 483 head := headers[len(headers)-1] 484 485 snap, err := New(¶ms.CliqueConfig{Epoch: tt.epoch}, db). 486 snapshot(context.Background(), &testerChainReader{db: db}, head.Number.Uint64(), head.Hash(), headers) 487 if err != nil { 488 t.Errorf("failed to create voting snapshot: %v", err) 489 return 490 } 491 // Verify the final list of signers against the expected ones 492 signers = make([]common.Address, len(tt.signersResults)) 493 for j, signer := range tt.signersResults { 494 signers[j] = accounts.address(signer) 495 } 496 for j := 0; j < len(signers); j++ { 497 for k := j + 1; k < len(signers); k++ { 498 if bytes.Compare(signers[j][:], signers[k][:]) > 0 { 499 signers[j], signers[k] = signers[k], signers[j] 500 } 501 } 502 } 503 signersResult := snap.signers() 504 if len(signersResult) != len(signers) { 505 t.Errorf("signers mismatch: have %x, want %x", signersResult, signers) 506 return 507 } 508 for j := 0; j < len(signersResult); j++ { 509 if !bytes.Equal(signersResult[j][:], signers[j][:]) { 510 t.Errorf("signer %d: signer mismatch: have %x, want %x", j, signersResult[j], signers[j]) 511 } 512 } 513 // Verify the final list of voters against the expected ones 514 voters = make([]common.Address, len(tt.votersResults)) 515 for j, voter := range tt.votersResults { 516 voters[j] = accounts.address(voter) 517 } 518 for j := 0; j < len(voters); j++ { 519 for k := j + 1; k < len(voters); k++ { 520 if bytes.Compare(voters[j][:], voters[k][:]) > 0 { 521 voters[j], voters[k] = voters[k], voters[j] 522 } 523 } 524 } 525 votersResult := snap.voters() 526 if len(votersResult) != len(voters) { 527 t.Errorf("voters mismatch: have %x, want %x", votersResult, voters) 528 return 529 } 530 for j := 0; j < len(votersResult); j++ { 531 if !bytes.Equal(votersResult[j][:], voters[j][:]) { 532 t.Errorf("voter %d: voter mismatch: have %x, want %x", j, votersResult[j], voters[j]) 533 } 534 } 535 }