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