github.com/luckypickle/go-ethereum-vet@v1.14.2/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 "testing" 24 25 "github.com/luckypickle/go-ethereum-vet/common" 26 "github.com/luckypickle/go-ethereum-vet/core" 27 "github.com/luckypickle/go-ethereum-vet/core/rawdb" 28 "github.com/luckypickle/go-ethereum-vet/core/types" 29 "github.com/luckypickle/go-ethereum-vet/crypto" 30 "github.com/luckypickle/go-ethereum-vet/ethdb" 31 "github.com/luckypickle/go-ethereum-vet/params" 32 ) 33 34 type testerVote struct { 35 signer string 36 voted string 37 auth bool 38 } 39 40 // testerAccountPool is a pool to maintain currently active tester accounts, 41 // mapped from textual names used in the tests below to actual Ethereum private 42 // keys capable of signing transactions. 43 type testerAccountPool struct { 44 accounts map[string]*ecdsa.PrivateKey 45 } 46 47 func newTesterAccountPool() *testerAccountPool { 48 return &testerAccountPool{ 49 accounts: make(map[string]*ecdsa.PrivateKey), 50 } 51 } 52 53 func (ap *testerAccountPool) sign(header *types.Header, signer string) { 54 // Ensure we have a persistent key for the signer 55 if ap.accounts[signer] == nil { 56 ap.accounts[signer], _ = crypto.GenerateKey() 57 } 58 // Sign the header and embed the signature in extra data 59 sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer]) 60 copy(header.Extra[len(header.Extra)-65:], sig) 61 } 62 63 func (ap *testerAccountPool) address(account string) common.Address { 64 // Ensure we have a persistent key for the account 65 if ap.accounts[account] == nil { 66 ap.accounts[account], _ = crypto.GenerateKey() 67 } 68 // Resolve and return the Ethereum address 69 return crypto.PubkeyToAddress(ap.accounts[account].PublicKey) 70 } 71 72 // testerChainReader implements consensus.ChainReader to access the genesis 73 // block. All other methods and requests will panic. 74 type testerChainReader struct { 75 db ethdb.Database 76 } 77 78 func (r *testerChainReader) Config() *params.ChainConfig { return params.AllCliqueProtocolChanges } 79 func (r *testerChainReader) CurrentHeader() *types.Header { panic("not supported") } 80 func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") } 81 func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic("not supported") } 82 func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") } 83 func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header { 84 if number == 0 { 85 return rawdb.ReadHeader(r.db, rawdb.ReadCanonicalHash(r.db, 0), 0) 86 } 87 return nil 88 } 89 90 // Tests that voting is evaluated correctly for various simple and complex scenarios. 91 func TestVoting(t *testing.T) { 92 // Define the various voting scenarios to test 93 tests := []struct { 94 epoch uint64 95 signers []string 96 votes []testerVote 97 results []string 98 }{ 99 { 100 // Single signer, no votes cast 101 signers: []string{"A"}, 102 votes: []testerVote{{signer: "A"}}, 103 results: []string{"A"}, 104 }, { 105 // Single signer, voting to add two others (only accept first, second needs 2 votes) 106 signers: []string{"A"}, 107 votes: []testerVote{ 108 {signer: "A", voted: "B", auth: true}, 109 {signer: "B"}, 110 {signer: "A", voted: "C", auth: true}, 111 }, 112 results: []string{"A", "B"}, 113 }, { 114 // Two signers, voting to add three others (only accept first two, third needs 3 votes already) 115 signers: []string{"A", "B"}, 116 votes: []testerVote{ 117 {signer: "A", voted: "C", auth: true}, 118 {signer: "B", voted: "C", auth: true}, 119 {signer: "A", voted: "D", auth: true}, 120 {signer: "B", voted: "D", auth: true}, 121 {signer: "C"}, 122 {signer: "A", voted: "E", auth: true}, 123 {signer: "B", voted: "E", auth: true}, 124 }, 125 results: []string{"A", "B", "C", "D"}, 126 }, { 127 // Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this) 128 signers: []string{"A"}, 129 votes: []testerVote{ 130 {signer: "A", voted: "A", auth: false}, 131 }, 132 results: []string{}, 133 }, { 134 // Two signers, actually needing mutual consent to drop either of them (not fulfilled) 135 signers: []string{"A", "B"}, 136 votes: []testerVote{ 137 {signer: "A", voted: "B", auth: false}, 138 }, 139 results: []string{"A", "B"}, 140 }, { 141 // Two signers, actually needing mutual consent to drop either of them (fulfilled) 142 signers: []string{"A", "B"}, 143 votes: []testerVote{ 144 {signer: "A", voted: "B", auth: false}, 145 {signer: "B", voted: "B", auth: false}, 146 }, 147 results: []string{"A"}, 148 }, { 149 // Three signers, two of them deciding to drop the third 150 signers: []string{"A", "B", "C"}, 151 votes: []testerVote{ 152 {signer: "A", voted: "C", auth: false}, 153 {signer: "B", voted: "C", auth: false}, 154 }, 155 results: []string{"A", "B"}, 156 }, { 157 // Four signers, consensus of two not being enough to drop anyone 158 signers: []string{"A", "B", "C", "D"}, 159 votes: []testerVote{ 160 {signer: "A", voted: "C", auth: false}, 161 {signer: "B", voted: "C", auth: false}, 162 }, 163 results: []string{"A", "B", "C", "D"}, 164 }, { 165 // Four signers, consensus of three already being enough to drop someone 166 signers: []string{"A", "B", "C", "D"}, 167 votes: []testerVote{ 168 {signer: "A", voted: "D", auth: false}, 169 {signer: "B", voted: "D", auth: false}, 170 {signer: "C", voted: "D", auth: false}, 171 }, 172 results: []string{"A", "B", "C"}, 173 }, { 174 // Authorizations are counted once per signer per target 175 signers: []string{"A", "B"}, 176 votes: []testerVote{ 177 {signer: "A", voted: "C", auth: true}, 178 {signer: "B"}, 179 {signer: "A", voted: "C", auth: true}, 180 {signer: "B"}, 181 {signer: "A", voted: "C", auth: true}, 182 }, 183 results: []string{"A", "B"}, 184 }, { 185 // Authorizing multiple accounts concurrently is permitted 186 signers: []string{"A", "B"}, 187 votes: []testerVote{ 188 {signer: "A", voted: "C", auth: true}, 189 {signer: "B"}, 190 {signer: "A", voted: "D", auth: true}, 191 {signer: "B"}, 192 {signer: "A"}, 193 {signer: "B", voted: "D", auth: true}, 194 {signer: "A"}, 195 {signer: "B", voted: "C", auth: true}, 196 }, 197 results: []string{"A", "B", "C", "D"}, 198 }, { 199 // Deauthorizations are counted once per signer per target 200 signers: []string{"A", "B"}, 201 votes: []testerVote{ 202 {signer: "A", voted: "B", auth: false}, 203 {signer: "B"}, 204 {signer: "A", voted: "B", auth: false}, 205 {signer: "B"}, 206 {signer: "A", voted: "B", auth: false}, 207 }, 208 results: []string{"A", "B"}, 209 }, { 210 // Deauthorizing multiple accounts concurrently is permitted 211 signers: []string{"A", "B", "C", "D"}, 212 votes: []testerVote{ 213 {signer: "A", voted: "C", auth: false}, 214 {signer: "B"}, 215 {signer: "C"}, 216 {signer: "A", voted: "D", auth: false}, 217 {signer: "B"}, 218 {signer: "C"}, 219 {signer: "A"}, 220 {signer: "B", voted: "D", auth: false}, 221 {signer: "C", voted: "D", auth: false}, 222 {signer: "A"}, 223 {signer: "B", voted: "C", auth: false}, 224 }, 225 results: []string{"A", "B"}, 226 }, { 227 // Votes from deauthorized signers are discarded immediately (deauth votes) 228 signers: []string{"A", "B", "C"}, 229 votes: []testerVote{ 230 {signer: "C", voted: "B", auth: false}, 231 {signer: "A", voted: "C", auth: false}, 232 {signer: "B", voted: "C", auth: false}, 233 {signer: "A", voted: "B", auth: false}, 234 }, 235 results: []string{"A", "B"}, 236 }, { 237 // Votes from deauthorized signers are discarded immediately (auth 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 // Cascading changes are not allowed, only the account being voted on may change 248 signers: []string{"A", "B", "C", "D"}, 249 votes: []testerVote{ 250 {signer: "A", voted: "C", auth: false}, 251 {signer: "B"}, 252 {signer: "C"}, 253 {signer: "A", voted: "D", auth: false}, 254 {signer: "B", voted: "C", auth: false}, 255 {signer: "C"}, 256 {signer: "A"}, 257 {signer: "B", voted: "D", auth: false}, 258 {signer: "C", voted: "D", auth: false}, 259 }, 260 results: []string{"A", "B", "C"}, 261 }, { 262 // Changes reaching consensus out of bounds (via a deauth) execute on touch 263 signers: []string{"A", "B", "C", "D"}, 264 votes: []testerVote{ 265 {signer: "A", voted: "C", auth: false}, 266 {signer: "B"}, 267 {signer: "C"}, 268 {signer: "A", voted: "D", auth: false}, 269 {signer: "B", voted: "C", auth: false}, 270 {signer: "C"}, 271 {signer: "A"}, 272 {signer: "B", voted: "D", auth: false}, 273 {signer: "C", voted: "D", auth: false}, 274 {signer: "A"}, 275 {signer: "C", voted: "C", auth: true}, 276 }, 277 results: []string{"A", "B"}, 278 }, { 279 // Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch 280 signers: []string{"A", "B", "C", "D"}, 281 votes: []testerVote{ 282 {signer: "A", voted: "C", auth: false}, 283 {signer: "B"}, 284 {signer: "C"}, 285 {signer: "A", voted: "D", auth: false}, 286 {signer: "B", voted: "C", auth: false}, 287 {signer: "C"}, 288 {signer: "A"}, 289 {signer: "B", voted: "D", auth: false}, 290 {signer: "C", voted: "D", auth: false}, 291 {signer: "A"}, 292 {signer: "B", voted: "C", auth: true}, 293 }, 294 results: []string{"A", "B", "C"}, 295 }, { 296 // Ensure that pending votes don't survive authorization status changes. This 297 // corner case can only appear if a signer is quickly added, removed and then 298 // readded (or the inverse), while one of the original voters dropped. If a 299 // past vote is left cached in the system somewhere, this will interfere with 300 // the final signer outcome. 301 signers: []string{"A", "B", "C", "D", "E"}, 302 votes: []testerVote{ 303 {signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed 304 {signer: "B", voted: "F", auth: true}, 305 {signer: "C", voted: "F", auth: true}, 306 {signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged") 307 {signer: "E", voted: "F", auth: false}, 308 {signer: "B", voted: "F", auth: false}, 309 {signer: "C", voted: "F", auth: false}, 310 {signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed 311 {signer: "E", voted: "F", auth: true}, 312 {signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed 313 {signer: "C", voted: "A", auth: false}, 314 {signer: "D", voted: "A", auth: false}, 315 {signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed 316 }, 317 results: []string{"B", "C", "D", "E", "F"}, 318 }, { 319 // Epoch transitions reset all votes to allow chain checkpointing 320 epoch: 3, 321 signers: []string{"A", "B"}, 322 votes: []testerVote{ 323 {signer: "A", voted: "C", auth: true}, 324 {signer: "B"}, 325 {signer: "A"}, // Checkpoint block, (don't vote here, it's validated outside of snapshots) 326 {signer: "B", voted: "C", auth: true}, 327 }, 328 results: []string{"A", "B"}, 329 }, 330 } 331 // Run through the scenarios and test them 332 for i, tt := range tests { 333 // Create the account pool and generate the initial set of signers 334 accounts := newTesterAccountPool() 335 336 signers := make([]common.Address, len(tt.signers)) 337 for j, signer := range tt.signers { 338 signers[j] = accounts.address(signer) 339 } 340 for j := 0; j < len(signers); j++ { 341 for k := j + 1; k < len(signers); k++ { 342 if bytes.Compare(signers[j][:], signers[k][:]) > 0 { 343 signers[j], signers[k] = signers[k], signers[j] 344 } 345 } 346 } 347 // Create the genesis block with the initial set of signers 348 genesis := &core.Genesis{ 349 ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal), 350 } 351 for j, signer := range signers { 352 copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:]) 353 } 354 // Create a pristine blockchain with the genesis injected 355 db := ethdb.NewMemDatabase() 356 genesis.Commit(db) 357 358 // Assemble a chain of headers from the cast votes 359 headers := make([]*types.Header, len(tt.votes)) 360 for j, vote := range tt.votes { 361 headers[j] = &types.Header{ 362 Number: big.NewInt(int64(j) + 1), 363 Time: big.NewInt(int64(j) * 15), 364 Coinbase: accounts.address(vote.voted), 365 Extra: make([]byte, extraVanity+extraSeal), 366 } 367 if j > 0 { 368 headers[j].ParentHash = headers[j-1].Hash() 369 } 370 if vote.auth { 371 copy(headers[j].Nonce[:], nonceAuthVote) 372 } 373 accounts.sign(headers[j], vote.signer) 374 } 375 // Pass all the headers through clique and ensure tallying succeeds 376 head := headers[len(headers)-1] 377 378 snap, err := New(¶ms.CliqueConfig{Epoch: tt.epoch}, db).snapshot(&testerChainReader{db: db}, head.Number.Uint64(), head.Hash(), headers) 379 if err != nil { 380 t.Errorf("test %d: failed to create voting snapshot: %v", i, err) 381 continue 382 } 383 // Verify the final list of signers against the expected ones 384 signers = make([]common.Address, len(tt.results)) 385 for j, signer := range tt.results { 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 result := snap.signers() 396 if len(result) != len(signers) { 397 t.Errorf("test %d: signers mismatch: have %x, want %x", i, result, signers) 398 continue 399 } 400 for j := 0; j < len(result); j++ { 401 if !bytes.Equal(result[j][:], signers[j][:]) { 402 t.Errorf("test %d, signer %d: signer mismatch: have %x, want %x", i, j, result[j], signers[j]) 403 } 404 } 405 } 406 }