From cbe983f5a9f0a8cd6cd1273aabf1b10ca67a2663 Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 14 Jun 2024 16:13:29 +0300 Subject: [PATCH] refactor(fw): extend `EthRPC` methods and use them in gentest (#568) Co-authored-by: Art Co-authored-by: danceratopz --- docs/CHANGELOG.md | 1 + src/cli/gentest.py | 73 +++++++----------------------- src/ethereum_test_tools/rpc/rpc.py | 39 ++++++++++++---- tests_consume/test_via_rlp.py | 2 +- 4 files changed, 47 insertions(+), 68 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 24543e51fd..32de46e09b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -43,6 +43,7 @@ Test fixtures for use by clients are available for each release on the [Github r - ✨ Add a new covariant marker `with_all_contract_creating_tx_types` that allows automatic parametrization of a test with all contract-creating transaction types at the current executing fork ([#602](https://github.com/ethereum/execution-spec-tests/pull/602)). - ✨ Tests are now encouraged to declare a `pre: Alloc` parameter to get the pre-allocation object for the test, and use `pre.deploy_contract` and `pre.fund_eoa` to deploy contracts and fund accounts respectively, instead of declaring the `pre` as a dictionary or modifying its contents directly (see the [state test tutorial](https://ethereum.github.io/execution-spec-tests/main/tutorials/state_transition/) for an updated example) ([#584](https://github.com/ethereum/execution-spec-tests/pull/584)). - ✨ Enable loading of [ethereum/tests/BlockchainTests](https://github.com/ethereum/tests/tree/develop/BlockchainTests) ([#596](https://github.com/ethereum/execution-spec-tests/pull/596)). +- 🔀 Refactor `gentest` to use `ethereum_test_tools.rpc.rpc` by adding to `get_transaction_by_hash`, `debug_trace_call` to `EthRPC` ([#568](https://github.com/ethereum/execution-spec-tests/pull/568)). ### 🔧 EVM Tools diff --git a/src/cli/gentest.py b/src/cli/gentest.py index 2a7ba97877..ede1050887 100644 --- a/src/cli/gentest.py +++ b/src/cli/gentest.py @@ -58,9 +58,9 @@ from typing import Dict, List, TextIO import click -import requests from ethereum_test_tools import Account, Address, Transaction, common +from ethereum_test_tools.rpc.rpc import BlockNumberType, EthRPC @click.command() @@ -96,7 +96,7 @@ def make_test(transaction_hash: str, output_file: TextIO, config_file: TextIO): state = request.debug_trace_call(tr) print("Perform eth_get_block_by_number", file=stderr) - block = request.eth_get_block_by_number(tr.block_number) + block = request.eth_get_block_by_number(int(tr.block_number, 16)) print("Generate py test", file=stderr) constructor = TestConstructor(PYTEST_TEMPLATE) @@ -283,39 +283,17 @@ def __init__(self, node_config: Config.RemoteNode): Initialize the RequestManager with specific client config. """ self.node_url = node_config.node_url - self.headers = { + headers = { "CF-Access-Client-Id": node_config.client_id, "CF-Access-Client-Secret": node_config.secret, - "Content-Type": "application/json", } - - def _make_request(self, data) -> requests.Response: - error_str = "An error occurred while making remote request: " - try: - response = requests.post(self.node_url, headers=self.headers, data=json.dumps(data)) - response.raise_for_status() - if response.status_code >= 200 and response.status_code < 300: - return response - else: - print(error_str + response.text, file=stderr) - raise requests.exceptions.HTTPError - except requests.exceptions.RequestException as e: - print(error_str, e, file=stderr) - raise e + self.rpc = EthRPC(node_config.node_url, extra_headers=headers) def eth_get_transaction_by_hash(self, transaction_hash: str) -> RemoteTransaction: """ Get transaction data. """ - data = { - "jsonrpc": "2.0", - "method": "eth_getTransactionByHash", - "params": [f"{transaction_hash}"], - "id": 1, - } - - response = self._make_request(data) - res = response.json().get("result", None) + res = self.rpc.get_transaction_by_hash(transaction_hash) assert ( res["type"] == "0x0" @@ -340,18 +318,11 @@ def eth_get_transaction_by_hash(self, transaction_hash: str) -> RemoteTransactio ), ) - def eth_get_block_by_number(self, block_number: str) -> RemoteBlock: + def eth_get_block_by_number(self, block_number: BlockNumberType) -> RemoteBlock: """ Get block by number """ - data = { - "jsonrpc": "2.0", - "method": "eth_getBlockByNumber", - "params": [f"{block_number}", False], - "id": 1, - } - response = self._make_request(data) - res = response.json().get("result", None) + res = self.rpc.get_block_by_number(block_number) return RequestManager.RemoteBlock( coinbase=res["miner"], @@ -363,28 +334,16 @@ def eth_get_block_by_number(self, block_number: str) -> RemoteBlock: def debug_trace_call(self, tr: RemoteTransaction) -> Dict[Address, Account]: """ - Get pre state required for transaction + Get pre-state required for transaction """ - data = { - "jsonrpc": "2.0", - "method": "debug_traceCall", - "params": [ - { - "from": f"{str(tr.transaction.sender)}", - "to": f"{str(tr.transaction.to)}", - "data": f"{str(tr.transaction.data)}", - }, - f"{tr.block_number}", - {"tracer": "prestateTracer"}, - ], - "id": 1, - } - - response = self._make_request(data).json() - if "error" in response: - raise Exception(response["error"]["message"]) - assert "result" in response, "No result in response on debug_traceCall" - return response["result"] + return self.rpc.debug_trace_call( + { + "from": f"{str(tr.transaction.sender)}", + "to": f"{str(tr.transaction.to)}", + "data": f"{str(tr.transaction.data)}", + }, + tr.block_number, + ) PYTEST_TEMPLATE = """ diff --git a/src/ethereum_test_tools/rpc/rpc.py b/src/ethereum_test_tools/rpc/rpc.py index 8b6393482f..a5ebd30247 100644 --- a/src/ethereum_test_tools/rpc/rpc.py +++ b/src/ethereum_test_tools/rpc/rpc.py @@ -18,14 +18,12 @@ class BaseRPC(ABC): Represents a base RPC class for every RPC call used within EEST based hive simulators. """ - def __init__(self, client_ip: str, port: int): - self.ip = client_ip - self.url = f"http://{client_ip}:{port}" + def __init__(self, url: str, extra_headers: Optional[Dict] = None): + self.url = url + self.extra_headers = extra_headers @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, max=10)) - def post_request( - self, method: str, params: List[Any], extra_headers: Optional[Dict] = None - ) -> Dict: + def post_request(self, method: str, params: List[Any]) -> Dict: """ Sends a JSON-RPC POST request to the client RPC server at port defined in the url. """ @@ -38,11 +36,15 @@ def post_request( base_header = { "Content-Type": "application/json", } - headers = base_header if extra_headers is None else {**base_header, **extra_headers} + headers = ( + base_header if self.extra_headers is None else {**base_header, **self.extra_headers} + ) response = requests.post(self.url, json=payload, headers=headers) response.raise_for_status() - result = response.json().get("result") + response_json = response.json() + assert "result" in response_json, "RPC response didn't contain a result field" + result = response_json["result"] if result is None or "error" in result: error_info = "result is None; and therefore contains no error info" @@ -63,11 +65,11 @@ class EthRPC(BaseRPC): hive simulators. """ - def __init__(self, client_ip): + def __init__(self, url: str, extra_headers: Optional[Dict] = None): """ Initializes the EthRPC class with the http port 8545, which requires no authentication. """ - super().__init__(client_ip, port=8545) + super().__init__(url, extra_headers=extra_headers) BlockNumberType = Union[int, Literal["latest", "earliest", "pending"]] @@ -92,6 +94,12 @@ def get_transaction_count(self, address: Address, block_number: BlockNumberType block = hex(block_number) if isinstance(block_number, int) else block_number return self.post_request("eth_getTransactionCount", [address, block]) + def get_transaction_by_hash(self, transaction_hash: str): + """ + `eth_getTransactionByHash`: Returns transaction details. + """ + return self.post_request("eth_getTransactionByHash", [f"{transaction_hash}"]) + def get_storage_at( self, address: str, position: str, block_number: BlockNumberType = "latest" ): @@ -113,3 +121,14 @@ def storage_at_keys( storage_value = self.get_storage_at(account, key, block_number) results[key] = storage_value return results + + def debug_trace_call(self, tr: dict[str, str], block_number: str): + """ + `debug_traceCall`: Returns pre state required for transaction + """ + params = [ + tr, + block_number, + {"tracer": "prestateTracer"}, + ] + return self.post_request("debug_traceCall", params) diff --git a/tests_consume/test_via_rlp.py b/tests_consume/test_via_rlp.py index 2f59fc8a46..f86a253bc9 100644 --- a/tests_consume/test_via_rlp.py +++ b/tests_consume/test_via_rlp.py @@ -190,7 +190,7 @@ def eth_rpc(client: Client) -> EthRPC: """ Initialize ethereum RPC client for the execution client under test. """ - return EthRPC(client_ip=client.ip) + return EthRPC(url=f"http://{client.ip}:8545") def compare_models(expected: FixtureHeader, got: FixtureHeader) -> dict: