From dbb3414fd0f73bcde71efb983fdb2a411dfbe47f Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Tue, 17 Jan 2017 18:02:09 +0100 Subject: [PATCH] generalise get_txids_by_asset_id into get_txids_filtered and remove get_transactions_by_asset_id --- bigchaindb/backend/mongodb/query.py | 31 +++++----- bigchaindb/backend/query.py | 13 ++++ bigchaindb/backend/rethinkdb/query.py | 33 +++++----- bigchaindb/core.py | 24 -------- tests/assets/test_digital_assets.py | 89 --------------------------- tests/backend/mongodb/test_queries.py | 45 ++++++++------ tests/backend/test_generics.py | 2 +- 7 files changed, 74 insertions(+), 163 deletions(-) diff --git a/bigchaindb/backend/mongodb/query.py b/bigchaindb/backend/mongodb/query.py index a9e52c82..c4e3cdc8 100644 --- a/bigchaindb/backend/mongodb/query.py +++ b/bigchaindb/backend/mongodb/query.py @@ -82,21 +82,6 @@ def get_blocks_status_from_transaction(conn, transaction_id): projection=['id', 'block.voters']) -@register_query(MongoDBConnection) -def get_txids_by_asset_id(conn, asset_id): - cursor = conn.db['bigchain'].aggregate([ - {'$match': { - 'block.transactions.asset.id': asset_id - }}, - {'$unwind': '$block.transactions'}, - {'$match': { - 'block.transactions.asset.id': asset_id - }}, - {'$project': {'block.transactions.id': True}} - ]) - return (elem['block']['transactions']['id'] for elem in cursor) - - @register_query(MongoDBConnection) def get_asset_by_id(conn, asset_id): cursor = conn.db['bigchain'].aggregate([ @@ -249,3 +234,19 @@ def get_unvoted_blocks(conn, node_pubkey): 'votes': False, '_id': False }} ]) + + +@register_query(MongoDBConnection) +def get_txids_filtered(conn, asset_id, operation=None): + match = {'block.transactions.asset.id': asset_id} + + if operation: + match['block.transactions.operation'] = operation + + cursor = conn.db['bigchain'].aggregate([ + {'$match': match}, + {'$unwind': '$block.transactions'}, + {'$match': match}, + {'$project': {'block.transactions.id': True}} + ]) + return (r['block']['transactions']['id'] for r in cursor) diff --git a/bigchaindb/backend/query.py b/bigchaindb/backend/query.py index e71f6be3..cbd17d25 100644 --- a/bigchaindb/backend/query.py +++ b/bigchaindb/backend/query.py @@ -318,3 +318,16 @@ def get_unvoted_blocks(connection, node_pubkey): """ raise NotImplementedError + + +@singledispatch +def get_txids_filtered(connection, asset_id, operation=None): + """ + Return all transactions for a particular asset id and optional operation. + + Args: + asset_id (str): ID of transaction that defined the asset + operation (str) (optional): Operation to filter on + """ + + raise NotImplementedError diff --git a/bigchaindb/backend/rethinkdb/query.py b/bigchaindb/backend/rethinkdb/query.py index f7a7c45a..fd0bdcb3 100644 --- a/bigchaindb/backend/rethinkdb/query.py +++ b/bigchaindb/backend/rethinkdb/query.py @@ -71,22 +71,6 @@ def get_blocks_status_from_transaction(connection, transaction_id): .pluck('votes', 'id', {'block': ['voters']})) -@register_query(RethinkDBConnection) -def get_txids_by_asset_id(connection, asset_id): - # here we only want to return the transaction ids since later on when - # we are going to retrieve the transaction with status validation - - # Then find any TRANSFER transactions related to the asset - tx_cursor = connection.run( - r.table('bigchain') - .get_all(asset_id, index='asset_id') - .concat_map(lambda block: block['block']['transactions']) - .filter(lambda transaction: transaction['asset']['id'] == asset_id) - .get_field('id')) - - return tx_cursor - - @register_query(RethinkDBConnection) def get_asset_by_id(connection, asset_id): return connection.run(_get_asset_create_tx_query(asset_id).pluck('asset')) @@ -249,3 +233,20 @@ def get_unvoted_blocks(connection, node_pubkey): # database level. Solving issue #444 can help untangling the situation unvoted_blocks = filter(lambda block: not utils.is_genesis_block(block), unvoted) return unvoted_blocks + + +@register_query(RethinkDBConnection) +def get_txids_filtered(connection, asset_id, operation=None): + # here we only want to return the transaction ids since later on when + # we are going to retrieve the transaction with status validation + + tx_filter = r.row['asset']['id'] == asset_id + if operation: + tx_filter &= r.row['operation'] == operation + + return connection.run( + r.table('bigchain') + .get_all(asset_id, index='asset_id') + .concat_map(lambda block: block['block']['transactions']) + .filter(tx_filter) + .get_field('id')) diff --git a/bigchaindb/core.py b/bigchaindb/core.py index 3c62e65d..a520bba4 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -317,30 +317,6 @@ class Bigchain(object): else: return None - def get_transactions_by_asset_id(self, asset_id): - """Retrieves valid or undecided transactions related to a particular - asset. - - A digital asset in bigchaindb is identified by an uuid. This allows us - to query all the transactions related to a particular digital asset, - knowing the id. - - Args: - asset_id (str): the id for this particular asset. - - Returns: - A list of valid or undecided transactions related to the asset. - If no transaction exists for that asset it returns an empty list - `[]` - """ - txids = backend.query.get_txids_by_asset_id(self.connection, asset_id) - transactions = [] - for txid in txids: - tx = self.get_transaction(txid) - if tx: - transactions.append(tx) - return transactions - def get_asset_by_id(self, asset_id): """Returns the asset associated with an asset_id. diff --git a/tests/assets/test_digital_assets.py b/tests/assets/test_digital_assets.py index 9d2adbd5..1dc4764f 100644 --- a/tests/assets/test_digital_assets.py +++ b/tests/assets/test_digital_assets.py @@ -90,95 +90,6 @@ def test_asset_id_mismatch(b, user_pk): Transaction.get_asset_id([tx1, tx2]) -@pytest.mark.bdb -@pytest.mark.usefixtures('inputs') -def test_get_transactions_by_asset_id(b, user_pk, user_sk): - from bigchaindb.models import Transaction - - tx_create = b.get_owned_ids(user_pk).pop() - tx_create = b.get_transaction(tx_create.txid) - asset_id = tx_create.id - txs = b.get_transactions_by_asset_id(asset_id) - - assert len(txs) == 1 - assert txs[0].id == tx_create.id - assert txs[0].id == asset_id - - # create a transfer transaction - tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)], - tx_create.id) - tx_transfer_signed = tx_transfer.sign([user_sk]) - # create the block - block = b.create_block([tx_transfer_signed]) - b.write_block(block) - # vote the block valid - vote = b.vote(block.id, b.get_last_voted_block().id, True) - b.write_vote(vote) - - txs = b.get_transactions_by_asset_id(asset_id) - - assert len(txs) == 2 - assert {tx_create.id, tx_transfer.id} == set(tx.id for tx in txs) - assert asset_id == Transaction.get_asset_id(txs) - - -@pytest.mark.bdb -@pytest.mark.usefixtures('inputs') -def test_get_transactions_by_asset_id_with_invalid_block(b, user_pk, user_sk): - from bigchaindb.models import Transaction - - tx_create = b.get_owned_ids(user_pk).pop() - tx_create = b.get_transaction(tx_create.txid) - asset_id = tx_create.id - txs = b.get_transactions_by_asset_id(asset_id) - - assert len(txs) == 1 - assert txs[0].id == tx_create.id - assert txs[0].id == asset_id - - # create a transfer transaction - tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)], - tx_create.id) - tx_transfer_signed = tx_transfer.sign([user_sk]) - # create the block - block = b.create_block([tx_transfer_signed]) - b.write_block(block) - # vote the block invalid - vote = b.vote(block.id, b.get_last_voted_block().id, False) - b.write_vote(vote) - - txs = b.get_transactions_by_asset_id(asset_id) - - assert len(txs) == 1 - - -@pytest.mark.bdb -@pytest.mark.usefixtures('inputs') -def test_get_asset_by_id(b, user_pk, user_sk): - from bigchaindb.models import Transaction - - tx_create = b.get_owned_ids(user_pk).pop() - tx_create = b.get_transaction(tx_create.txid) - - # create a transfer transaction - tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)], - tx_create.id) - tx_transfer_signed = tx_transfer.sign([user_sk]) - # create the block - block = b.create_block([tx_transfer_signed]) - b.write_block(block) - # vote the block valid - vote = b.vote(block.id, b.get_last_voted_block().id, True) - b.write_vote(vote) - - asset_id = Transaction.get_asset_id([tx_create, tx_transfer]) - txs = b.get_transactions_by_asset_id(asset_id) - assert len(txs) == 2 - - asset = b.get_asset_by_id(asset_id) - assert asset == tx_create.asset - - def test_create_invalid_divisible_asset(b, user_pk, user_sk): from bigchaindb.models import Transaction from bigchaindb.common.exceptions import AmountError diff --git a/tests/backend/mongodb/test_queries.py b/tests/backend/mongodb/test_queries.py index 9f20cc9f..48089805 100644 --- a/tests/backend/mongodb/test_queries.py +++ b/tests/backend/mongodb/test_queries.py @@ -125,24 +125,6 @@ def test_get_block_status_from_transaction(create_tx): assert block_db['block']['voters'] == block.voters -def test_get_txids_by_asset_id(signed_create_tx, signed_transfer_tx): - from bigchaindb.backend import connect, query - from bigchaindb.models import Block - conn = connect() - - # create and insert two blocks, one for the create and one for the - # transfer transaction - block = Block(transactions=[signed_create_tx]) - conn.db.bigchain.insert_one(block.to_dict()) - block = Block(transactions=[signed_transfer_tx]) - conn.db.bigchain.insert_one(block.to_dict()) - - txids = list(query.get_txids_by_asset_id(conn, signed_create_tx.id)) - - assert len(txids) == 2 - assert txids == [signed_create_tx.id, signed_transfer_tx.id] - - def test_get_asset_by_id(create_tx): from bigchaindb.backend import connect, query from bigchaindb.models import Block @@ -366,3 +348,30 @@ def test_get_unvoted_blocks(signed_create_tx): assert len(unvoted_blocks) == 1 assert unvoted_blocks[0] == block.to_dict() + + +def test_get_txids_filtered(signed_create_tx, signed_transfer_tx): + from bigchaindb.backend import connect, query + from bigchaindb.models import Block, Transaction + conn = connect() + + # create and insert two blocks, one for the create and one for the + # transfer transaction + block = Block(transactions=[signed_create_tx]) + conn.db.bigchain.insert_one(block.to_dict()) + block = Block(transactions=[signed_transfer_tx]) + conn.db.bigchain.insert_one(block.to_dict()) + + asset_id = Transaction.get_asset_id([signed_create_tx, signed_transfer_tx]) + + # Test get by just asset id + txids = set(query.get_txids_filtered(conn, asset_id)) + assert txids == {signed_create_tx.id, signed_transfer_tx.id} + + # Test get by asset and CREATE + txids = set(query.get_txids_filtered(conn, asset_id, Transaction.CREATE)) + assert txids == {signed_create_tx.id} + + # Test get by asset and TRANSFER + txids = set(query.get_txids_filtered(conn, asset_id, Transaction.TRANSFER)) + assert txids == {signed_transfer_tx.id} diff --git a/tests/backend/test_generics.py b/tests/backend/test_generics.py index 2ab33a7c..8db08999 100644 --- a/tests/backend/test_generics.py +++ b/tests/backend/test_generics.py @@ -26,7 +26,7 @@ def test_schema(schema_func_name, args_qty): ('get_stale_transactions', 1), ('get_blocks_status_from_transaction', 1), ('get_transaction_from_backlog', 1), - ('get_txids_by_asset_id', 1), + ('get_txids_filtered', 1), ('get_asset_by_id', 1), ('get_owned_ids', 1), ('get_votes_by_block_id', 1),