engine.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  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. package backend
  17. import (
  18. "fmt"
  19. "math/big"
  20. "math/rand"
  21. "time"
  22. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  23. "github.com/ethereum/go-ethereum/common"
  24. "github.com/ethereum/go-ethereum/consensus"
  25. "github.com/ethereum/go-ethereum/consensus/istanbul"
  26. "github.com/ethereum/go-ethereum/consensus/istanbul/backend/contract"
  27. istanbulcommon "github.com/ethereum/go-ethereum/consensus/istanbul/common"
  28. "github.com/ethereum/go-ethereum/consensus/istanbul/validator"
  29. "github.com/ethereum/go-ethereum/core/state"
  30. "github.com/ethereum/go-ethereum/core/types"
  31. "github.com/ethereum/go-ethereum/ethdb"
  32. "github.com/ethereum/go-ethereum/log"
  33. "github.com/ethereum/go-ethereum/params"
  34. "github.com/ethereum/go-ethereum/rpc"
  35. )
  36. const (
  37. checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database
  38. inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
  39. inmemoryPeers = 40
  40. inmemoryMessages = 1024
  41. )
  42. // Author retrieves the Ethereum address of the account that minted the given
  43. // block, which may be different from the header's coinbase if a consensus
  44. // engine is based on signatures.
  45. func (sb *Backend) Author(header *types.Header) (common.Address, error) {
  46. return sb.EngineForBlockNumber(header.Number).Author(header)
  47. }
  48. // Signers extracts all the addresses who have signed the given header
  49. // It will extract for each seal who signed it, regardless of if the seal is
  50. // repeated
  51. func (sb *Backend) Signers(header *types.Header) ([]common.Address, error) {
  52. return sb.EngineForBlockNumber(header.Number).Signers(header)
  53. }
  54. // VerifyHeader checks whether a header conforms to the consensus rules of a
  55. // given engine. Verifying the seal may be done optionally here, or explicitly
  56. // via the VerifySeal method.
  57. func (sb *Backend) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
  58. return sb.verifyHeader(chain, header, nil)
  59. }
  60. func (sb *Backend) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
  61. // Assemble the voting snapshot
  62. snap, err := sb.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, parents)
  63. if err != nil {
  64. return err
  65. }
  66. return sb.EngineForBlockNumber(header.Number).VerifyHeader(chain, header, parents, snap.ValSet)
  67. }
  68. // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
  69. // concurrently. The method returns a quit channel to abort the operations and
  70. // a results channel to retrieve the async verifications (the order is that of
  71. // the input slice).
  72. func (sb *Backend) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
  73. abort := make(chan struct{})
  74. results := make(chan error, len(headers))
  75. go func() {
  76. errored := false
  77. for i, header := range headers {
  78. var err error
  79. if errored {
  80. err = consensus.ErrUnknownAncestor
  81. } else {
  82. err = sb.verifyHeader(chain, header, headers[:i])
  83. }
  84. if err != nil {
  85. errored = true
  86. }
  87. select {
  88. case <-abort:
  89. return
  90. case results <- err:
  91. }
  92. }
  93. }()
  94. return abort, results
  95. }
  96. // VerifyUncles verifies that the given block's uncles conform to the consensus
  97. // rules of a given engine.
  98. func (sb *Backend) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
  99. return sb.EngineForBlockNumber(block.Header().Number).VerifyUncles(chain, block)
  100. }
  101. // VerifySeal checks whether the crypto seal on a header is valid according to
  102. // the consensus rules of the given engine.
  103. func (sb *Backend) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
  104. // get parent header and ensure the signer is in parent's validator set
  105. number := header.Number.Uint64()
  106. if number == 0 {
  107. return istanbulcommon.ErrUnknownBlock
  108. }
  109. // Assemble the voting snapshot
  110. snap, err := sb.snapshot(chain, number-1, header.ParentHash, nil)
  111. if err != nil {
  112. return err
  113. }
  114. return sb.EngineForBlockNumber(header.Number).VerifySeal(chain, header, snap.ValSet)
  115. }
  116. // Prepare initializes the consensus fields of a block header according to the
  117. // rules of a particular engine. The changes are executed inline.
  118. func (sb *Backend) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
  119. // Assemble the voting snapshot
  120. snap, err := sb.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, nil)
  121. if err != nil {
  122. return err
  123. }
  124. err = sb.EngineForBlockNumber(header.Number).Prepare(chain, header, snap.ValSet)
  125. if err != nil {
  126. return err
  127. }
  128. // get valid candidate list
  129. sb.candidatesLock.RLock()
  130. var addresses []common.Address
  131. var authorizes []bool
  132. for address, authorize := range sb.candidates {
  133. if snap.checkVote(address, authorize) {
  134. addresses = append(addresses, address)
  135. authorizes = append(authorizes, authorize)
  136. }
  137. }
  138. sb.candidatesLock.RUnlock()
  139. if len(addresses) > 0 {
  140. index := rand.Intn(len(addresses))
  141. err = sb.EngineForBlockNumber(header.Number).WriteVote(header, addresses[index], authorizes[index])
  142. if err != nil {
  143. log.Error("BFT: error writing validator vote", "err", err)
  144. return err
  145. }
  146. }
  147. return nil
  148. }
  149. // Finalize runs any post-transaction state modifications (e.g. block rewards)
  150. // and assembles the final block.
  151. //
  152. // Note, the block header and state database might be updated to reflect any
  153. // consensus rules that happen at finalization (e.g. block rewards).
  154. func (sb *Backend) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
  155. sb.EngineForBlockNumber(header.Number).Finalize(chain, header, state, txs, uncles)
  156. }
  157. // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
  158. // nor block rewards given, and returns the final block.
  159. 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) {
  160. return sb.EngineForBlockNumber(header.Number).FinalizeAndAssemble(chain, header, state, txs, uncles, receipts)
  161. }
  162. // Seal generates a new block for the given input block with the local miner's
  163. // seal place on top.
  164. func (sb *Backend) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
  165. // update the block header timestamp and signature and propose the block to core engine
  166. header := block.Header()
  167. number := header.Number.Uint64()
  168. // Bail out if we're unauthorized to sign a block
  169. snap, err := sb.snapshot(chain, number-1, header.ParentHash, nil)
  170. if err != nil {
  171. return err
  172. }
  173. block, err = sb.EngineForBlockNumber(header.Number).Seal(chain, block, snap.ValSet)
  174. if err != nil {
  175. return err
  176. }
  177. delay := time.Until(time.Unix(int64(block.Header().Time), 0))
  178. go func() {
  179. // wait for the timestamp of header, use this to adjust the block period
  180. select {
  181. case <-time.After(delay):
  182. case <-stop:
  183. results <- nil
  184. return
  185. }
  186. // get the proposed block hash and clear it if the seal() is completed.
  187. sb.sealMu.Lock()
  188. sb.proposedBlockHash = block.Hash()
  189. defer func() {
  190. sb.proposedBlockHash = common.Hash{}
  191. sb.sealMu.Unlock()
  192. }()
  193. // post block into Istanbul engine
  194. go sb.EventMux().Post(istanbul.RequestEvent{
  195. Proposal: block,
  196. })
  197. for {
  198. select {
  199. case result := <-sb.commitCh:
  200. // if the block hash and the hash from channel are the same,
  201. // return the result. Otherwise, keep waiting the next hash.
  202. if result != nil && block.Hash() == result.Hash() {
  203. results <- result
  204. return
  205. }
  206. case <-stop:
  207. results <- nil
  208. return
  209. }
  210. }
  211. }()
  212. return nil
  213. }
  214. // APIs returns the RPC APIs this consensus engine provides.
  215. func (sb *Backend) APIs(chain consensus.ChainHeaderReader) []rpc.API {
  216. return []rpc.API{{
  217. Namespace: "istanbul",
  218. Version: "1.0",
  219. Service: &API{chain: chain, backend: sb},
  220. Public: true,
  221. }}
  222. }
  223. // Start implements consensus.Istanbul.Start
  224. func (sb *Backend) Start(chain consensus.ChainHeaderReader, currentBlock func() *types.Block, hasBadBlock func(db ethdb.Reader, hash common.Hash) bool) error {
  225. sb.coreMu.Lock()
  226. defer sb.coreMu.Unlock()
  227. if sb.coreStarted {
  228. return istanbul.ErrStartedEngine
  229. }
  230. // clear previous data
  231. sb.proposedBlockHash = common.Hash{}
  232. if sb.commitCh != nil {
  233. close(sb.commitCh)
  234. }
  235. sb.commitCh = make(chan *types.Block, 1)
  236. sb.chain = chain
  237. sb.currentBlock = currentBlock
  238. sb.hasBadBlock = hasBadBlock
  239. // Check if qbft Consensus needs to be used after chain is set
  240. var err error
  241. if sb.IsQBFTConsensus() {
  242. log.Info("start QBFT")
  243. err = sb.startQBFT()
  244. } else {
  245. log.Info("start IBFT")
  246. err = sb.startIBFT()
  247. }
  248. if err != nil {
  249. return err
  250. }
  251. sb.coreStarted = true
  252. return nil
  253. }
  254. // Stop implements consensus.Istanbul.Stop
  255. func (sb *Backend) Stop() error {
  256. sb.coreMu.Lock()
  257. defer sb.coreMu.Unlock()
  258. if !sb.coreStarted {
  259. return istanbul.ErrStoppedEngine
  260. }
  261. if err := sb.stop(); err != nil {
  262. return err
  263. }
  264. sb.coreStarted = false
  265. return nil
  266. }
  267. func addrsToString(addrs []common.Address) []string {
  268. strs := make([]string, len(addrs))
  269. for i, addr := range addrs {
  270. strs[i] = addr.String()
  271. }
  272. return strs
  273. }
  274. func (sb *Backend) snapLogger(snap *Snapshot) log.Logger {
  275. return sb.logger.New(
  276. "snap.number", snap.Number,
  277. "snap.hash", snap.Hash.String(),
  278. "snap.epoch", snap.Epoch,
  279. "snap.validators", addrsToString(snap.validators()),
  280. "snap.votes", snap.Votes,
  281. )
  282. }
  283. func (sb *Backend) storeSnap(snap *Snapshot) error {
  284. logger := sb.snapLogger(snap)
  285. logger.Debug("BFT: store snapshot to database")
  286. if err := snap.store(sb.db); err != nil {
  287. logger.Error("BFT: failed to store snapshot to database", "err", err)
  288. return err
  289. }
  290. return nil
  291. }
  292. // snapshot retrieves the authorization snapshot at a given point in time.
  293. func (sb *Backend) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
  294. // Search for a snapshot in memory or on disk for checkpoints
  295. var (
  296. headers []*types.Header
  297. snap *Snapshot
  298. )
  299. for snap == nil {
  300. // If an in-memory snapshot was found, use that
  301. if s, ok := sb.recents.Get(hash); ok {
  302. snap = s.(*Snapshot)
  303. sb.snapLogger(snap).Trace("BFT: loaded voting snapshot from cache")
  304. break
  305. }
  306. // If an on-disk checkpoint snapshot can be found, use that
  307. if number%checkpointInterval == 0 {
  308. if s, err := loadSnapshot(sb.config.GetConfig(new(big.Int).SetUint64(number)).Epoch, sb.db, hash); err == nil {
  309. snap = s
  310. sb.snapLogger(snap).Trace("BFT: loaded voting snapshot from database")
  311. break
  312. }
  313. }
  314. // If we're at block zero, make a snapshot
  315. if number == 0 {
  316. genesis := chain.GetHeaderByNumber(0)
  317. if err := sb.EngineForBlockNumber(big.NewInt(0)).VerifyHeader(chain, genesis, nil, nil); err != nil {
  318. sb.logger.Error("BFT: invalid genesis block", "err", err)
  319. return nil, err
  320. }
  321. var validators []common.Address
  322. validatorContract := sb.config.GetValidatorContractAddress(big.NewInt(0))
  323. if validatorContract != (common.Address{}) && sb.config.GetValidatorSelectionMode(big.NewInt(0)) == params.ContractMode {
  324. validatorContractCaller, err := contract.NewValidatorContractInterfaceCaller(validatorContract, sb.config.Client)
  325. if err != nil {
  326. return nil, fmt.Errorf("invalid smart contract in genesis alloc: %w", err)
  327. }
  328. opts := bind.CallOpts{
  329. Pending: false,
  330. BlockNumber: big.NewInt(0),
  331. }
  332. validators, err = validatorContractCaller.GetValidators(&opts)
  333. log.Trace("BFT: Initialising snap with contract validators", "address", validatorContract, "validators", validators)
  334. if err != nil {
  335. log.Error("BFT: invalid smart contract in genesis alloc", "err", err)
  336. return nil, err
  337. }
  338. } else {
  339. validatorsFromConfig := sb.config.GetValidatorsAt(big.NewInt(0))
  340. if len(validatorsFromConfig) > 0 {
  341. validators = validatorsFromConfig
  342. log.Info("BFT: Initialising snap with config validators", "validators", validators)
  343. } else {
  344. var err error
  345. validators, err = sb.EngineForBlockNumber(big.NewInt(0)).ExtractGenesisValidators(genesis)
  346. log.Info("BFT: Initialising snap with extradata", "validators", validators)
  347. if err != nil {
  348. log.Error("BFT: invalid genesis block", "err", err)
  349. return nil, err
  350. }
  351. }
  352. }
  353. snap = newSnapshot(sb.config.GetConfig(new(big.Int).SetUint64(number)).Epoch, 0, genesis.Hash(), validator.NewSet(validators, sb.config.ProposerPolicy))
  354. if err := sb.storeSnap(snap); err != nil {
  355. return nil, err
  356. }
  357. break
  358. }
  359. // No snapshot for this header, gather the header and move backward
  360. var header *types.Header
  361. if len(parents) > 0 {
  362. // If we have explicit parents, pick from there (enforced)
  363. header = parents[len(parents)-1]
  364. if header.Hash() != hash || header.Number.Uint64() != number {
  365. return nil, consensus.ErrUnknownAncestor
  366. }
  367. parents = parents[:len(parents)-1]
  368. } else {
  369. // No explicit parents (or no more left), reach out to the database
  370. header = chain.GetHeader(hash, number)
  371. if header == nil {
  372. return nil, consensus.ErrUnknownAncestor
  373. }
  374. }
  375. headers = append(headers, header)
  376. number, hash = number-1, header.ParentHash
  377. }
  378. // Previous snapshot found, apply any pending headers on top of it
  379. for i := 0; i < len(headers)/2; i++ {
  380. headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
  381. }
  382. snap, err := sb.snapApply(snap, headers)
  383. if err != nil {
  384. return nil, err
  385. }
  386. sb.recents.Add(snap.Hash, snap)
  387. targetBlockHeight := new(big.Int).SetUint64(number)
  388. validatorContract := sb.config.GetValidatorContractAddress(targetBlockHeight)
  389. // we only need to update the validator set if it's a new block
  390. if len(headers) == 0 && validatorContract != (common.Address{}) && sb.config.GetValidatorSelectionMode(targetBlockHeight) == params.ContractMode {
  391. sb.logger.Trace("Applying snap with smart contract validators", "address", validatorContract, "client", sb.config.Client)
  392. validatorContractCaller, err := contract.NewValidatorContractInterfaceCaller(validatorContract, sb.config.Client)
  393. if err != nil {
  394. return nil, fmt.Errorf("BFT: invalid smart contract in genesis alloc: %w", err)
  395. }
  396. opts := bind.CallOpts{
  397. Pending: false,
  398. BlockNumber: targetBlockHeight,
  399. }
  400. validators, err := validatorContractCaller.GetValidators(&opts)
  401. if err != nil {
  402. log.Error("BFT: invalid validator smart contract", "err", err)
  403. return nil, err
  404. }
  405. sb.logger.Trace("Fetched validators from smart contract", "validators", validators)
  406. valSet := validator.NewSet(validators, sb.config.ProposerPolicy)
  407. snap.ValSet = valSet
  408. } else if validatorsFromTransitions := sb.config.GetValidatorsAt(targetBlockHeight); len(validatorsFromTransitions) > 0 && sb.config.GetValidatorSelectionMode(targetBlockHeight) == params.BlockHeaderMode {
  409. //Note! we only want to set this once at this block height. Subsequent blocks will be propagated with the same
  410. // validator as they are copied into the block header on the next block. Then normal voting can take place
  411. // again.
  412. valSet := validator.NewSet(validatorsFromTransitions, sb.config.ProposerPolicy)
  413. snap.ValSet = valSet
  414. }
  415. // If we've generated a new checkpoint snapshot, save to disk
  416. if snap.Number%checkpointInterval == 0 && len(headers) > 0 {
  417. if err = sb.storeSnap(snap); err != nil {
  418. return nil, err
  419. }
  420. }
  421. return snap, err
  422. }
  423. // SealHash returns the hash of a block prior to it being sealed.
  424. func (sb *Backend) SealHash(header *types.Header) common.Hash {
  425. return sb.EngineForBlockNumber(header.Number).SealHash(header)
  426. }
  427. func (sb *Backend) snapApply(snap *Snapshot, headers []*types.Header) (*Snapshot, error) {
  428. // Allow passing in no headers for cleaner code
  429. if len(headers) == 0 {
  430. return snap, nil
  431. }
  432. // Sanity check that the headers can be applied
  433. for i := 0; i < len(headers)-1; i++ {
  434. if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
  435. return nil, istanbulcommon.ErrInvalidVotingChain
  436. }
  437. }
  438. if headers[0].Number.Uint64() != snap.Number+1 {
  439. return nil, istanbulcommon.ErrInvalidVotingChain
  440. }
  441. // Iterate through the headers and create a new snapshot
  442. snapCpy := snap.copy()
  443. for _, header := range headers {
  444. err := sb.snapApplyHeader(snapCpy, header)
  445. if err != nil {
  446. return nil, err
  447. }
  448. }
  449. snapCpy.Number += uint64(len(headers))
  450. snapCpy.Hash = headers[len(headers)-1].Hash()
  451. return snapCpy, nil
  452. }
  453. func (sb *Backend) snapApplyHeader(snap *Snapshot, header *types.Header) error {
  454. logger := sb.snapLogger(snap).New("header.number", header.Number.Uint64(), "header.hash", header.Hash().String())
  455. logger.Trace("BFT: apply header to voting snapshot")
  456. // Remove any votes on checkpoint blocks
  457. number := header.Number.Uint64()
  458. if number%snap.Epoch == 0 {
  459. snap.Votes = nil
  460. snap.Tally = make(map[common.Address]Tally)
  461. }
  462. // Resolve the authorization key and check against validators
  463. validator, err := sb.EngineForBlockNumber(header.Number).Author(header)
  464. if err != nil {
  465. logger.Error("BFT: invalid header author", "err", err)
  466. return err
  467. }
  468. logger = logger.New("header.author", validator)
  469. if _, v := snap.ValSet.GetByAddress(validator); v == nil {
  470. logger.Error("BFT: header author is not a validator", "Validators", snap.ValSet, "Author", validator)
  471. return istanbulcommon.ErrUnauthorized
  472. }
  473. // Read vote from header
  474. candidate, authorize, err := sb.EngineForBlockNumber(header.Number).ReadVote(header)
  475. if err != nil {
  476. logger.Error("BFT: invalid header vote", "err", err)
  477. return err
  478. }
  479. logger = logger.New("candidate", candidate.String(), "authorize", authorize)
  480. // Header authorized, discard any previous votes from the validator
  481. for i, vote := range snap.Votes {
  482. if vote.Validator == validator && vote.Address == candidate {
  483. logger.Trace("BFT: discard previous vote from tally", "old.authorize", vote.Authorize)
  484. // Uncast the vote from the cached tally
  485. snap.uncast(vote.Address, vote.Authorize)
  486. // Uncast the vote from the chronological list
  487. snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
  488. break // only one vote allowed
  489. }
  490. }
  491. logger.Debug("BFT: add vote to tally")
  492. if snap.cast(candidate, authorize) {
  493. snap.Votes = append(snap.Votes, &Vote{
  494. Validator: validator,
  495. Block: number,
  496. Address: candidate,
  497. Authorize: authorize,
  498. })
  499. }
  500. // If the vote passed, update the list of validators
  501. if tally := snap.Tally[candidate]; tally.Votes > snap.ValSet.Size()/2 {
  502. if tally.Authorize {
  503. logger.Info("BFT: reached majority to add validator")
  504. snap.ValSet.AddValidator(candidate)
  505. } else {
  506. logger.Info("BFT: reached majority to remove validator")
  507. snap.ValSet.RemoveValidator(candidate)
  508. // Discard any previous votes the deauthorized validator cast
  509. for i := 0; i < len(snap.Votes); i++ {
  510. if snap.Votes[i].Validator == candidate {
  511. // Uncast the vote from the cached tally
  512. snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize)
  513. // Uncast the vote from the chronological list
  514. snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
  515. i--
  516. }
  517. }
  518. }
  519. // Discard any previous votes around the just changed account
  520. for i := 0; i < len(snap.Votes); i++ {
  521. if snap.Votes[i].Address == candidate {
  522. snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
  523. i--
  524. }
  525. }
  526. delete(snap.Tally, candidate)
  527. }
  528. return nil
  529. }