Problem: The way end_block checks for concluded elections assumes there is only one type of election (so we can't conclude an upsert-validator and a migration at the same height)

Solution: Re-engineered the code in `Elections` that checks for `approved_elections` to check all election types simultaneously, then return concluded elections sorted by election type
This commit is contained in:
z-bowen 2018-09-12 18:47:45 +02:00
parent af36843788
commit 7803e6320f
2 changed files with 23 additions and 16 deletions

View File

@ -20,13 +20,13 @@ from abci.types_pb2 import (
)
from bigchaindb import BigchainDB
from bigchaindb.elections.election import Election
from bigchaindb.version import __tm_supported_versions__
from bigchaindb.utils import tendermint_version_is_compatible
from bigchaindb.tendermint_utils import (decode_transaction,
calculate_hash)
from bigchaindb.lib import Block, PreCommitState
from bigchaindb.backend.query import PRE_COMMIT_ID
from bigchaindb.upsert_validator import ValidatorElection
import bigchaindb.upsert_validator.validator_utils as vutils
from bigchaindb.events import EventTypes, Event
@ -219,15 +219,14 @@ class App(BaseApplication):
else:
self.block_txn_hash = block['app_hash']
# Check if the current block concluded any validator elections and
# update the locally tracked validator set
validator_update = ValidatorElection.approved_update(self.bigchaindb,
self.new_height,
self.block_transactions)
# Check the current block to see if any elections have concluded.
concluded_elections = Election.approved_elections(self.bigchaindb,
self.new_height,
self.block_transactions)
validator_update = concluded_elections.get('VALIDATOR_ELECTION')
update = [validator_update] if validator_update else []
# Store pre-commit state to recover in case there is a crash
# during `commit`
# Store pre-commit state to recover in case there is a crash during `commit`
pre_commit_state = PreCommitState(commit_id=PRE_COMMIT_ID,
height=self.new_height,
transactions=self.block_txn_ids)

View File

@ -242,24 +242,32 @@ class Election(Transaction):
return response
@classmethod
def approved_update(cls, bigchain, new_height, txns):
votes = {}
def approved_elections(cls, bigchain, new_height, txns):
elections = {}
for txn in txns:
if not isinstance(txn, Vote):
continue
election_id = txn.asset['id']
election_votes = votes.get(election_id, [])
e = bigchain.get_transaction(election_id)
election_operation = e.OPERATION
election_operation_votes = elections.get(election_operation, {})
# Once we conclude an election of a given type, we stop looking at that election class
if type(election_operation_votes) != dict:
continue
election_votes = election_operation_votes.get(election_id, [])
election_votes.append(txn)
votes[election_id] = election_votes
election_operation_votes[election_id] = election_votes
elections[election_operation] = election_operation_votes
election = cls.has_concluded(bigchain, election_id, election_votes, new_height)
# Once an election concludes any other conclusion for the same
# or any other election is invalidated
if election:
# Once we conclude an election, we store the result in the db
cls.store_election_results(bigchain, election, new_height)
return cls.on_approval(bigchain, election, new_height)
return None
# And keep the transaction filed under the election_type
elections[election_operation] = election.on_approval(bigchain, election, new_height)
approved_elections = {k: v for (k, v) in elections.items() if type(v) != dict}
return approved_elections
@classmethod
def on_approval(cls, bigchain, election, new_height):