diff --git a/bigchaindb/tendermint/lib.py b/bigchaindb/tendermint/lib.py index eba528fd..507e5d63 100644 --- a/bigchaindb/tendermint/lib.py +++ b/bigchaindb/tendermint/lib.py @@ -332,5 +332,19 @@ class BigchainDB(Bigchain): def fastquery(self): return fastquery.FastQuery(self.connection) + def get_validators(self): + try: + resp = requests.get(f'{ENDPOINT}validators') + validators = resp.json()['result']['validators'] + for v in validators: + v.pop('accum') + v.pop('address') + + return validators + + except requests.exceptions.RequestException as e: + logger.error('Error while connecting to Tendermint HTTP API') + raise e + Block = namedtuple('Block', ('app_hash', 'height', 'transactions')) diff --git a/bigchaindb/web/routes.py b/bigchaindb/web/routes.py index 5b6185ba..e53aa761 100644 --- a/bigchaindb/web/routes.py +++ b/bigchaindb/web/routes.py @@ -8,6 +8,7 @@ from bigchaindb.web.views import ( transactions as tx, outputs, votes, + validators, ) @@ -34,6 +35,7 @@ ROUTES_API_V1 = [ r('transactions', tx.TransactionListApi), r('outputs/', outputs.OutputListApi), r('votes/', votes.VotesApi), + r('validators/', validators.ValidatorsApi), ] diff --git a/bigchaindb/web/views/info.py b/bigchaindb/web/views/info.py index 205f4930..660f96ef 100644 --- a/bigchaindb/web/views/info.py +++ b/bigchaindb/web/views/info.py @@ -50,4 +50,5 @@ def get_api_v1_info(api_prefix): 'outputs': '{}outputs/'.format(api_prefix), 'streams': websocket_root, 'metadata': '{}metadata/'.format(api_prefix), + 'validators': '{}validators'.format(api_prefix), } diff --git a/bigchaindb/web/views/validators.py b/bigchaindb/web/views/validators.py new file mode 100644 index 00000000..825b0c7d --- /dev/null +++ b/bigchaindb/web/views/validators.py @@ -0,0 +1,18 @@ +from flask import current_app +from flask_restful import Resource + + +class ValidatorsApi(Resource): + def get(self): + """API endpoint to get validators set. + + Return: + A JSON string containing the validator set of the current node. + """ + + pool = current_app.config['bigchain_pool'] + + with pool() as bigchain: + validators = bigchain.get_validators() + + return validators diff --git a/tests/web/test_info.py b/tests/web/test_info.py index f682b5e3..81c0972a 100644 --- a/tests/web/test_info.py +++ b/tests/web/test_info.py @@ -18,6 +18,7 @@ def test_api_root_endpoint(client, wsserver_base_url): 'streams': '{}/api/v1/streams/valid_transactions'.format( wsserver_base_url), 'metadata': '/api/v1/metadata/', + 'validators': '/api/v1/validators', } }, 'docs': 'https://docs.bigchaindb.com/projects/server/en/vtsttst/', diff --git a/tests/web/test_validators.py b/tests/web/test_validators.py new file mode 100644 index 00000000..7a0e20ce --- /dev/null +++ b/tests/web/test_validators.py @@ -0,0 +1,49 @@ +import pytest + +from requests.exceptions import RequestException + +pytestmark = pytest.mark.tendermint + +VALIDATORS_ENDPOINT = '/api/v1/validators/' + + +def test_get_validators_endpoint(b, client, monkeypatch): + + def mock_get(uri): + return MockResponse() + monkeypatch.setattr('requests.get', mock_get) + + res = client.get(VALIDATORS_ENDPOINT) + + assert is_validator(res.json[0]) + assert res.status_code == 200 + + +def test_get_validators_500_endpoint(b, client, monkeypatch): + + def mock_get(uri): + raise RequestException + monkeypatch.setattr('requests.get', mock_get) + + with pytest.raises(RequestException): + client.get(VALIDATORS_ENDPOINT) + + +# Helper +def is_validator(v): + return ('pub_key' in v) and ('voting_power' in v) + + +class MockResponse(): + + def json(self): + return {'id': '', + 'jsonrpc': '2.0', + 'result': + {'block_height': 5, + 'validators': [ + {'accum': 0, + 'address': 'F5426F0980E36E03044F74DD414248D29ABCBDB2', + 'pub_key': {'data': '4E2685D9016126864733225BE00F005515200727FBAB1312FC78C8B76831255A', + 'type': 'ed25519'}, + 'voting_power': 10}]}}