diff --git a/bigchaindb/common/exceptions.py b/bigchaindb/common/exceptions.py index ec4c9702..869aa2c7 100644 --- a/bigchaindb/common/exceptions.py +++ b/bigchaindb/common/exceptions.py @@ -19,10 +19,6 @@ class StartupError(BigchainDBError): """Raised when there is an error starting up the system""" -class GenesisBlockAlreadyExistsError(BigchainDBError): - """Raised when trying to create the already existing genesis block""" - - class CyclicBlockchainError(BigchainDBError): """Raised when there is a cycle in the blockchain""" @@ -110,3 +106,7 @@ class DuplicateTransaction(ValidationError): class ThresholdTooDeep(ValidationError): """Raised if threshold condition is too deep""" + + +class GenesisBlockAlreadyExistsError(ValidationError): + """Raised when trying to create the already existing genesis block""" diff --git a/bigchaindb/pipelines/block.py b/bigchaindb/pipelines/block.py index 945d369c..bf4af2e4 100644 --- a/bigchaindb/pipelines/block.py +++ b/bigchaindb/pipelines/block.py @@ -13,7 +13,8 @@ import bigchaindb from bigchaindb import backend from bigchaindb.backend.changefeed import ChangeFeed from bigchaindb.models import Transaction -from bigchaindb.common.exceptions import ValidationError +from bigchaindb.common.exceptions import (ValidationError, + GenesisBlockAlreadyExistsError) from bigchaindb import Bigchain @@ -73,6 +74,14 @@ class BlockPipeline: # If transaction is not valid it should not be included try: + # Do not allow an externally submitted GENESIS transaction. + # A simple check is enough as a pipeline is started only after the + # creation of GENESIS block, or after the verification of a GENESIS + # block. Voting will fail at a later stage if the GENESIS block is + # absent. + if tx.operation == Transaction.GENESIS: + raise GenesisBlockAlreadyExistsError('Duplicate GENESIS transaction') + tx.validate(self.bigchain) return tx except ValidationError as e: diff --git a/tests/conftest.py b/tests/conftest.py index 46f4dc37..2ea8d063 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -506,3 +506,12 @@ def wsserver_port(wsserver_config): @pytest.fixture def wsserver_base_url(wsserver_scheme, wsserver_host, wsserver_port): return '{}://{}:{}'.format(wsserver_scheme, wsserver_host, wsserver_port) + + +@pytest.fixture +def genesis_tx(b, user_pk): + from bigchaindb.models import Transaction + tx = Transaction.create([b.me], [([user_pk], 1)]) + tx.operation = Transaction.GENESIS + genesis_tx = tx.sign([b.me_private]) + return genesis_tx diff --git a/tests/pipelines/test_block_creation.py b/tests/pipelines/test_block_creation.py index 27efc65d..e472df3d 100644 --- a/tests/pipelines/test_block_creation.py +++ b/tests/pipelines/test_block_creation.py @@ -226,3 +226,14 @@ def test_block_snowflake(create_tx, signed_transfer_tx): snowflake.send(signed_transfer_tx) snowflake.send(create_tx) assert snowflake.send(None) == [create_tx, signed_transfer_tx] + + +@pytest.mark.bdb +@pytest.mark.genesis +def test_duplicate_genesis_transaction(b, genesis_block, genesis_tx): + # Try to create a duplicate GENESIS transaction + # Expect None as it will be rejected during validation + + from bigchaindb.pipelines import block + block_maker = block.BlockPipeline() + assert block_maker.validate_tx(genesis_tx.to_dict()) is None