Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions newsfragments/3646.performance.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
``parse_block_identifier`` / ``async_parse_block_identifier`` now forward a
block hash to the JSON-RPC node directly instead of resolving it to a block
number first. This removes an extra ``eth_getBlockBy*`` round-trip on every
contract call that targets a specific block hash.
27 changes: 15 additions & 12 deletions tests/core/contracts/test_contract_util_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ def test_parse_block_identifier_int_and_string(w3, block_identifier, expected_ou
def test_parse_block_identifier_bytes_and_hex(w3):
block_0 = w3.eth.get_block(0)
block_0_hash = block_0["hash"]
# test retrieve by bytes
block_id_by_hash = parse_block_identifier(w3, block_0_hash)
assert block_id_by_hash == 0
# test retrieve by hexstring
block_0_hexstring = w3.to_hex(block_0_hash)
block_id_by_hex = parse_block_identifier(w3, block_0_hexstring)
assert block_id_by_hex == 0
# Block hashes are passed through unchanged so the RPC node receives them
# directly. This avoids an extra eth_getBlockBy* round-trip just to look
# up a block number that the major clients already accept as a
# default-block parameter. See issue #3646.
assert parse_block_identifier(w3, block_0_hash) == block_0_hexstring
assert parse_block_identifier(w3, block_0_hexstring) == block_0_hexstring


@pytest.mark.parametrize(
Expand Down Expand Up @@ -98,13 +98,16 @@ async def test_async_parse_block_identifier_int_and_string(
async def test_async_parse_block_identifier_bytes_and_hex(async_w3):
block_0 = await async_w3.eth.get_block(0)
block_0_hash = block_0["hash"]
# test retrieve by bytes
block_id_by_hash = await async_parse_block_identifier(async_w3, block_0_hash)
assert block_id_by_hash == 0
# test retrieve by hexstring
block_0_hexstring = async_w3.to_hex(block_0_hash)
block_id_by_hex = await async_parse_block_identifier(async_w3, block_0_hexstring)
assert block_id_by_hex == 0
# See test_parse_block_identifier_bytes_and_hex for rationale.
assert (
await async_parse_block_identifier(async_w3, block_0_hash)
== block_0_hexstring
)
assert (
await async_parse_block_identifier(async_w3, block_0_hexstring)
== block_0_hexstring
)


@pytest.mark.asyncio
Expand Down
16 changes: 13 additions & 3 deletions web3/_utils/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
abi_ens_resolver,
abi_string_to_text,
)
from web3._utils.type_conversion import (
to_hex_if_bytes,
)
from web3.exceptions import (
BlockNumberOutOfRange,
Web3TypeError,
Expand Down Expand Up @@ -342,7 +345,13 @@ def parse_block_identifier(
elif isinstance(block_identifier, bytes) or is_hex_encoded_block_hash(
block_identifier
):
return w3.eth.get_block(block_identifier)["number"]
# Forward the block hash to the JSON-RPC call instead of resolving it to
# a block number first. The execution-apis spec lists block hashes as a
# valid default-block parameter and the major clients (geth, reth,
# erigon, nethermind) accept them for eth_call/eth_estimateGas, so we
# avoid an extra eth_getBlockBy* round-trip. If a node rejects the
# hash, the underlying RPC error propagates to the caller unchanged.
return to_hex_if_bytes(block_identifier)
else:
raise BlockNumberOutOfRange

Expand Down Expand Up @@ -370,8 +379,9 @@ async def async_parse_block_identifier(
elif isinstance(block_identifier, bytes) or is_hex_encoded_block_hash(
block_identifier
):
requested_block = await async_w3.eth.get_block(block_identifier)
return requested_block["number"]
# See parse_block_identifier for rationale. Pass the hash through so a
# single eth_call request is enough.
return to_hex_if_bytes(block_identifier)
else:
raise BlockNumberOutOfRange

Expand Down