Skip to content

Commit

Permalink
refactor(fw): extend EthRPC methods and use them in gentest (#568)
Browse files Browse the repository at this point in the history
Co-authored-by: Art <art@MacBook-Pro-Art.local>
Co-authored-by: danceratopz <danceratopz@gmail.com>
  • Loading branch information
3 people authored and spencer-tb committed Jun 26, 2024
1 parent 2783933 commit 3ebb981
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 68 deletions.
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
73 changes: 16 additions & 57 deletions src/cli/gentest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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"
Expand All @@ -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"],
Expand All @@ -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 = """
Expand Down
39 changes: 29 additions & 10 deletions src/ethereum_test_tools/rpc/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand All @@ -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"
Expand All @@ -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"]]

Expand All @@ -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"
):
Expand All @@ -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)
2 changes: 1 addition & 1 deletion tests_consume/test_via_rlp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 3ebb981

Please sign in to comment.