From d2e71eaebc1cd91364180a77ad53d8ab1360f07c Mon Sep 17 00:00:00 2001 From: Vanshdeep Singh Date: Wed, 15 Aug 2018 11:05:08 +0200 Subject: [PATCH] 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 --- bigchaindb/lib.py | 15 ------- .../upsert_validator/validator_election.py | 10 ++--- tests/commands/test_commands.py | 2 +- tests/upsert_validator/conftest.py | 2 +- .../test_validator_election_vote.py | 40 +++++++------------ 5 files changed, 21 insertions(+), 48 deletions(-) diff --git a/bigchaindb/lib.py b/bigchaindb/lib.py index 3577f523..17562428 100644 --- a/bigchaindb/lib.py +++ b/bigchaindb/lib.py @@ -442,21 +442,6 @@ class BigchainDB(object): validators = result['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): return backend.query.delete_validator_update(self.connection) diff --git a/bigchaindb/upsert_validator/validator_election.py b/bigchaindb/upsert_validator/validator_election.py index 89981ea3..fd38631a 100644 --- a/bigchaindb/upsert_validator/validator_election.py +++ b/bigchaindb/upsert_validator/validator_election.py @@ -33,13 +33,13 @@ class ValidatorElection(Transaction): super().__init__(operation, asset, inputs, outputs, metadata, version, hash_id) @classmethod - def current_validators(cls, bigchain): + def current_validators(cls, bigchain, height=None): """Return a dictionary of validators with key as `public_key` and value as the `voting_power` """ validators = {} - for validator in bigchain.get_validators(): + for validator in bigchain.get_validators(height): # NOTE: we assume that Tendermint encodes public key in base64 public_key = public_key_from_ed25519_key(key_from_base64(validator['pub_key']['data'])) validators[public_key] = validator['voting_power'] @@ -183,7 +183,7 @@ class ValidatorElection(Transaction): return self.count_votes(election_pk, txns) @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 NOTE: * 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) votes_commited = election.get_commited_votes(bigchain, election_pk) 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): total_votes = sum(current_validators.values()) @@ -216,7 +216,7 @@ class ValidatorElection(Transaction): election_votes.append(txn) 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 # or any other election is invalidated if election: diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index d84c55a5..191e1273 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -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): from bigchaindb.commands.bigchaindb import run_upsert_validator_new - def mock_get(): + def mock_get(height): return [ {'pub_key': {'data': 'zL/DasvKulXZzhSNFwx4cLRXKkSM9GPK7Y0nZ4FEylM=', 'type': 'tendermint/PubKeyEd25519'}, diff --git a/tests/upsert_validator/conftest.py b/tests/upsert_validator/conftest.py index d6293b14..7d1f5cf1 100644 --- a/tests/upsert_validator/conftest.py +++ b/tests/upsert_validator/conftest.py @@ -22,7 +22,7 @@ def new_validator(): def mock_get_validators(network_validators): - def validator_set(): + def validator_set(height): validators = [] for public_key, power in network_validators.items(): validators.append({ diff --git a/tests/upsert_validator/test_validator_election_vote.py b/tests/upsert_validator/test_validator_election_vote.py index 64a7b8f4..c4cdc29d 100644 --- a/tests/upsert_validator/test_validator_election_vote.py +++ b/tests/upsert_validator/test_validator_election_vote.py @@ -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]) -@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 def test_upsert_validator(b, node_key, node_keys, new_validator, ed25519_node_keys): 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_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]) - 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') + 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