diff --git a/bigchaindb/__init__.py b/bigchaindb/__init__.py index 2e4ef8b5..e1762f5d 100644 --- a/bigchaindb/__init__.py +++ b/bigchaindb/__init__.py @@ -94,9 +94,9 @@ _config = copy.deepcopy(config) from bigchaindb.common.transaction import Transaction # noqa from bigchaindb import models # noqa from bigchaindb.upsert_validator import ValidatorElection # noqa -from bigchaindb.common.vote import Vote # noqa +from bigchaindb.elections.vote import Vote # noqa Transaction.register_type(Transaction.CREATE, models.Transaction) Transaction.register_type(Transaction.TRANSFER, models.Transaction) Transaction.register_type(ValidatorElection.OPERATION, ValidatorElection) -Transaction.register_type(Vote.VOTE, Vote) +Transaction.register_type(Vote.OPERATION, Vote) diff --git a/bigchaindb/backend/localmongodb/query.py b/bigchaindb/backend/localmongodb/query.py index 89a1e33f..4c016f18 100644 --- a/bigchaindb/backend/localmongodb/query.py +++ b/bigchaindb/backend/localmongodb/query.py @@ -282,7 +282,7 @@ def store_validator_set(conn, validators_update): @register_query(LocalMongoDBConnection) -def store_election(conn, election): +def store_election_results(conn, election): height = election['height'] return conn.run( conn.collection('elections').replace_one( diff --git a/bigchaindb/backend/localmongodb/schema.py b/bigchaindb/backend/localmongodb/schema.py index ce38b551..22899a86 100644 --- a/bigchaindb/backend/localmongodb/schema.py +++ b/bigchaindb/backend/localmongodb/schema.py @@ -143,6 +143,9 @@ def create_abci_chains_indexes(conn, dbname): conn.conn[dbname]['abci_chains'].create_index('height', name='height', unique=True,) + conn.conn[dbname]['abci_chains'].create_index('chain_id', + name='chain_id', + unique=True) def create_elections_secondary_index(conn, dbname): @@ -151,8 +154,3 @@ def create_elections_secondary_index(conn, dbname): conn.conn[dbname]['elections'].create_index('election_id', name='election_id', unique=True,) - - logger.info('Create `abci_chains.chain_id` secondary index.') - conn.conn[dbname]['abci_chains'].create_index('chain_id', - name='chain_id', - unique=True) diff --git a/bigchaindb/backend/query.py b/bigchaindb/backend/query.py index 30daf009..2da36bd5 100644 --- a/bigchaindb/backend/query.py +++ b/bigchaindb/backend/query.py @@ -352,7 +352,7 @@ def store_validator_set(conn, validator_update): @singledispatch -def store_election(conn, validator_update): +def store_election_results(conn, validator_update): """Store election results""" raise NotImplementedError diff --git a/bigchaindb/commands/bigchaindb.py b/bigchaindb/commands/bigchaindb.py index d4678ca2..2c6fd5f7 100644 --- a/bigchaindb/commands/bigchaindb.py +++ b/bigchaindb/commands/bigchaindb.py @@ -17,7 +17,7 @@ from bigchaindb.utils import load_node_key from bigchaindb.common.exceptions import (DatabaseAlreadyExists, DatabaseDoesNotExist, ValidationError) -from bigchaindb.common.vote import Vote +from bigchaindb.elections.vote import Vote import bigchaindb from bigchaindb import (backend, ValidatorElection, BigchainDB) diff --git a/bigchaindb/common/schema/transaction_vote_v2.0.yaml b/bigchaindb/common/schema/transaction_vote_v2.0.yaml index b2cf8d81..5e7c4763 100644 --- a/bigchaindb/common/schema/transaction_vote_v2.0.yaml +++ b/bigchaindb/common/schema/transaction_vote_v2.0.yaml @@ -12,7 +12,7 @@ required: properties: operation: type: string - value: "OPERATION" + value: "VOTE" outputs: type: array items: diff --git a/bigchaindb/core.py b/bigchaindb/core.py index 9c23b5b8..6991180a 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -209,9 +209,10 @@ class App(BaseApplication): # Check if the current block concluded any validator elections and # update the locally tracked validator set - validator_updates = ValidatorElection.is_approved(self.bigchaindb, - self.new_height, - self.block_transactions) + validator_update = ValidatorElection.approved_update(self.bigchaindb, + self.new_height, + self.block_transactions) + update = [validator_update] if validator_update else [] # Store pre-commit state to recover in case there is a crash # during `commit` @@ -220,7 +221,7 @@ class App(BaseApplication): transactions=self.block_txn_ids) logger.debug('Updating PreCommitState: %s', self.new_height) self.bigchaindb.store_pre_commit_state(pre_commit_state._asdict()) - return ResponseEndBlock(validator_updates=validator_updates) + return ResponseEndBlock(validator_updates=update) def commit(self): """Store the new height and along with block hash.""" diff --git a/bigchaindb/elections/__init__.py b/bigchaindb/elections/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bigchaindb/common/election.py b/bigchaindb/elections/election.py similarity index 94% rename from bigchaindb/common/election.py rename to bigchaindb/elections/election.py index d9e907f9..fd5c2269 100644 --- a/bigchaindb/common/election.py +++ b/bigchaindb/elections/election.py @@ -5,7 +5,7 @@ import base58 from bigchaindb import backend -from bigchaindb.common.vote import Vote +from bigchaindb.elections.vote import Vote from bigchaindb.common.exceptions import (InvalidSignature, MultipleInputsError, InvalidProposer, @@ -26,6 +26,8 @@ class Election(Transaction): OPERATION = None # the model for votes issued by the election VOTE_TYPE = Vote + # Custom validation schema + TX_SCHEMA_CUSTOM = None # Election Statuses: ONGOING = 'ongoing' CONCLUDED = 'concluded' @@ -145,6 +147,8 @@ class Election(Transaction): cls.validate_id(tx) _validate_schema(TX_SCHEMA_COMMON, tx) _validate_schema(TX_SCHEMA_CREATE, tx) + if cls.TX_SCHEMA_CUSTOM: + _validate_schema(cls.TX_SCHEMA_CUSTOM, tx) @classmethod def create(cls, tx_signers, recipients, metadata=None, asset=None): @@ -162,7 +166,7 @@ class Election(Transaction): def count_votes(cls, election_pk, transactions, getter=getattr): votes = 0 for txn in transactions: - if getter(txn, 'operation') == cls.VOTE_TYPE.VOTE: + if getter(txn, 'operation') == cls.VOTE_TYPE.OPERATION: for output in getter(txn, 'outputs'): # NOTE: We enforce that a valid vote to election id will have only # election_pk in the output public keys, including any other public key @@ -221,11 +225,11 @@ class Election(Transaction): return result @classmethod - def store_election(cls, bigchain, election, height): - bigchain.store_election(height, election) + def store_election_results(cls, bigchain, election, height): + bigchain.store_election_results(height, election) @classmethod - def is_approved(cls, bigchain, new_height, txns): + def approved_update(cls, bigchain, new_height, txns): votes = {} for txn in txns: if not isinstance(txn, cls.VOTE_TYPE): @@ -240,9 +244,9 @@ class Election(Transaction): # Once an election concludes any other conclusion for the same # or any other election is invalidated if election: - cls.store_election(bigchain, election, new_height) + cls.store_election_results(bigchain, election, new_height) return cls.on_approval(bigchain, election, new_height) - return [] + return None @classmethod def on_approval(cls, bigchain, election, new_height): diff --git a/bigchaindb/common/vote.py b/bigchaindb/elections/vote.py similarity index 88% rename from bigchaindb/common/vote.py rename to bigchaindb/elections/vote.py index 67ed3444..6951da3c 100644 --- a/bigchaindb/common/vote.py +++ b/bigchaindb/elections/vote.py @@ -11,11 +11,13 @@ from bigchaindb.common.schema import (_validate_schema, class Vote(Transaction): - VOTE = 'VOTE' + OPERATION = 'VOTE' # NOTE: This class inherits TRANSFER txn type. The `TRANSFER` property is # overriden to re-use methods from parent class - TRANSFER = VOTE - ALLOWED_OPERATIONS = (VOTE,) + TRANSFER = OPERATION + ALLOWED_OPERATIONS = (OPERATION,) + # Custom validation schema + TX_SCHEMA_CUSTOM = TX_SCHEMA_VOTE def validate(self, bigchain, current_transactions=[]): """Validate election vote transaction @@ -39,7 +41,7 @@ class Vote(Transaction): @classmethod def generate(cls, inputs, recipients, election_id, metadata=None): (inputs, outputs) = cls.validate_transfer(inputs, recipients, election_id, metadata) - election_vote = cls(cls.VOTE, {'id': election_id}, inputs, outputs, metadata) + election_vote = cls(cls.OPERATION, {'id': election_id}, inputs, outputs, metadata) cls.validate_schema(election_vote.to_dict(), skip_id=True) return election_vote @@ -52,7 +54,7 @@ class Vote(Transaction): cls.validate_id(tx) _validate_schema(TX_SCHEMA_COMMON, tx) _validate_schema(TX_SCHEMA_TRANSFER, tx) - _validate_schema(TX_SCHEMA_VOTE, tx) + _validate_schema(cls.TX_SCHEMA_CUSTOM, tx) @classmethod def create(cls, tx_signers, recipients, metadata=None, asset=None): diff --git a/bigchaindb/lib.py b/bigchaindb/lib.py index d7034bba..7c6b4b39 100644 --- a/bigchaindb/lib.py +++ b/bigchaindb/lib.py @@ -480,13 +480,13 @@ class BigchainDB(object): self.store_abci_chain(block['height'] + 1, new_chain_id, False) - def store_election(self, height, election): + def store_election_results(self, height, election): """Store election results :param height: the block height at which the election concluded :param election: a concluded election """ - return backend.query.store_election(self.connection, {'height': height, - 'election_id': election.id}) + return backend.query.store_election_results(self.connection, {'height': height, + 'election_id': election.id}) Block = namedtuple('Block', ('app_hash', 'height', 'transactions')) diff --git a/bigchaindb/upsert_validator/validator_election.py b/bigchaindb/upsert_validator/validator_election.py index 97e7a1ab..df2d48c7 100644 --- a/bigchaindb/upsert_validator/validator_election.py +++ b/bigchaindb/upsert_validator/validator_election.py @@ -3,9 +3,8 @@ # Code is Apache-2.0 and docs are CC-BY-4.0 from bigchaindb.common.exceptions import InvalidPowerChange -from bigchaindb.common.election import Election -from bigchaindb.common.schema import (_validate_schema, - TX_SCHEMA_VALIDATOR_ELECTION) +from bigchaindb.elections.election import Election +from bigchaindb.common.schema import (TX_SCHEMA_VALIDATOR_ELECTION) from .validator_utils import (new_validator_set, encode_validator) @@ -16,6 +15,7 @@ class ValidatorElection(Election): # by renaming CREATE to VALIDATOR_ELECTION CREATE = OPERATION ALLOWED_OPERATIONS = (OPERATION,) + TX_SCHEMA_CUSTOM = TX_SCHEMA_VALIDATOR_ELECTION def validate(self, bigchain, current_transactions=[]): """For more details refer BEP-21: https://github.com/bigchaindb/BEPs/tree/master/21 @@ -31,16 +31,6 @@ class ValidatorElection(Election): return self - @classmethod - def validate_schema(cls, tx, skip_id=False): - """Validate the validator election transaction. Since `VALIDATOR_ELECTION` extends `ELECTION` - transaction, all the validations for `ELECTION` transaction are covered by `super` - """ - - super(ValidatorElection, cls).validate_schema(tx, skip_id=skip_id) - - _validate_schema(TX_SCHEMA_VALIDATOR_ELECTION, tx) - @classmethod def on_approval(cls, bigchain, election, new_height): # The new validator set comes into effect from height = new_height+1 @@ -51,4 +41,4 @@ class ValidatorElection(Election): updated_validator_set = [v for v in updated_validator_set if v['voting_power'] > 0] bigchain.store_validator_set(new_height+1, updated_validator_set, election.id) - return [encode_validator(election.asset['data'])] + return encode_validator(election.asset['data']) diff --git a/tests/upsert_validator/conftest.py b/tests/upsert_validator/conftest.py index 30f1f300..6a55227f 100644 --- a/tests/upsert_validator/conftest.py +++ b/tests/upsert_validator/conftest.py @@ -65,7 +65,7 @@ def concluded_election(b, ongoing_election, ed25519_node_keys): election_result = {'height': 2, 'election_id': ongoing_election.id} - query.store_election(b.connection, election_result) + query.store_election_results(b.connection, election_result) return ongoing_election diff --git a/tests/upsert_validator/test_upsert_validator_vote.py b/tests/upsert_validator/test_upsert_validator_vote.py index 6cea8ac6..c85d423c 100644 --- a/tests/upsert_validator/test_upsert_validator_vote.py +++ b/tests/upsert_validator/test_upsert_validator_vote.py @@ -10,7 +10,7 @@ from bigchaindb.upsert_validator import ValidatorElection from bigchaindb.common.exceptions import AmountError from bigchaindb.common.crypto import generate_key_pair from bigchaindb.common.exceptions import ValidationError -from bigchaindb.common.vote import Vote +from bigchaindb.elections.vote import Vote from tests.utils import generate_block pytestmark = [pytest.mark.execute] @@ -300,19 +300,22 @@ def test_get_validator_update(b, node_keys, node_key, ed25519_node_keys): assert not ValidatorElection.has_concluded(b, election.id, [tx_vote0, tx_vote1]) assert ValidatorElection.has_concluded(b, election.id, [tx_vote0, tx_vote1, tx_vote2]) - assert ValidatorElection.is_approved(b, 4, [tx_vote0]) == [] - assert ValidatorElection.is_approved(b, 4, [tx_vote0, tx_vote1]) == [] + assert not ValidatorElection.approved_update(b, 4, [tx_vote0]) + assert not ValidatorElection.approved_update(b, 4, [tx_vote0, tx_vote1]) - update = ValidatorElection.is_approved(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 + update = ValidatorElection.approved_update(b, 4, [tx_vote0, tx_vote1, tx_vote2]) + update_public_key = None + if update: + update_public_key = codecs.encode(update.pub_key.data, 'base64').decode().rstrip('\n') + assert update assert update_public_key == public_key64 b.store_bulk_transactions([tx_vote0, tx_vote1]) - update = ValidatorElection.is_approved(b, 4, [tx_vote2]) - update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n') - assert len(update) == 1 + update = ValidatorElection.approved_update(b, 4, [tx_vote2]) + if update: + update_public_key = codecs.encode(update.pub_key.data, 'base64').decode().rstrip('\n') + assert update assert update_public_key == public_key64 # remove validator @@ -333,9 +336,10 @@ def test_get_validator_update(b, node_keys, node_key, ed25519_node_keys): b.store_bulk_transactions([tx_vote0, tx_vote1]) - update = ValidatorElection.is_approved(b, 9, [tx_vote2]) - update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n') - assert len(update) == 1 + update = ValidatorElection.approved_update(b, 9, [tx_vote2]) + if update: + update_public_key = codecs.encode(update.pub_key.data, 'base64').decode().rstrip('\n') + assert update assert update_public_key == public_key64 # assert that the public key is not a part of the current validator set