diff --git a/examples/scan.py b/examples/scan.py index 95c4e63..90a968a 100644 --- a/examples/scan.py +++ b/examples/scan.py @@ -7,5 +7,6 @@ if __name__ == '__main__': logging.config.dictConfig(yaml.safe_load(open('../.logging.yaml', 'r'))) rpc_url = "http://127.0.0.1:26657" - ws_client = ScanBlock(endpoint_url=rpc_url) - ws_client.start() + scan = ScanBlock(endpoint_url=rpc_url) + block_time_interval = scan.avg_block_time_interval() + scan.start(block_interval_ms=int(block_time_interval * 1000)) diff --git a/fxsdk/client/grpc.py b/fxsdk/client/grpc.py index 0dac41b..770f983 100644 --- a/fxsdk/client/grpc.py +++ b/fxsdk/client/grpc.py @@ -106,7 +106,7 @@ def query_gas_prices(self) -> [Coin]: return parse_coins(response.minimum_gas_price) def query_block_by_height(self, height: int) -> Block: - response = TendermintClient(self.channel).GetBlockByHeight(GetBlockByHeightRequest(height)) + response = TendermintClient(self.channel).GetBlockByHeight(GetBlockByHeightRequest(height=height)) return response.block def query_latest_block(self) -> Block: @@ -124,6 +124,19 @@ def query_node_info(self) -> DefaultNodeInfo: response = TendermintClient(self.channel).GetNodeInfo(GetNodeInfoRequest()) return response.default_node_info + def avg_block_time_interval(self, block_interval: int = 2000) -> float: + latest_block = self.query_latest_block() + latest_block_height = latest_block.header.height + latest_block_time = latest_block.header.time.seconds + if latest_block_height > block_interval: + block = self.query_block_by_height(latest_block_height - block_interval) + else: + block = self.query_block_by_height(1) + block_interval = latest_block_height - 1 + block_time = block.header.time.seconds + block_time_interval = (latest_block_time - block_time) / block_interval + return float(format(block_time_interval, '.3f')) + def query_tx(self, tx_hash: str) -> GetTxResponse: return TxClient(self.channel).GetTx(GetTxRequest(hash=tx_hash)) diff --git a/fxsdk/client/http.py b/fxsdk/client/http.py index 47864f1..53522e0 100644 --- a/fxsdk/client/http.py +++ b/fxsdk/client/http.py @@ -6,6 +6,7 @@ import requests import ujson +from datetime import datetime from typing import Optional, Dict from requests.sessions import HTTPAdapter from urllib3.util.retry import Retry @@ -123,6 +124,21 @@ def _handle_response(response) -> Dict: except ValueError: raise RPCException('Invalid Response: %s' % response.text) + def avg_block_time_interval(self, block_interval: int = 2000) -> float: + latest_block = self.get_block() + latest_block_time = latest_block.get('block', {}).get('header', {}).get('time', None) + latest_block_height = int(latest_block.get('block', {}).get('header', {}).get('height', None)) + if latest_block_height > block_interval: + block = self.get_block(latest_block_height - block_interval) + else: + block = self.get_block(1) + block_interval = latest_block_height - 1 + block_time = block.get('block', {}).get('header', {}).get('time', None) + latest_block_time = datetime.fromisoformat(latest_block_time) + block_time = datetime.fromisoformat(block_time) + block_time_interval = (latest_block_time.timestamp() - block_time.timestamp()) / block_interval + return float(format(block_time_interval, '.3f')) + def get_abci_info(self) -> Dict: return self._request('abci_info') diff --git a/fxsdk/client/scan.py b/fxsdk/client/scan.py index c3aeb67..a37fbfa 100644 --- a/fxsdk/client/scan.py +++ b/fxsdk/client/scan.py @@ -11,28 +11,22 @@ class ScanException(Exception): class ScanBlock(HttpRpcClient): - def __init__(self, endpoint_url, block_interval_ms: int = 500): - super().__init__(endpoint_url) - self._start_block_height = -1 - self._block_interval_ms = block_interval_ms - def start(self, start_block_height: int = -1): - if start_block_height > 0: - self._start_block_height = start_block_height - else: + def start(self, block_interval_ms=500, start_block_height: int = -1): + if start_block_height <= 0: status = self.get_status() if status.get('sync_info', {}).get('catching_up', True): raise ScanException("node is catching up") latest_block_height = status.get('sync_info', {}).get('latest_block_height', 1) - self._start_block_height = int(latest_block_height) + start_block_height = int(latest_block_height) while True: try: - block_result = self.get_block_results(self._start_block_height) + block_result = self.get_block_results(start_block_height) txs_results = block_result.get('txs_results', []) txs_results = txs_results if txs_results else [] txs = [] if len(txs_results) > 0: - block = self.get_block(self._start_block_height) + block = self.get_block(start_block_height) base64_txs = block.get('block', {}).get('data', {}).get('txs', []) for base64_tx in base64_txs: tx = new_tx_from_base64(base64_tx) @@ -40,11 +34,11 @@ def start(self, start_block_height: int = -1): block_events = block_result.get('finalize_block_events', []) block_height = block_result.get('height', 0) self.process_new_block(block_height, block_events, txs_results, txs) - self._start_block_height += 1 + start_block_height += 1 except RPCException as e: self._log.debug(f"rpc exception: {e}") continue - time.sleep(self._block_interval_ms / 1000) + time.sleep(block_interval_ms / 1000) def process_new_block(self, block_height: int, block_events: [Dict], txs_result: [Dict], txs: [Tx]): self._log.info( diff --git a/tests/test_grpc_query.py b/tests/test_grpc_query.py index 4a49ccf..4a823ce 100644 --- a/tests/test_grpc_query.py +++ b/tests/test_grpc_query.py @@ -44,6 +44,18 @@ def test_query_node_info(self): res = grpc_cli.query_node_info() print(res) + def test_query_latest_block(self): + res = grpc_cli.query_latest_block() + print(res) + + def test_query_block_by_height(self): + res = grpc_cli.query_block_by_height(height=1) + print(res) + + def test_avg_block_time_interval(self): + res = grpc_cli.avg_block_time_interval() + print(res) + if __name__ == '__main__': unittest.main() diff --git a/tests/test_http_query.py b/tests/test_http_query.py index d29226e..6d21926 100644 --- a/tests/test_http_query.py +++ b/tests/test_http_query.py @@ -1,11 +1,8 @@ import asyncio import json -import logging.config import unittest from typing import Dict -import yaml - from fxsdk.client.http import HttpRpcClient, AsyncHttpRpcClient from fxsdk.client.websockets import WebsocketRpcClient from fxsdk.msg import event @@ -21,6 +18,16 @@ def test_get_block_result(self): print(block_res) assert block_res + def test_get_block(self): + block = rpc_client.get_block() + print(json.dumps(block, indent=2)) + assert block + + def test_avg_block_time_interval(self): + res = rpc_client.avg_block_time_interval() + print(res) + assert res + class TestWebsocketRpcClient(unittest.IsolatedAsyncioTestCase): @@ -29,7 +36,6 @@ async def callback(msg: Dict): print("msg", json.dumps(msg)) async def test_get_block_result(self): - logging.config.dictConfig(yaml.safe_load(open('../.logging.yaml', 'r'))) loop = asyncio.get_event_loop() ws_client = await WebsocketRpcClient.create(endpoint_url=rpc_url, loop=loop, callback=self.callback) await ws_client.get_status()