Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fw): Remove _make_request and replace using it to EthRPC methods #568

Merged
merged 11 commits into from
Jun 14, 2024
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):
danceratopz marked this conversation as resolved.
Show resolved Hide resolved
"""
`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)
danceratopz marked this conversation as resolved.
Show resolved Hide resolved
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