Skip to content

Commit 0ecffd7

Browse files
author
Paul Robinson
committed
fix bug in how eth_tester middleware filled default fields (ethereum#2600)
1 parent ce2793a commit 0ecffd7

File tree

3 files changed

+205
-9
lines changed

3 files changed

+205
-9
lines changed

newsfragments/2600.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fixed bug in how async_eth_tester_middleware fills default fields

tests/core/middleware/test_eth_tester_middleware.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
import pytest
2+
from unittest.mock import (
3+
Mock,
4+
)
25

6+
from web3.providers.eth_tester.middleware import (
7+
async_default_transaction_fields_middleware,
8+
default_transaction_fields_middleware,
9+
)
310
from web3.types import (
411
BlockData,
512
)
613

14+
SAMPLE_ADDRESS_LIST = [
15+
"0x0000000000000000000000000000000000000001",
16+
"0x0000000000000000000000000000000000000002",
17+
"0x0000000000000000000000000000000000000003",
18+
]
19+
SAMPLE_ADDRESS = "0x0000000000000000000000000000000000000004"
20+
721

822
@pytest.mark.parametrize("block_number", {0, "0x0", "earliest"})
923
def test_get_transaction_count_formatters(w3, block_number):
@@ -20,3 +34,164 @@ def test_get_block_formatters(w3):
2034
keys_diff = all_block_keys.difference(latest_block_keys)
2135
assert len(keys_diff) == 1
2236
assert keys_diff.pop() == "mixHash" # mixHash is not implemented in eth-tester
37+
38+
39+
@pytest.mark.parametrize(
40+
"w3_accounts, w3_coinbase, method, from_field_added, from_field_value",
41+
(
42+
(SAMPLE_ADDRESS_LIST, SAMPLE_ADDRESS, "eth_call", True, SAMPLE_ADDRESS),
43+
(
44+
SAMPLE_ADDRESS_LIST,
45+
SAMPLE_ADDRESS,
46+
"eth_estimateGas",
47+
True,
48+
SAMPLE_ADDRESS,
49+
),
50+
(
51+
SAMPLE_ADDRESS_LIST,
52+
SAMPLE_ADDRESS,
53+
"eth_sendTransaction",
54+
True,
55+
SAMPLE_ADDRESS,
56+
),
57+
(SAMPLE_ADDRESS_LIST, SAMPLE_ADDRESS, "eth_gasPrice", False, None),
58+
(SAMPLE_ADDRESS_LIST, SAMPLE_ADDRESS, "eth_blockNumber", False, None),
59+
(SAMPLE_ADDRESS_LIST, SAMPLE_ADDRESS, "meow", False, None),
60+
(SAMPLE_ADDRESS_LIST, None, "eth_call", True, SAMPLE_ADDRESS_LIST[0]),
61+
(SAMPLE_ADDRESS_LIST, None, "eth_estimateGas", True, SAMPLE_ADDRESS_LIST[0]),
62+
(
63+
SAMPLE_ADDRESS_LIST,
64+
None,
65+
"eth_sendTransaction",
66+
True,
67+
SAMPLE_ADDRESS_LIST[0],
68+
),
69+
(SAMPLE_ADDRESS_LIST, None, "eth_gasPrice", False, None),
70+
(SAMPLE_ADDRESS_LIST, None, "eth_blockNumber", False, None),
71+
(SAMPLE_ADDRESS_LIST, None, "meow", False, None),
72+
(None, SAMPLE_ADDRESS, "eth_call", True, SAMPLE_ADDRESS),
73+
(None, SAMPLE_ADDRESS, "eth_estimateGas", True, SAMPLE_ADDRESS),
74+
(None, SAMPLE_ADDRESS, "eth_sendTransaction", True, SAMPLE_ADDRESS),
75+
(None, SAMPLE_ADDRESS, "eth_gasPrice", False, SAMPLE_ADDRESS),
76+
(None, SAMPLE_ADDRESS, "eth_blockNumber", False, SAMPLE_ADDRESS),
77+
(None, SAMPLE_ADDRESS, "meow", False, SAMPLE_ADDRESS),
78+
(None, None, "eth_call", True, None),
79+
(None, None, "eth_estimateGas", True, None),
80+
(None, None, "eth_sendTransaction", True, None),
81+
(None, None, "eth_gasPrice", False, None),
82+
(None, None, "eth_blockNumber", False, None),
83+
(None, None, "meow", False, None),
84+
),
85+
)
86+
def test_default_transaction_fields_middleware(
87+
w3_accounts, w3_coinbase, method, from_field_added, from_field_value
88+
):
89+
def mock_request(_method, params):
90+
return params
91+
92+
mock_w3 = Mock()
93+
mock_w3.eth.accounts = w3_accounts
94+
mock_w3.eth.coinbase = w3_coinbase
95+
96+
middleware = default_transaction_fields_middleware(mock_request, mock_w3)
97+
base_params = {"chainId": 5}
98+
filled_transaction = middleware(method, [base_params])
99+
100+
filled_params = filled_transaction[0]
101+
102+
assert ("from" in filled_params.keys()) == from_field_added
103+
if "from" in filled_params.keys():
104+
assert filled_params["from"] == from_field_value
105+
106+
filled_transaction[0].pop("from", None)
107+
assert filled_transaction[0] == base_params
108+
109+
110+
# -- async -- #
111+
112+
113+
@pytest.mark.parametrize(
114+
"w3_accounts, w3_coinbase, method, from_field_added, from_field_value",
115+
(
116+
(SAMPLE_ADDRESS_LIST, SAMPLE_ADDRESS, "eth_call", True, SAMPLE_ADDRESS),
117+
(
118+
SAMPLE_ADDRESS_LIST,
119+
SAMPLE_ADDRESS,
120+
"eth_estimateGas",
121+
True,
122+
SAMPLE_ADDRESS,
123+
),
124+
(
125+
SAMPLE_ADDRESS_LIST,
126+
SAMPLE_ADDRESS,
127+
"eth_sendTransaction",
128+
True,
129+
SAMPLE_ADDRESS,
130+
),
131+
(SAMPLE_ADDRESS_LIST, SAMPLE_ADDRESS, "eth_gasPrice", False, None),
132+
(SAMPLE_ADDRESS_LIST, SAMPLE_ADDRESS, "eth_blockNumber", False, None),
133+
(SAMPLE_ADDRESS_LIST, SAMPLE_ADDRESS, "meow", False, None),
134+
(SAMPLE_ADDRESS_LIST, None, "eth_call", True, SAMPLE_ADDRESS_LIST[0]),
135+
(SAMPLE_ADDRESS_LIST, None, "eth_estimateGas", True, SAMPLE_ADDRESS_LIST[0]),
136+
(
137+
SAMPLE_ADDRESS_LIST,
138+
None,
139+
"eth_sendTransaction",
140+
True,
141+
SAMPLE_ADDRESS_LIST[0],
142+
),
143+
(SAMPLE_ADDRESS_LIST, None, "eth_gasPrice", False, None),
144+
(SAMPLE_ADDRESS_LIST, None, "eth_blockNumber", False, None),
145+
(SAMPLE_ADDRESS_LIST, None, "meow", False, None),
146+
(None, SAMPLE_ADDRESS, "eth_call", True, SAMPLE_ADDRESS),
147+
(None, SAMPLE_ADDRESS, "eth_estimateGas", True, SAMPLE_ADDRESS),
148+
(None, SAMPLE_ADDRESS, "eth_sendTransaction", True, SAMPLE_ADDRESS),
149+
(None, SAMPLE_ADDRESS, "eth_gasPrice", False, SAMPLE_ADDRESS),
150+
(None, SAMPLE_ADDRESS, "eth_blockNumber", False, SAMPLE_ADDRESS),
151+
(None, SAMPLE_ADDRESS, "meow", False, SAMPLE_ADDRESS),
152+
(None, None, "eth_call", True, None),
153+
(None, None, "eth_estimateGas", True, None),
154+
(None, None, "eth_sendTransaction", True, None),
155+
(None, None, "eth_gasPrice", False, None),
156+
(None, None, "eth_blockNumber", False, None),
157+
(None, None, "meow", False, None),
158+
),
159+
)
160+
@pytest.mark.asyncio
161+
async def test_async_default_transaction_fields_middleware(
162+
w3_accounts,
163+
w3_coinbase,
164+
method,
165+
from_field_added,
166+
from_field_value,
167+
):
168+
async def mock_request(_method, params):
169+
return params
170+
171+
async def mock_async_accounts():
172+
return w3_accounts
173+
174+
async def mock_async_coinbase():
175+
return w3_coinbase
176+
177+
mock_w3 = Mock()
178+
mock_w3.eth.accounts = mock_async_accounts()
179+
mock_w3.eth.coinbase = mock_async_coinbase()
180+
181+
middleware = await async_default_transaction_fields_middleware(
182+
mock_request, mock_w3
183+
)
184+
base_params = {"chainId": 5}
185+
filled_transaction = await middleware(method, [base_params])
186+
187+
filled_params = filled_transaction[0]
188+
assert ("from" in filled_params.keys()) == from_field_added
189+
if "from" in filled_params.keys():
190+
assert filled_params["from"] == from_field_value
191+
192+
filled_transaction[0].pop("from", None)
193+
assert filled_transaction[0] == base_params
194+
195+
# clean up
196+
mock_w3.eth.accounts.close()
197+
mock_w3.eth.coinbase.close()

web3/providers/eth_tester/middleware.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -315,15 +315,21 @@ async def async_ethereum_tester_middleware( # type: ignore
315315

316316

317317
def guess_from(w3: "Web3", _: TxParams) -> ChecksumAddress:
318-
coinbase = w3.eth.coinbase
318+
if w3.eth.coinbase:
319+
return w3.eth.coinbase
320+
elif w3.eth.accounts and len(w3.eth.accounts) > 0:
321+
return w3.eth.accounts[0]
322+
323+
return None
324+
325+
326+
async def async_guess_from(async_w3: "Web3", _: TxParams) -> ChecksumAddress:
327+
coinbase = await async_w3.eth.coinbase # type: ignore
328+
accounts = await async_w3.eth.accounts # type: ignore
319329
if coinbase is not None:
320330
return coinbase
321-
322-
try:
323-
return w3.eth.accounts[0]
324-
except KeyError:
325-
# no accounts available to pre-fill, carry on
326-
pass
331+
elif accounts is not None and len(accounts) > 0:
332+
return accounts[0]
327333

328334
return None
329335

@@ -340,6 +346,18 @@ def fill_default(
340346
return assoc(transaction, field, guess_val)
341347

342348

349+
@curry
350+
async def async_fill_default(
351+
field: str, guess_func: Callable[..., Any], async_w3: "Web3", transaction: TxParams
352+
) -> TxParams:
353+
# type ignored b/c TxParams keys must be string literal types
354+
if field in transaction and transaction[field] is not None: # type: ignore
355+
return transaction
356+
else:
357+
guess_val = await guess_func(async_w3, transaction)
358+
return assoc(transaction, field, guess_val)
359+
360+
343361
def default_transaction_fields_middleware(
344362
make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3"
345363
) -> Callable[[RPCEndpoint, Any], RPCResponse]:
@@ -363,15 +381,17 @@ def middleware(method: RPCEndpoint, params: Any) -> RPCResponse:
363381

364382

365383
async def async_default_transaction_fields_middleware(
366-
make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3"
384+
make_request: Callable[[RPCEndpoint, Any], Any], async_w3: "Web3"
367385
) -> Callable[[RPCEndpoint, Any], RPCResponse]:
368386
async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse:
369387
if method in (
370388
"eth_call",
371389
"eth_estimateGas",
372390
"eth_sendTransaction",
373391
):
374-
filled_transaction = fill_default("from", guess_from, web3, params[0])
392+
filled_transaction = await async_fill_default(
393+
"from", async_guess_from, async_w3, params[0]
394+
)
375395
return await make_request(method, [filled_transaction] + list(params)[1:])
376396
else:
377397
return await make_request(method, params)

0 commit comments

Comments
 (0)