diff --git a/Dockerfile-all-in-one b/Dockerfile-all-in-one index 6c50d971..0ccde6bb 100644 --- a/Dockerfile-all-in-one +++ b/Dockerfile-all-in-one @@ -21,7 +21,7 @@ RUN apk --update add sudo bash \ RUN apk --update add mongodb monit # Install Tendermint -RUN wget https://github.com/tendermint/tendermint/releases/download/v${TM_VERSION}-autodraft/tendermint_${TM_VERSION}_linux_amd64.zip \ +RUN wget https://github.com/tendermint/tendermint/releases/download/v${TM_VERSION}/tendermint_${TM_VERSION}_linux_amd64.zip \ && unzip tendermint_${TM_VERSION}_linux_amd64.zip \ && mv tendermint /usr/local/bin/ \ && rm tendermint_${TM_VERSION}_linux_amd64.zip diff --git a/bigchaindb/common/exceptions.py b/bigchaindb/common/exceptions.py index 0c145d13..357e998d 100644 --- a/bigchaindb/common/exceptions.py +++ b/bigchaindb/common/exceptions.py @@ -96,10 +96,6 @@ class ThresholdTooDeep(ValidationError): """Raised if threshold condition is too deep""" -class GenesisBlockAlreadyExistsError(ValidationError): - """Raised when trying to create the already existing genesis block""" - - class MultipleValidatorOperationError(ValidationError): """Raised when a validator update pending but new request is submited""" diff --git a/bigchaindb/exceptions.py b/bigchaindb/exceptions.py index 336ce231..bb5d7c01 100644 --- a/bigchaindb/exceptions.py +++ b/bigchaindb/exceptions.py @@ -4,7 +4,3 @@ class BigchainDBError(Exception): class CriticalDoubleSpend(BigchainDBError): """Data integrity error that requires attention""" - - -class CriticalDoubleInclusion(BigchainDBError): - """Data integrity error that requires attention""" diff --git a/docs/contributing/source/dev-setup-coding-and-contribution-process/run-node-as-processes.md b/docs/contributing/source/dev-setup-coding-and-contribution-process/run-node-as-processes.md index 4c98578a..d39b2b9f 100644 --- a/docs/contributing/source/dev-setup-coding-and-contribution-process/run-node-as-processes.md +++ b/docs/contributing/source/dev-setup-coding-and-contribution-process/run-node-as-processes.md @@ -23,7 +23,7 @@ Find [the version number of the latest Tendermint release](https://github.com/te ```bash $ sudo apt install -y unzip -$ wget https://github.com/tendermint/tendermint/releases/download/v0.22.8-autodraft/tendermint_0.22.8_linux_amd64.zip +$ wget https://github.com/tendermint/tendermint/releases/download/v0.22.8/tendermint_0.22.8_linux_amd64.zip $ unzip tendermint_0.22.8_linux_amd64.zip $ rm tendermint_0.22.8_linux_amd64.zip $ sudo mv tendermint /usr/local/bin diff --git a/docs/server/source/drivers-clients/index.rst b/docs/server/source/drivers-clients/index.rst index 3cf4d0fa..79954971 100644 --- a/docs/server/source/drivers-clients/index.rst +++ b/docs/server/source/drivers-clients/index.rst @@ -11,10 +11,11 @@ Libraries and Tools Maintained by the BigchainDB Team Community-Driven Libraries and Tools ------------------------------------ -.. note:: +.. warning:: Some of these projects are a work in progress, but may still be useful. + Others might not work with the latest version of BigchainDB. * `Haskell transaction builder `_ * `Go driver `_ diff --git a/tests/conftest.py b/tests/conftest.py index 7cb33b14..e08a3886 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -330,17 +330,19 @@ def _get_height(b): def inputs(user_pk, b, alice): from bigchaindb.models import Transaction # create blocks with transactions for `USER` to spend - for block in range(4): + for height in range(1, 4): transactions = [ Transaction.create( [alice_pubkey(alice)], [([user_pk], 1)], metadata={'msg': random.random()}, - ).sign([alice_privkey(alice)]).to_dict() + ).sign([alice_privkey(alice)]) for _ in range(10) ] - block = Block(app_hash='', height=_get_height(b), transactions=transactions) + tx_ids = [tx.id for tx in transactions] + block = Block(app_hash='hash'+str(height), height=height, transactions=tx_ids) b.store_block(block._asdict()) + b.store_bulk_transactions(transactions) @pytest.fixture diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index 2a8ace6a..88d7d445 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -7,196 +7,74 @@ from base58 import b58decode pytestmark = pytest.mark.bdb -@pytest.mark.skipif(reason='Some tests throw a ResourceWarning that might result in some weird ' - 'exceptions while running the tests. The problem seems to *not* ' - 'interfere with the correctness of the tests. ') -def test_remove_unclosed_sockets(): - pass - - class TestBigchainApi(object): - @pytest.mark.genesis - def test_get_last_voted_block_cyclic_blockchain(self, b, monkeypatch, alice): - from bigchaindb.common.crypto import PrivateKey - from bigchaindb.common.exceptions import CyclicBlockchainError - from bigchaindb.common.utils import serialize - from bigchaindb.models import Transaction - - tx = Transaction.create([alice.public_key], [([alice.public_key], 1)]) - tx = tx.sign([alice.private_key]) - monkeypatch.setattr('time.time', lambda: 1) - block1 = b.create_block([tx]) - b.write_block(block1) - - # Manipulate vote to create a cyclic Blockchain - vote = b.vote(block1.id, b.get_last_voted_block().id, True) - vote['vote']['previous_block'] = block1.id - vote_data = serialize(vote['vote']) - vote['signature'] = PrivateKey(alice.private_key).sign(vote_data.encode()) - b.write_vote(vote) - - with pytest.raises(CyclicBlockchainError): - b.get_last_voted_block() - - @pytest.mark.genesis - def test_try_voting_while_constructing_cyclic_blockchain(self, b, - monkeypatch, alice): - from bigchaindb.common.exceptions import CyclicBlockchainError - from bigchaindb.models import Transaction - - tx = Transaction.create([alice.public_key], [([alice.public_key], 1)]) - tx = tx.sign([alice.private_key]) - block1 = b.create_block([tx]) - - # We can simply submit twice the same block id and check if `Bigchain` - # throws - with pytest.raises(CyclicBlockchainError): - b.vote(block1.id, block1.id, True) - - @pytest.mark.genesis - def test_has_previous_vote_when_already_voted(self, b, monkeypatch, alice): + @pytest.mark.tendermint + def test_get_spent_with_double_inclusion_detected(self, b, alice): from bigchaindb.models import Transaction + from bigchaindb.backend.exceptions import OperationError tx = Transaction.create([alice.public_key], [([alice.public_key], 1)]) tx = tx.sign([alice.private_key]) - monkeypatch.setattr('time.time', lambda: 1) - block = b.create_block([tx]) - b.write_block(block) + b.store_bulk_transactions([tx]) - assert b.has_previous_vote(block.id) is False - - vote = b.vote(block.id, b.get_last_voted_block().id, True) - b.write_vote(vote) - - assert b.has_previous_vote(block.id) is True - - @pytest.mark.genesis - def test_get_spent_with_double_inclusion_detected(self, b, monkeypatch, alice): - from bigchaindb.exceptions import CriticalDoubleInclusion - from bigchaindb.models import Transaction - - tx = Transaction.create([alice.public_key], [([alice.public_key], 1)]) - tx = tx.sign([alice.private_key]) - - monkeypatch.setattr('time.time', lambda: 1000000000) - block1 = b.create_block([tx]) - b.write_block(block1) - - monkeypatch.setattr('time.time', lambda: 1000000020) transfer_tx = Transaction.transfer(tx.to_inputs(), [([alice.public_key], 1)], asset_id=tx.id) transfer_tx = transfer_tx.sign([alice.private_key]) - block2 = b.create_block([transfer_tx]) - b.write_block(block2) + b.store_bulk_transactions([transfer_tx]) - monkeypatch.setattr('time.time', lambda: 1000000030) transfer_tx2 = Transaction.transfer(tx.to_inputs(), [([alice.public_key], 1)], asset_id=tx.id) transfer_tx2 = transfer_tx2.sign([alice.private_key]) - block3 = b.create_block([transfer_tx2]) - b.write_block(block3) + with pytest.raises(OperationError): + b.store_bulk_transactions([transfer_tx2]) - # Vote both block2 and block3 valid - vote = b.vote(block2.id, b.get_last_voted_block().id, True) - b.write_vote(vote) - vote = b.vote(block3.id, b.get_last_voted_block().id, True) - b.write_vote(vote) - - with pytest.raises(CriticalDoubleInclusion): - b.get_spent(tx.id, 0) - - @pytest.mark.genesis - def test_get_spent_with_double_spend_detected(self, b, monkeypatch, alice): - from bigchaindb.exceptions import CriticalDoubleSpend + @pytest.mark.tendermint + def test_get_spent_with_double_spend_detected(self, b, alice): from bigchaindb.models import Transaction + from bigchaindb.common.exceptions import DoubleSpend + from bigchaindb.exceptions import CriticalDoubleSpend tx = Transaction.create([alice.public_key], [([alice.public_key], 1)]) tx = tx.sign([alice.private_key]) - monkeypatch.setattr('time.time', lambda: 1000000000) - block1 = b.create_block([tx]) - b.write_block(block1) + b.store_bulk_transactions([tx]) - monkeypatch.setattr('time.time', lambda: 1000000020) transfer_tx = Transaction.transfer(tx.to_inputs(), [([alice.public_key], 1)], asset_id=tx.id) transfer_tx = transfer_tx.sign([alice.private_key]) - block2 = b.create_block([transfer_tx]) - b.write_block(block2) - - monkeypatch.setattr('time.time', lambda: 1000000030) transfer_tx2 = Transaction.transfer(tx.to_inputs(), [([alice.public_key], 2)], asset_id=tx.id) transfer_tx2 = transfer_tx2.sign([alice.private_key]) - block3 = b.create_block([transfer_tx2]) - b.write_block(block3) - # Vote both block2 and block3 valid - vote = b.vote(block2.id, b.get_last_voted_block().id, True) - b.write_vote(vote) - vote = b.vote(block3.id, b.get_last_voted_block().id, True) - b.write_vote(vote) + with pytest.raises(DoubleSpend): + b.validate_transaction(transfer_tx2, [transfer_tx]) + + b.store_bulk_transactions([transfer_tx]) + + with pytest.raises(DoubleSpend): + b.validate_transaction(transfer_tx2) + + b.store_bulk_transactions([transfer_tx2]) with pytest.raises(CriticalDoubleSpend): b.get_spent(tx.id, 0) - @pytest.mark.genesis - def test_get_block_status_for_tx_with_double_inclusion(self, b, monkeypatch, alice): - from bigchaindb.exceptions import CriticalDoubleInclusion + @pytest.mark.tendermint + def test_get_block_status_for_tx_with_double_inclusion(self, b, alice): from bigchaindb.models import Transaction + from bigchaindb.backend.exceptions import OperationError tx = Transaction.create([alice.public_key], [([alice.public_key], 1)]) tx = tx.sign([alice.private_key]) - monkeypatch.setattr('time.time', lambda: 1000000000) - block1 = b.create_block([tx]) - b.write_block(block1) + b.store_bulk_transactions([tx]) - monkeypatch.setattr('time.time', lambda: 1000000020) - block2 = b.create_block([tx]) - b.write_block(block2) + with pytest.raises(OperationError): + b.store_bulk_transactions([tx]) - # Vote both blocks valid (creating a double spend) - vote = b.vote(block1.id, b.get_last_voted_block().id, True) - b.write_vote(vote) - vote = b.vote(block2.id, b.get_last_voted_block().id, True) - b.write_vote(vote) - - with pytest.raises(CriticalDoubleInclusion): - b.get_blocks_status_containing_tx(tx.id) - - @pytest.mark.genesis - def test_get_transaction_in_invalid_and_valid_block(self, monkeypatch, b, alice): - from bigchaindb.models import Transaction - - monkeypatch.setattr('time.time', lambda: 1000000000) - tx1 = Transaction.create([alice.public_key], [([alice.public_key], 1)], - metadata={'msg': 1}) - tx1 = tx1.sign([alice.private_key]) - block1 = b.create_block([tx1]) - b.write_block(block1) - - monkeypatch.setattr('time.time', lambda: 1000000020) - tx2 = Transaction.create([alice.public_key], [([alice.public_key], 1)], - metadata={'msg': 2}) - tx2 = tx2.sign([alice.private_key]) - block2 = b.create_block([tx2]) - b.write_block(block2) - - # vote the first block invalid - vote = b.vote(block1.id, b.get_last_voted_block().id, False) - b.write_vote(vote) - - # vote the second block valid - vote = b.vote(block2.id, b.get_last_voted_block().id, True) - b.write_vote(vote) - - assert b.get_transaction(tx1.id) is None - assert b.get_transaction(tx2.id) == tx2 - - @pytest.mark.genesis + @pytest.mark.tendermint def test_text_search(self, b, alice): from bigchaindb.models import Transaction from bigchaindb.backend.exceptions import OperationError @@ -215,13 +93,8 @@ class TestBigchainApi(object): tx3 = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=asset3).sign([alice.private_key]) - # create the block - block = b.create_block([tx1, tx2, tx3]) - b.write_block(block) - - # vote valid - vote = b.vote(block.id, b.get_last_voted_block().id, True) - b.write_vote(vote) + # write the transactions to the DB + b.store_bulk_transactions([tx1, tx2, tx3]) # get the assets through text search # this query only works with MongoDB @@ -232,182 +105,13 @@ class TestBigchainApi(object): else: assert len(assets) == 3 - @pytest.mark.genesis - def test_text_search_returns_valid_only(self, monkeypatch, b, alice): - from bigchaindb.models import Transaction - from bigchaindb.backend.exceptions import OperationError - from bigchaindb.backend.localmongodb.connection import LocalMongoDBConnection - - asset_valid = {'msg': 'Hello BigchainDB!'} - asset_invalid = {'msg': 'Goodbye BigchainDB!'} - - monkeypatch.setattr('time.time', lambda: 1000000000) - tx1 = Transaction.create([alice.public_key], [([alice.public_key], 1)], - asset=asset_valid) - tx1 = tx1.sign([alice.private_key]) - block1 = b.create_block([tx1]) - b.write_block(block1) - - monkeypatch.setattr('time.time', lambda: 1000000020) - tx2 = Transaction.create([alice.public_key], [([alice.public_key], 1)], - asset=asset_invalid) - tx2 = tx2.sign([alice.private_key]) - block2 = b.create_block([tx2]) - b.write_block(block2) - - # vote the first block valid - vote = b.vote(block1.id, b.get_last_voted_block().id, True) - b.write_vote(vote) - - # vote the second block invalid - vote = b.vote(block2.id, b.get_last_voted_block().id, False) - b.write_vote(vote) - - # get assets with text search - try: - assets = list(b.text_search('bigchaindb')) - except OperationError: - assert not isinstance(b.connection, LocalMongoDBConnection) - return - - # should only return one asset - assert len(assets) == 1 - # should return the asset created by tx1 - assert assets[0] == { - 'data': {'msg': 'Hello BigchainDB!'}, - 'id': tx1.id - } - - @pytest.mark.usefixtures('inputs') - def test_write_transaction(self, b, user_pk, user_sk): - from bigchaindb.models import Transaction - - input_tx = b.get_owned_ids(user_pk).pop() - input_tx = b.get_transaction(input_tx.txid) - inputs = input_tx.to_inputs() - tx = Transaction.transfer(inputs, [([user_pk], 1)], - asset_id=input_tx.id) - tx = tx.sign([user_sk]) - b.write_transaction(tx) - - tx_from_db, status = b.get_transaction(tx.id, include_status=True) - - assert tx_from_db.to_dict() == tx.to_dict() - - @pytest.mark.usefixtures('inputs') - def test_read_transaction(self, b, user_pk, user_sk): - from bigchaindb.models import Transaction - - input_tx = b.get_owned_ids(user_pk).pop() - input_tx = b.get_transaction(input_tx.txid) - inputs = input_tx.to_inputs() - tx = Transaction.transfer(inputs, [([user_pk], 1)], - asset_id=input_tx.id) - tx = tx.sign([user_sk]) - b.write_transaction(tx) - - # create block and write it to the bighcain before retrieving the transaction - block = b.create_block([tx]) - b.write_block(block) - - response, status = b.get_transaction(tx.id, include_status=True) - # add validity information, which will be returned - assert tx.to_dict() == response.to_dict() - - @pytest.mark.usefixtures('inputs') - def test_read_transaction_invalid_block(self, b, user_pk, user_sk): - from bigchaindb.models import Transaction - - input_tx = b.get_owned_ids(user_pk).pop() - input_tx = b.get_transaction(input_tx.txid) - inputs = input_tx.to_inputs() - tx = Transaction.transfer(inputs, [([user_pk], 1)], - asset_id=input_tx.id) - tx = tx.sign([user_sk]) - # There's no need to b.write_transaction(tx) to the backlog - - # create block - block = b.create_block([tx]) - b.write_block(block) - - # vote the block invalid - vote = b.vote(block.id, b.get_last_voted_block().id, False) - b.write_vote(vote) - response = b.get_transaction(tx.id) - - # should be None, because invalid blocks are ignored - # and a copy of the tx is not in the backlog - assert response is None - - @pytest.mark.usefixtures('inputs') - def test_genesis_block(self, b): - from bigchaindb.backend import query - block = query.get_genesis_block(b.connection) - - assert len(block['block']['transactions']) == 1 - assert block['block']['transactions'][0]['operation'] == 'GENESIS' - assert block['block']['transactions'][0]['inputs'][0]['fulfills'] is None - - @pytest.mark.genesis - def test_create_genesis_block_fails_if_table_not_empty(self, b): - from bigchaindb.common.exceptions import GenesisBlockAlreadyExistsError - - with pytest.raises(GenesisBlockAlreadyExistsError): - b.create_genesis_block() - - @pytest.mark.skipif(reason='This test may not make sense after changing the chainification mode') - def test_get_last_block(self, b): - from bigchaindb.backend import query - # get the number of blocks - num_blocks = query.count_blocks(b.connection) - - # get the last block - last_block = b.get_last_block() - - assert last_block['block']['block_number'] == num_blocks - 1 - - @pytest.mark.skipif(reason='This test may not make sense after changing the chainification mode') - def test_get_last_block_id(self, b): - last_block = b.get_last_block() - last_block_id = b.get_last_block_id() - - assert last_block_id == last_block['id'] - - @pytest.mark.skipif(reason='This test may not make sense after changing the chainification mode') - def test_get_previous_block(self, b): - last_block = b.get_last_block() - new_block = b.create_block([]) - b.write_block(new_block) - - prev_block = b.get_previous_block(new_block) - - assert prev_block == last_block - - @pytest.mark.skipif(reason='This test may not make sense after changing the chainification mode') - def test_get_previous_block_id(self, b): - last_block = b.get_last_block() - new_block = b.create_block([]) - b.write_block(new_block) - - prev_block_id = b.get_previous_block_id(new_block) - - assert prev_block_id == last_block['id'] - - def test_create_empty_block(self, b): - from bigchaindb.common.exceptions import OperationError - - with pytest.raises(OperationError) as excinfo: - b.create_block([]) - - assert excinfo.value.args[0] == 'Empty block creation is not allowed' - @pytest.mark.usefixtures('inputs') + @pytest.mark.tendermint def test_non_create_input_not_found(self, b, user_pk): from cryptoconditions import Ed25519Sha256 from bigchaindb.common.exceptions import InputDoesNotExist from bigchaindb.common.transaction import Input, TransactionLink from bigchaindb.models import Transaction - from bigchaindb import BigchainDB # Create an input for a non existing transaction input = Input(Ed25519Sha256(public_key=b58decode(user_pk)), @@ -415,21 +119,28 @@ class TestBigchainApi(object): TransactionLink('somethingsomething', 0)) tx = Transaction.transfer([input], [([user_pk], 1)], asset_id='mock_asset_link') - with pytest.raises(InputDoesNotExist): - tx.validate(BigchainDB()) + tx.validate(b) - def test_count_backlog(self, b, user_pk, alice): - from bigchaindb.backend import query + @pytest.mark.tendermint + def test_write_transaction(self, b, user_sk, user_pk, alice, create_tx): from bigchaindb.models import Transaction - for i in range(4): - tx = Transaction.create([alice.public_key], [([user_pk], 1)], - metadata={'msg': i}) \ - .sign([alice.private_key]) - b.write_transaction(tx) + asset1 = {'msg': 'BigchainDB 1'} - assert query.count_backlog(b.connection) == 4 + tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], + asset=asset1).sign([alice.private_key]) + b.store_bulk_transactions([tx]) + + tx_from_db = b.get_transaction(tx.id) + + before = tx.to_dict() + after = tx_from_db.to_dict() + + assert before['asset']['data'] == after['asset']['data'] + before.pop('asset', None) + after.pop('asset', None) + assert before == after class TestTransactionValidation(object):