Problem: ValidatorElection.conclude not dependent on height

Solution: Height is an important factor when concluding an election during
replay i.e. fetching the current validator set should be dependent on height
This commit is contained in:
Vanshdeep Singh 2018-08-15 11:05:08 +02:00
parent b2839668d6
commit d2e71eaebc
5 changed files with 21 additions and 48 deletions

View File

@ -442,21 +442,6 @@ class BigchainDB(object):
validators = result['validators'] validators = result['validators']
return validators return validators
def get_validator_update(self, txns):
votes = {}
for txn in txns:
if isinstance(txn, ValidatorElectionVote):
election_id = txn.asset['id']
election_votes = votes.get(election_id, [])
votes[election_id] = election_votes.append(txn)
election = ValidatorElection.conclude(self, election_id, election_votes)
# Once an election concludes any other conclusion for the same
# or any other election is invalidated
if election:
return [election.asset['data']]
return []
def delete_validator_update(self): def delete_validator_update(self):
return backend.query.delete_validator_update(self.connection) return backend.query.delete_validator_update(self.connection)

View File

@ -33,13 +33,13 @@ class ValidatorElection(Transaction):
super().__init__(operation, asset, inputs, outputs, metadata, version, hash_id) super().__init__(operation, asset, inputs, outputs, metadata, version, hash_id)
@classmethod @classmethod
def current_validators(cls, bigchain): def current_validators(cls, bigchain, height=None):
"""Return a dictionary of validators with key as `public_key` and """Return a dictionary of validators with key as `public_key` and
value as the `voting_power` value as the `voting_power`
""" """
validators = {} validators = {}
for validator in bigchain.get_validators(): for validator in bigchain.get_validators(height):
# NOTE: we assume that Tendermint encodes public key in base64 # NOTE: we assume that Tendermint encodes public key in base64
public_key = public_key_from_ed25519_key(key_from_base64(validator['pub_key']['data'])) public_key = public_key_from_ed25519_key(key_from_base64(validator['pub_key']['data']))
validators[public_key] = validator['voting_power'] validators[public_key] = validator['voting_power']
@ -183,7 +183,7 @@ class ValidatorElection(Transaction):
return self.count_votes(election_pk, txns) return self.count_votes(election_pk, txns)
@classmethod @classmethod
def conclude(cls, bigchain, txn_id, current_votes=[]): def conclude(cls, bigchain, txn_id, current_votes=[], height=None):
"""Check if the given election has concluded or not """Check if the given election has concluded or not
NOTE: NOTE:
* Election is concluded iff the current validator set is exactly equal * Election is concluded iff the current validator set is exactly equal
@ -196,7 +196,7 @@ class ValidatorElection(Transaction):
election_pk = election.to_public_key(election.id) election_pk = election.to_public_key(election.id)
votes_commited = election.get_commited_votes(bigchain, election_pk) votes_commited = election.get_commited_votes(bigchain, election_pk)
votes_current = election.count_votes(election_pk, current_votes) votes_current = election.count_votes(election_pk, current_votes)
current_validators = election.current_validators(bigchain) current_validators = election.current_validators(bigchain, height)
if election.is_same_topology(current_validators, election.outputs): if election.is_same_topology(current_validators, election.outputs):
total_votes = sum(current_validators.values()) total_votes = sum(current_validators.values())
@ -216,7 +216,7 @@ class ValidatorElection(Transaction):
election_votes.append(txn) election_votes.append(txn)
votes[election_id] = election_votes votes[election_id] = election_votes
election = cls.conclude(bigchain, election_id, election_votes) election = cls.conclude(bigchain, election_id, election_votes, new_height)
# Once an election concludes any other conclusion for the same # Once an election concludes any other conclusion for the same
# or any other election is invalidated # or any other election is invalidated
if election: if election:

View File

@ -384,7 +384,7 @@ def test_upsert_validator_new_with_tendermint(b, priv_validator_path, user_sk, m
def test_upsert_validator_new_without_tendermint(b, priv_validator_path, user_sk, monkeypatch): def test_upsert_validator_new_without_tendermint(b, priv_validator_path, user_sk, monkeypatch):
from bigchaindb.commands.bigchaindb import run_upsert_validator_new from bigchaindb.commands.bigchaindb import run_upsert_validator_new
def mock_get(): def mock_get(height):
return [ return [
{'pub_key': {'data': 'zL/DasvKulXZzhSNFwx4cLRXKkSM9GPK7Y0nZ4FEylM=', {'pub_key': {'data': 'zL/DasvKulXZzhSNFwx4cLRXKkSM9GPK7Y0nZ4FEylM=',
'type': 'tendermint/PubKeyEd25519'}, 'type': 'tendermint/PubKeyEd25519'},

View File

@ -22,7 +22,7 @@ def new_validator():
def mock_get_validators(network_validators): def mock_get_validators(network_validators):
def validator_set(): def validator_set(height):
validators = [] validators = []
for public_key, power in network_validators.items(): for public_key, power in network_validators.items():
validators.append({ validators.append({

View File

@ -194,30 +194,6 @@ def test_valid_election_conclude(b_mock, valid_election, ed25519_node_keys):
assert not ValidatorElection.conclude(b_mock, valid_election.id, [tx_vote3]) assert not ValidatorElection.conclude(b_mock, valid_election.id, [tx_vote3])
@pytest.mark.tendermint
@pytest.mark.bdb
def test_get_validator_update_conclude(b_mock, valid_election, ed25519_node_keys):
# store election
b_mock.store_bulk_transactions([valid_election])
# Node 0: cast vote
tx_vote0 = gen_vote(valid_election, 0, ed25519_node_keys)
assert b_mock.get_validator_update([tx_vote0]) == []
b_mock.store_bulk_transactions([tx_vote0])
tx_vote1 = gen_vote(valid_election, 1, ed25519_node_keys)
assert b_mock.get_validator_update([tx_vote1]) == []
b_mock.store_bulk_transactions([tx_vote1])
# Election can only be concluded once
tx_vote2 = gen_vote(valid_election, 2, ed25519_node_keys)
assert b_mock.get_validator_update([tx_vote2]) == [valid_election.asset['data']]
b_mock.store_bulk_transactions([tx_vote2])
tx_vote3 = gen_vote(valid_election, 3, ed25519_node_keys)
assert b_mock.get_validator_update([tx_vote3]) == []
@pytest.mark.abci @pytest.mark.abci
def test_upsert_validator(b, node_key, node_keys, new_validator, ed25519_node_keys): def test_upsert_validator(b, node_key, node_keys, new_validator, ed25519_node_keys):
import time import time
@ -292,12 +268,24 @@ def test_get_validator_update(b, node_keys, node_key, ed25519_node_keys):
tx_vote1 = gen_vote(election, 1, ed25519_node_keys) tx_vote1 = gen_vote(election, 1, ed25519_node_keys)
tx_vote2 = gen_vote(election, 2, ed25519_node_keys) tx_vote2 = gen_vote(election, 2, ed25519_node_keys)
assert not ValidatorElection.conclude(b, election.id, [tx_vote0])
assert not ValidatorElection.conclude(b, election.id, [tx_vote0, tx_vote1])
assert ValidatorElection.conclude(b, election.id, [tx_vote0, tx_vote1, tx_vote2]) assert ValidatorElection.conclude(b, election.id, [tx_vote0, tx_vote1, tx_vote2])
update = ValidatorElection.get_validator_update(b, 4, [tx_vote0, tx_vote1, tx_vote2]) assert ValidatorElection.get_validator_update(b, 4, [tx_vote0]) == []
assert ValidatorElection.get_validator_update(b, 4, [tx_vote0, tx_vote1]) == []
assert len(update) == 1 update = ValidatorElection.get_validator_update(b, 4, [tx_vote0, tx_vote1, tx_vote2])
update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n') update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n')
assert len(update) == 1
assert update_public_key == public_key64
b.store_bulk_transactions([tx_vote0, tx_vote1])
update = ValidatorElection.get_validator_update(b, 4, [tx_vote2])
print('update', update)
update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n')
assert len(update) == 1
assert update_public_key == public_key64 assert update_public_key == public_key64