github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/consensus/istanbul/backend/engine.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 backend 18 19 import ( 20 "math/big" 21 "math/rand" 22 "time" 23 24 "github.com/electroneum/electroneum-sc/common" 25 "github.com/electroneum/electroneum-sc/common/math" 26 "github.com/electroneum/electroneum-sc/consensus" 27 "github.com/electroneum/electroneum-sc/consensus/istanbul" 28 istanbulcommon "github.com/electroneum/electroneum-sc/consensus/istanbul/common" 29 "github.com/electroneum/electroneum-sc/consensus/istanbul/validator" 30 "github.com/electroneum/electroneum-sc/core/state" 31 "github.com/electroneum/electroneum-sc/core/types" 32 "github.com/electroneum/electroneum-sc/ethdb" 33 "github.com/electroneum/electroneum-sc/log" 34 "github.com/electroneum/electroneum-sc/params" 35 "github.com/electroneum/electroneum-sc/rpc" 36 ) 37 38 const ( 39 checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database 40 inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory 41 inmemoryEmissions = 128 42 inmemoryPeers = 40 43 inmemoryMessages = 1024 44 ) 45 46 // Author retrieves the Ethereum address of the account that minted the given 47 // block, which may be different from the header's coinbase if a consensus 48 // engine is based on signatures. 49 func (sb *Backend) Author(header *types.Header) (common.Address, error) { 50 return sb.EngineForBlockNumber(header.Number).Author(header) 51 } 52 53 // Signers extracts all the addresses who have signed the given header 54 // It will extract for each seal who signed it, regardless of if the seal is 55 // repeated 56 func (sb *Backend) Signers(header *types.Header) ([]common.Address, error) { 57 return sb.EngineForBlockNumber(header.Number).Signers(header) 58 } 59 60 // VerifyHeader checks whether a header conforms to the consensus rules of a 61 // given engine. Verifying the seal may be done optionally here, or explicitly 62 // via the VerifySeal method. 63 func (sb *Backend) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { 64 return sb.verifyHeader(chain, header, nil) 65 } 66 67 func (sb *Backend) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { 68 // Assemble the voting snapshot 69 snap, err := sb.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, parents) 70 if err != nil { 71 return err 72 } 73 74 // Compute etn emission per block 75 _, err = sb.emission(chain, header.Number.Uint64()-1, header.ParentHash, parents) 76 if err != nil { 77 return err 78 } 79 80 return sb.EngineForBlockNumber(header.Number).VerifyHeader(chain, header, parents, snap.ValSet) 81 } 82 83 // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers 84 // concurrently. The method returns a quit channel to abort the operations and 85 // a results channel to retrieve the async verifications (the order is that of 86 // the input slice). 87 func (sb *Backend) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { 88 abort := make(chan struct{}) 89 results := make(chan error, len(headers)) 90 go func() { 91 errored := false 92 for i, header := range headers { 93 var err error 94 if errored { 95 err = consensus.ErrUnknownAncestor 96 } else { 97 err = sb.verifyHeader(chain, header, headers[:i]) 98 } 99 100 if err != nil { 101 errored = true 102 } 103 104 select { 105 case <-abort: 106 return 107 case results <- err: 108 } 109 } 110 }() 111 return abort, results 112 } 113 114 // VerifyUncles verifies that the given block's uncles conform to the consensus 115 // rules of a given engine. 116 func (sb *Backend) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { 117 return sb.EngineForBlockNumber(block.Header().Number).VerifyUncles(chain, block) 118 } 119 120 // VerifySeal checks whether the crypto seal on a header is valid according to 121 // the consensus rules of the given engine. 122 func (sb *Backend) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error { 123 // get parent header and ensure the signer is in parent's validator set 124 number := header.Number.Uint64() 125 if number == 0 { 126 return istanbulcommon.ErrUnknownBlock 127 } 128 129 // Assemble the voting snapshot 130 snap, err := sb.snapshot(chain, number-1, header.ParentHash, nil) 131 if err != nil { 132 return err 133 } 134 135 return sb.EngineForBlockNumber(header.Number).VerifySeal(chain, header, snap.ValSet) 136 } 137 138 // Prepare initializes the consensus fields of a block header according to the 139 // rules of a particular engine. The changes are executed inline. 140 func (sb *Backend) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { 141 // Assemble the voting snapshot 142 snap, err := sb.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, nil) 143 if err != nil { 144 return err 145 } 146 147 err = sb.EngineForBlockNumber(header.Number).Prepare(chain, header, snap.ValSet) 148 if err != nil { 149 return err 150 } 151 152 // get valid candidate list 153 sb.candidatesLock.RLock() 154 var addresses []common.Address 155 var authorizes []bool 156 for address, authorize := range sb.candidates { 157 if snap.checkVote(address, authorize) { 158 addresses = append(addresses, address) 159 authorizes = append(authorizes, authorize) 160 } 161 } 162 sb.candidatesLock.RUnlock() 163 164 if len(addresses) > 0 { 165 index := rand.Intn(len(addresses)) 166 167 err = sb.EngineForBlockNumber(header.Number).WriteVote(header, addresses[index], authorizes[index]) 168 if err != nil { 169 log.Error("IBFT: error writing validator vote", "err", err) 170 return err 171 } 172 } 173 174 return nil 175 } 176 177 // Finalize runs any post-transaction state modifications (e.g. block rewards) 178 // and assembles the final block. 179 // 180 // Note, the block header and state database might be updated to reflect any 181 // consensus rules that happen at finalization (e.g. block rewards). 182 func (sb *Backend) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { 183 if header.Coinbase != (common.Address{}) { 184 blockReward := sb.GetBaseBlockReward(chain, header) 185 state.AddBalance(header.Coinbase, blockReward) 186 } 187 sb.EngineForBlockNumber(header.Number).Finalize(chain, header, state, txs, uncles) 188 } 189 190 // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, 191 // nor block rewards given, and returns the final block. 192 func (sb *Backend) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { 193 sb.Finalize(chain, header, state, txs, uncles) 194 return sb.EngineForBlockNumber(header.Number).FinalizeAndAssemble(chain, header, state, txs, uncles, receipts) 195 } 196 197 // Seal generates a new block for the given input block with the local miner's 198 // seal place on top. 199 func (sb *Backend) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { 200 // update the block header timestamp and signature and propose the block to core engine 201 header := block.Header() 202 number := header.Number.Uint64() 203 204 // Bail out if we're unauthorized to sign a block 205 snap, err := sb.snapshot(chain, number-1, header.ParentHash, nil) 206 if err != nil { 207 return err 208 } 209 210 block, err = sb.EngineForBlockNumber(header.Number).Seal(chain, block, snap.ValSet) 211 if err != nil { 212 return err 213 } 214 215 delay := time.Until(time.Unix(int64(block.Header().Time), 0)) 216 217 go func() { 218 // wait for the timestamp of header, use this to adjust the block period 219 select { 220 case <-time.After(delay): 221 case <-stop: 222 results <- nil 223 return 224 } 225 226 // get the proposed block hash and clear it if the seal() is completed. 227 sb.sealMu.Lock() 228 sb.proposedBlockHash = block.Hash() 229 230 defer func() { 231 sb.proposedBlockHash = common.Hash{} 232 sb.sealMu.Unlock() 233 }() 234 // post block into Istanbul engine 235 go sb.EventMux().Post(istanbul.RequestEvent{ 236 Proposal: block, 237 }) 238 for { 239 select { 240 case result := <-sb.commitCh: 241 // if the block hash and the hash from channel are the same, 242 // return the result. Otherwise, keep waiting the next hash. 243 if result != nil && block.Hash() == result.Hash() { 244 results <- result 245 return 246 } 247 case <-stop: 248 results <- nil 249 return 250 } 251 } 252 }() 253 return nil 254 } 255 256 func (sb *Backend) GetBaseBlockReward(chain consensus.ChainHeaderReader, header *types.Header) *big.Int { 257 var legacyV9BlockHeight = big.NewInt(862866 * 24) // original 862866 height * 24 to make it compatible with 5-second block time 258 var halvingPeriod = big.NewInt(25228800) // 4 years in 5-second block time 259 var baseReward = big.NewInt(4e+18) // ~100ETN every 120 seconds 260 261 var offset = new(big.Int).Sub(big.NewInt(1500000*24), legacyV9BlockHeight) // block height at time of BC migration - legacyV9BlockHeight 262 263 var offsetBlockNumber = new(big.Int).Add(header.Number, offset) 264 var halvings = new(big.Int).Div(offsetBlockNumber, halvingPeriod) // (blockNumber + offset) / halvingPeriod 265 266 // Get current circulating supply 267 emission, err := sb.emission(chain, header.Number.Uint64()-1, header.ParentHash, nil) 268 if err != nil { 269 return big.NewInt(0) 270 } 271 272 // 0 block reward once circulating supply = max supply 273 if emission.CirculatingSupply.Cmp(math.MustParseBig256(params.ETNMaxSupply)) >= 0 { 274 return big.NewInt(0) 275 } 276 277 // Shift the base reward "halvings" times 278 return new(big.Int).Rsh(baseReward, uint(halvings.Uint64())) 279 } 280 281 func (sb *Backend) GetTotalEmission(chain consensus.ChainHeaderReader, header *types.Header) *big.Int { 282 emission, err := sb.emission(chain, header.Number.Uint64()-1, header.ParentHash, nil) 283 if err != nil { 284 return big.NewInt(0) 285 } 286 return emission.CirculatingSupply 287 } 288 289 // APIs returns the RPC APIs this consensus engine provides. 290 func (sb *Backend) APIs(chain consensus.ChainHeaderReader) []rpc.API { 291 return []rpc.API{{ 292 Namespace: "istanbul", 293 Version: "1.0", 294 Service: &API{chain: chain, backend: sb}, 295 Public: true, 296 }} 297 } 298 299 // Start implements consensus.Istanbul.Start 300 func (sb *Backend) Start(chain consensus.ChainHeaderReader, currentBlock func() *types.Block, hasBadBlock func(db ethdb.Reader, hash common.Hash) bool) error { 301 sb.coreMu.Lock() 302 defer sb.coreMu.Unlock() 303 if sb.coreStarted { 304 return istanbul.ErrStartedEngine 305 } 306 307 // clear previous data 308 sb.proposedBlockHash = common.Hash{} 309 if sb.commitCh != nil { 310 close(sb.commitCh) 311 } 312 sb.commitCh = make(chan *types.Block, 1) 313 314 sb.chain = chain 315 sb.currentBlock = currentBlock 316 sb.hasBadBlock = hasBadBlock 317 318 // Check if qbft Consensus needs to be used after chain is set 319 if err := sb.startQBFT(); err != nil { 320 return err 321 } 322 323 sb.coreStarted = true 324 325 return nil 326 } 327 328 // Stop implements consensus.Istanbul.Stop 329 func (sb *Backend) Stop() error { 330 sb.coreMu.Lock() 331 defer sb.coreMu.Unlock() 332 if !sb.coreStarted { 333 return istanbul.ErrStoppedEngine 334 } 335 if err := sb.stop(); err != nil { 336 return err 337 } 338 sb.coreStarted = false 339 340 return nil 341 } 342 343 func addrsToString(addrs []common.Address) []string { 344 strs := make([]string, len(addrs)) 345 for i, addr := range addrs { 346 strs[i] = addr.String() 347 } 348 return strs 349 } 350 351 func (sb *Backend) emissionLogger(emission *Emission) log.Logger { 352 return sb.logger.New( 353 "emission.number", emission.Number, 354 "emission.hash", emission.Hash.String(), 355 "emission.circulatingsupply", emission.CirculatingSupply, 356 ) 357 } 358 359 func (sb *Backend) storeEmission(emission *Emission) error { 360 logger := sb.emissionLogger(emission) 361 logger.Debug("IBFT: store emission to database") 362 if err := emission.store(sb.db); err != nil { 363 logger.Error("IBFT: failed to store emission to database", "err", err) 364 return err 365 } 366 367 return nil 368 } 369 370 func (sb *Backend) emission(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Emission, error) { 371 var ( 372 headers []*types.Header 373 emission *Emission 374 ) 375 376 for emission == nil { 377 // If an in-memory emission was found, use that 378 if e, ok := sb.recentsEmission.Get(hash); ok { 379 emission = e.(*Emission) 380 sb.emissionLogger(emission).Trace("IBFT: loaded emission from cache") 381 break 382 } 383 // If an on-disk checkpoint emission can be found, use that 384 if number%checkpointInterval == 0 { 385 if s, err := loadEmission(hash, sb.db); err == nil { 386 emission = s 387 sb.emissionLogger(emission).Trace("IBFT: loaded emission from database") 388 break 389 } 390 } 391 392 // If we're at block zero, set initial emission 393 if number == 0 { 394 genesis := chain.GetHeaderByNumber(0) 395 if err := sb.EngineForBlockNumber(big.NewInt(0)).VerifyHeader(chain, genesis, nil, nil); err != nil { 396 sb.logger.Error("IBFT: invalid genesis block", "err", err) 397 return nil, err 398 } 399 400 emission = newEmission(0, genesis.Hash(), chain.Config().GenesisETN) 401 if err := sb.storeEmission(emission); err != nil { 402 return nil, err 403 } 404 break 405 } 406 407 // No emission for this header, gather the header and move backward 408 var header *types.Header 409 if len(parents) > 0 { 410 // If we have explicit parents, pick from there (enforced) 411 header = parents[len(parents)-1] 412 if header.Hash() != hash || header.Number.Uint64() != number { 413 return nil, consensus.ErrUnknownAncestor 414 } 415 parents = parents[:len(parents)-1] 416 } else { 417 // No explicit parents (or no more left), reach out to the database 418 header = chain.GetHeader(hash, number) 419 if header == nil { 420 return nil, consensus.ErrUnknownAncestor 421 } 422 } 423 424 headers = append(headers, header) 425 number, hash = number-1, header.ParentHash 426 } 427 428 // Previous emission found, apply any pending headers on top of it 429 for i := 0; i < len(headers)/2; i++ { 430 headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i] 431 } 432 433 emission, err := sb.emissionApply(chain, emission, headers) 434 if err != nil { 435 return nil, err 436 } 437 sb.recentsEmission.Add(emission.Hash, emission) 438 439 // If we've generated a new checkpoint emission, save to disk 440 if emission.Number%checkpointInterval == 0 && len(headers) > 0 { 441 if err = sb.storeEmission(emission); err != nil { 442 return nil, err 443 } 444 } 445 446 return emission, err 447 } 448 449 func (sb *Backend) emissionApply(chain consensus.ChainHeaderReader, emission *Emission, headers []*types.Header) (*Emission, error) { 450 // Allow passing in no headers for cleaner code 451 if len(headers) == 0 { 452 return emission, nil 453 } 454 // Sanity check that the headers can be applied 455 for i := 0; i < len(headers)-1; i++ { 456 if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 { 457 return nil, istanbulcommon.ErrInvalidVotingChain 458 } 459 } 460 if headers[0].Number.Uint64() != emission.Number+1 { 461 return nil, istanbulcommon.ErrInvalidVotingChain 462 } 463 // Iterate through the headers and create a new snapshot 464 emissionCpy := emission.copy() 465 466 for _, header := range headers { 467 emissionCpy.CirculatingSupply = new(big.Int).Add(emissionCpy.CirculatingSupply, sb.GetBaseBlockReward(chain, header)) 468 } 469 emissionCpy.Number += uint64(len(headers)) 470 emissionCpy.Hash = headers[len(headers)-1].Hash() 471 472 return emissionCpy, nil 473 } 474 475 func (sb *Backend) snapLogger(snap *Snapshot) log.Logger { 476 return sb.logger.New( 477 "snap.number", snap.Number, 478 "snap.hash", snap.Hash.String(), 479 "snap.epoch", snap.Epoch, 480 "snap.validators", addrsToString(snap.validators()), 481 "snap.votes", snap.Votes, 482 ) 483 } 484 485 func (sb *Backend) storeSnap(snap *Snapshot) error { 486 logger := sb.snapLogger(snap) 487 logger.Debug("IBFT: store snapshot to database") 488 if err := snap.store(sb.db); err != nil { 489 logger.Error("IBFT: failed to store snapshot to database", "err", err) 490 return err 491 } 492 493 return nil 494 } 495 496 // snapshot retrieves the authorization snapshot at a given point in time. 497 func (sb *Backend) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) { 498 // Search for a snapshot in memory or on disk for checkpoints 499 var ( 500 headers []*types.Header 501 snap *Snapshot 502 ) 503 for snap == nil { 504 // If an in-memory snapshot was found, use that 505 if s, ok := sb.recents.Get(hash); ok { 506 snap = s.(*Snapshot) 507 sb.snapLogger(snap).Trace("IBFT: loaded voting snapshot from cache") 508 break 509 } 510 // If an on-disk checkpoint snapshot can be found, use that 511 if number%checkpointInterval == 0 { 512 if s, err := loadSnapshot(sb.config.Epoch, sb.db, hash); err == nil { 513 snap = s 514 sb.snapLogger(snap).Trace("IBFT: loaded voting snapshot from database") 515 break 516 } 517 } 518 519 // If we're at block zero, make a snapshot 520 if number == 0 { 521 genesis := chain.GetHeaderByNumber(0) 522 if err := sb.EngineForBlockNumber(big.NewInt(0)).VerifyHeader(chain, genesis, nil, nil); err != nil { 523 sb.logger.Error("IBFT: invalid genesis block", "err", err) 524 return nil, err 525 } 526 527 // Get the validators from genesis to create a snapshot 528 validators, err := sb.EngineForBlockNumber(big.NewInt(0)).Validators(genesis) 529 if err != nil { 530 sb.logger.Error("IBFT: invalid genesis block", "err", err) 531 return nil, err 532 } 533 534 snap = newSnapshot(sb.config.Epoch, 0, genesis.Hash(), validator.NewSet(validators, sb.config.ProposerPolicy)) 535 if err := sb.storeSnap(snap); err != nil { 536 return nil, err 537 } 538 break 539 } 540 541 // No snapshot for this header, gather the header and move backward 542 var header *types.Header 543 if len(parents) > 0 { 544 // If we have explicit parents, pick from there (enforced) 545 header = parents[len(parents)-1] 546 if header.Hash() != hash || header.Number.Uint64() != number { 547 return nil, consensus.ErrUnknownAncestor 548 } 549 parents = parents[:len(parents)-1] 550 } else { 551 // No explicit parents (or no more left), reach out to the database 552 header = chain.GetHeader(hash, number) 553 if header == nil { 554 return nil, consensus.ErrUnknownAncestor 555 } 556 } 557 558 headers = append(headers, header) 559 number, hash = number-1, header.ParentHash 560 } 561 562 // Previous snapshot found, apply any pending headers on top of it 563 for i := 0; i < len(headers)/2; i++ { 564 headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i] 565 } 566 567 snap, err := sb.snapApply(snap, headers) 568 if err != nil { 569 return nil, err 570 } 571 sb.recents.Add(snap.Hash, snap) 572 573 // If we've generated a new checkpoint snapshot, save to disk 574 if snap.Number%checkpointInterval == 0 && len(headers) > 0 { 575 if err = sb.storeSnap(snap); err != nil { 576 return nil, err 577 } 578 } 579 580 return snap, err 581 } 582 583 // SealHash returns the hash of a block prior to it being sealed. 584 func (sb *Backend) SealHash(header *types.Header) common.Hash { 585 return sb.EngineForBlockNumber(header.Number).SealHash(header) 586 } 587 588 func (sb *Backend) snapApply(snap *Snapshot, headers []*types.Header) (*Snapshot, error) { 589 // Allow passing in no headers for cleaner code 590 if len(headers) == 0 { 591 return snap, nil 592 } 593 // Sanity check that the headers can be applied 594 for i := 0; i < len(headers)-1; i++ { 595 if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 { 596 return nil, istanbulcommon.ErrInvalidVotingChain 597 } 598 } 599 if headers[0].Number.Uint64() != snap.Number+1 { 600 return nil, istanbulcommon.ErrInvalidVotingChain 601 } 602 // Iterate through the headers and create a new snapshot 603 snapCpy := snap.copy() 604 605 for _, header := range headers { 606 err := sb.snapApplyHeader(snapCpy, header) 607 if err != nil { 608 return nil, err 609 } 610 } 611 snapCpy.Number += uint64(len(headers)) 612 snapCpy.Hash = headers[len(headers)-1].Hash() 613 614 return snapCpy, nil 615 } 616 617 func (sb *Backend) snapApplyHeader(snap *Snapshot, header *types.Header) error { 618 logger := sb.snapLogger(snap).New("header.number", header.Number.Uint64(), "header.hash", header.Hash().String()) 619 620 logger.Trace("IBFT: apply header to voting snapshot") 621 622 // Remove any votes on checkpoint blocks 623 number := header.Number.Uint64() 624 if number%snap.Epoch == 0 { 625 snap.Votes = nil 626 snap.Tally = make(map[common.Address]Tally) 627 } 628 629 // Resolve the authorization key and check against validators 630 validator, err := sb.EngineForBlockNumber(header.Number).Author(header) 631 if err != nil { 632 logger.Error("IBFT: invalid header author", "err", err) 633 return err 634 } 635 636 logger = logger.New("header.author", validator) 637 638 if _, v := snap.ValSet.GetByAddress(validator); v == nil { 639 logger.Error("IBFT: header author is not a validator") 640 return istanbulcommon.ErrUnauthorized 641 } 642 643 // Read vote from header 644 candidate, authorize, err := sb.EngineForBlockNumber(header.Number).ReadVote(header) 645 if err != nil { 646 logger.Error("IBFT: invalid header vote", "err", err) 647 return err 648 } 649 650 logger = logger.New("candidate", candidate.String(), "authorize", authorize) 651 // Header authorized, discard any previous votes from the validator 652 for i, vote := range snap.Votes { 653 if vote.Validator == validator && vote.Address == candidate { 654 logger.Trace("IBFT: discard previous vote from tally", "old.authorize", vote.Authorize) 655 // Uncast the vote from the cached tally 656 snap.uncast(vote.Address, vote.Authorize) 657 658 // Uncast the vote from the chronological list 659 snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) 660 break // only one vote allowed 661 } 662 } 663 664 logger.Debug("IBFT: add vote to tally") 665 if snap.cast(candidate, authorize) { 666 snap.Votes = append(snap.Votes, &Vote{ 667 Validator: validator, 668 Block: number, 669 Address: candidate, 670 Authorize: authorize, 671 }) 672 } 673 674 // If the vote passed, update the list of validators 675 if tally := snap.Tally[candidate]; tally.Votes > snap.ValSet.Size()/2 { 676 if tally.Authorize { 677 logger.Info("IBFT: reached majority to add validator") 678 snap.ValSet.AddValidator(candidate) 679 } else { 680 logger.Info("IBFT: reached majority to remove validator") 681 snap.ValSet.RemoveValidator(candidate) 682 683 // Discard any previous votes the deauthorized validator cast 684 for i := 0; i < len(snap.Votes); i++ { 685 if snap.Votes[i].Validator == candidate { 686 // Uncast the vote from the cached tally 687 snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize) 688 689 // Uncast the vote from the chronological list 690 snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) 691 692 i-- 693 } 694 } 695 } 696 // Discard any previous votes around the just changed account 697 for i := 0; i < len(snap.Votes); i++ { 698 if snap.Votes[i].Address == candidate { 699 snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) 700 i-- 701 } 702 } 703 delete(snap.Tally, candidate) 704 } 705 return nil 706 }