diff --git a/bigchaindb/consensus.py b/bigchaindb/consensus.py index 593aea9f..1eafe7f5 100644 --- a/bigchaindb/consensus.py +++ b/bigchaindb/consensus.py @@ -163,7 +163,6 @@ class BaseConsensusRules(AbstractConsensusRules): return transaction - # TODO: Unsure if a bigchain parameter is really necessary here? @staticmethod def validate_block(bigchain, block): """Validate a block. @@ -185,6 +184,15 @@ class BaseConsensusRules(AbstractConsensusRules): if calculated_hash != block['id']: raise exceptions.InvalidHash() + # Check if the block was created by a federation node + if block['block']['node_pubkey'] not in (bigchain.federation_nodes + [bigchain.me]): + raise exceptions.OperationError('Only federation nodes can create blocks') + + # Check if block signature is valid + verifying_key = crypto.VerifyingKey(block['block']['node_pubkey']) + if not verifying_key.verify(util.serialize(block['block']), block['signature']): + raise exceptions.InvalidSignature('Invalid block signature') + return block @staticmethod diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index 07479944..fb7f3539 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -433,6 +433,37 @@ class TestBlockValidation(object): assert block == b.validate_block(block) assert b.is_valid_block(block) + def test_invalid_signature(self, b): + # create a valid block + block = b.create_block([]) + + # replace the block signature with an invalid one + block['signature'] = crypto.SigningKey(b.me_private).sign(b'wrongdata') + + # check that validate_block raises an InvalidSignature exception + with pytest.raises(exceptions.InvalidSignature): + b.validate_block(block) + + def test_invalid_node_pubkey(self, b): + # blocks can only be created by a federation node + # create a valid block + block = b.create_block([]) + + # create some temp keys + tmp_sk, tmp_vk = crypto.generate_key_pair() + + # change the block node_pubkey + block['block']['node_pubkey'] = tmp_vk + + # just to make sure lets re-hash the block and create a valid signature + # from a non federation node + block['id'] = crypto.hash_data(util.serialize(block['block'])) + block['signature'] = crypto.SigningKey(tmp_sk).sign(util.serialize(block['block'])) + + # check that validate_block raises an OperationError + with pytest.raises(exceptions.OperationError): + b.validate_block(block) + class TestBigchainVoter(object): def test_valid_block_voting(self, b):