Integrate get block with transaction id api (#2021)

* Integrate api, get block with transaction id

* Fixed docs and docstrings

* Fix docs

* Remove status from tendermint, fix mongo query
This commit is contained in:
Vanshdeep Singh 2018-02-08 19:02:21 +05:30 committed by vrde
parent a7ed8cf4cb
commit fa33fc26af
7 changed files with 111 additions and 21 deletions

View File

@ -191,3 +191,11 @@ def get_block(conn, block_id):
conn.collection('blocks')
.find_one({'height': block_id},
projection={'_id': False}))
@register_query(LocalMongoDBConnection)
def get_block_with_transaction(conn, txid):
return conn.run(
conn.collection('blocks')
.find({'transactions': txid},
projection={'_id': False, 'height': True}))

View File

@ -339,6 +339,20 @@ def get_block(connection, block_id):
raise NotImplementedError
@singledispatch
def get_block_with_transaction(connection, txid):
"""Get a block containing transaction id `txid`
Args:
txid (str): id of transaction to be searched.
Returns:
block_id (int): the block id or `None`
"""
raise NotImplementedError
@singledispatch
def write_assets(connection, assets):
"""Write a list of assets to the assets table.

View File

@ -191,6 +191,22 @@ class BigchainDB(Bigchain):
else:
return block
def get_block_containing_tx(self, txid):
"""Retrieve the list of blocks (block ids) containing a
transaction with transaction id `txid`
Args:
txid (str): transaction id of the transaction to query
Returns:
Block id list (list(int))
"""
blocks = list(backend.query.get_block_with_transaction(self.connection, txid))
if len(blocks) > 1:
logger.critical('Transaction id %s exists in multiple blocks', txid)
return [block['height'] for block in blocks]
def validate_transaction(self, tx, current_transactions=[]):
"""Validate a transaction against the current status of the database."""

View File

@ -5,7 +5,6 @@ For more information please refer to the documentation: http://bigchaindb.com/ht
from flask import current_app
from flask_restful import Resource, reqparse
from bigchaindb import Bigchain
from bigchaindb.web.views.base import make_error
@ -42,18 +41,13 @@ class BlockListApi(Resource):
"""
parser = reqparse.RequestParser()
parser.add_argument('transaction_id', type=str, required=True)
parser.add_argument('status', type=str, case_sensitive=False,
choices=[Bigchain.BLOCK_VALID, Bigchain.BLOCK_INVALID, Bigchain.BLOCK_UNDECIDED])
args = parser.parse_args(strict=True)
tx_id = args['transaction_id']
status = args['status']
pool = current_app.config['bigchain_pool']
with pool() as bigchain:
block_statuses = bigchain.get_blocks_status_containing_tx(tx_id)
blocks = [block_id for block_id, block_status in block_statuses.items()
if not status or block_status == status]
blocks = bigchain.get_block_containing_tx(tx_id)
return blocks

View File

@ -251,7 +251,14 @@ def main():
ctx['block'] = pretty_json(block_dict)
ctx['blockid'] = block.height
block = Block(transactions=[tx], node_pubkey=node_public, voters=[node_public], signature=signature)
# block status
block_list = [
block.height
]
ctx['block_list'] = pretty_json(block_list)
# block = Block(transactions=[tx], node_pubkey=node_public, voters=[node_public], signature=signature)
block_transfer = Block(transactions=[tx_transfer], node_pubkey=node_public,
voters=[node_public], signature=signature)
ctx['block_transfer'] = pretty_json(block_transfer.to_dict())
@ -263,13 +270,6 @@ def main():
vote = b.vote(vblock.id, DUMMY_SHA3, True)
ctx['vote'] = pretty_json(vote)
# block status
block_list = [
block_transfer.id,
block.id
]
ctx['block_list'] = pretty_json(block_list)
base_path = os.path.join(os.path.dirname(__file__),
'source/http-samples')
if not os.path.exists(base_path):

View File

@ -645,19 +645,22 @@ Blocks
:statuscode 400: The request wasn't understood by the server, e.g. just requesting ``/blocks`` without the ``block_id``.
.. http:get:: /api/v1/blocks?transaction_id={transaction_id}&status={UNDECIDED|VALID|INVALID}
.. http:get:: /api/v1/blocks?transaction_id={transaction_id}
Retrieve a list of ``block_id`` with their corresponding status that contain a transaction with the ID ``transaction_id``.
Retrieve a list of block IDs (block heights), such that the blocks with those IDs contain a transaction with the ID ``transaction_id``. A correct response may consist of an empty list or a list with one block ID.
Any blocks, be they ``UNDECIDED``, ``VALID`` or ``INVALID`` will be
returned if no status filter is provided.
.. note::
The query parameter ``status`` has been deprecated. It allowed
users to filter blocks based on their status i.e. only blocks with the specified
status were included in the response. Since then this behavior has changed
and now block are created only after the transactions are accepted by the
network i.e. blocks have only one status ``VALID``
.. note::
In case no block was found, an empty list and an HTTP status code
``200 OK`` is returned, as the request was still successful.
:query string transaction_id: transaction ID *(required)*
:query string status: Filter blocks by their status. One of ``VALID``, ``UNDECIDED`` or ``INVALID``.
**Example request**:
@ -671,7 +674,7 @@ Blocks
:resheader Content-Type: ``application/json``
:statuscode 200: A list of blocks containing a transaction with ID ``transaction_id`` was found and returned.
:statuscode 200: The request was properly formed and zero or more blocks were found containing the specified ``transaction_id``.
:statuscode 400: The request wasn't understood by the server, e.g. just requesting ``/blocks``, without defining ``transaction_id``.

View File

@ -35,3 +35,58 @@ def test_get_block_returns_404_if_not_found(client):
res = client.get(BLOCKS_ENDPOINT + '123/')
assert res.status_code == 404
@pytest.mark.bdb
def test_get_block_containing_transaction(tb, client):
b = tb
tx = Transaction.create([b.me], [([b.me], 1)], asset={'cycle': 'hero'})
tx = tx.sign([b.me_private])
b.store_transaction(tx)
block = Block(app_hash='random_utxo',
height=13,
transactions=[tx.id])
b.store_block(block._asdict())
res = client.get('{}?transaction_id={}'.format(BLOCKS_ENDPOINT, tx.id))
expected_response = [block.height]
assert res.json == expected_response
assert res.status_code == 200
@pytest.mark.bdb
def test_get_blocks_by_txid_endpoint_returns_empty_list_not_found(client):
res = client.get(BLOCKS_ENDPOINT + '?transaction_id=')
assert res.status_code == 200
assert len(res.json) == 0
res = client.get(BLOCKS_ENDPOINT + '?transaction_id=123')
assert res.status_code == 200
assert len(res.json) == 0
@pytest.mark.bdb
def test_get_blocks_by_txid_endpoint_returns_400_bad_query_params(client):
res = client.get(BLOCKS_ENDPOINT)
assert res.status_code == 400
res = client.get(BLOCKS_ENDPOINT + '?ts_id=123')
assert res.status_code == 400
assert res.json == {
'message': {
'transaction_id': 'Missing required parameter in the JSON body or the post body or the query string'
}
}
res = client.get(BLOCKS_ENDPOINT + '?transaction_id=123&foo=123')
assert res.status_code == 400
assert res.json == {
'message': 'Unknown arguments: foo'
}
res = client.get(BLOCKS_ENDPOINT + '?transaction_id=123&status=123')
assert res.status_code == 400
assert res.json == {
'message': 'Unknown arguments: status'
}