Skip to content

Commit 31c5126

Browse files
committed
Catch newly raised geth errors when tx indexing in progress:
- Geth ``1.13.11`` started to return an error when transaction indexing is in progress and a ``eth_getTransactionReceipt`` call is made. Handle this in the ``wait_for_transaction_receipt`` method by catching the error in order to continue waiting.
1 parent c7b9771 commit 31c5126

File tree

10 files changed

+107
-12
lines changed

10 files changed

+107
-12
lines changed

newsfragments/3217.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Handle new geth errors related to waiting for a transaction receipt while transactions are still being indexed.

tests/core/contracts/test_contract_panic_errors.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
from tests.core.contracts.utils import (
55
deploy,
66
)
7-
from web3._utils.contract_error_handling import (
8-
PANIC_ERROR_CODES,
9-
)
107
from web3._utils.contract_sources.contract_data.panic_errors_contract import (
118
PANIC_ERRORS_CONTRACT_DATA,
129
)
10+
from web3._utils.error_formatters_utils import (
11+
PANIC_ERROR_CODES,
12+
)
1313
from web3.exceptions import (
1414
ContractPanicError,
1515
)

tests/core/eth-module/test_transactions.py

+62
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
Web3ValidationError,
2525
)
2626
from web3.middleware import (
27+
async_construct_error_generator_middleware,
28+
async_construct_result_generator_middleware,
29+
construct_error_generator_middleware,
2730
construct_result_generator_middleware,
2831
)
2932
from web3.middleware.simulate_unmined_transaction import (
@@ -33,6 +36,16 @@
3336
RECEIPT_TIMEOUT = 0.2
3437

3538

39+
def _swap_error_middleware_for_result_middleware_and_return_error(
40+
w3, result_middleware
41+
):
42+
# On the first call, this will be in ``layer=0`` and we will remove this
43+
# middleware and add the result middleware to layer 0 for the next call.
44+
w3.middleware_onion.remove("error_middleware")
45+
w3.middleware_onion.inject(result_middleware, "result_middleware", layer=0)
46+
return {"code": -32000, "message": "transaction indexing in progress"}
47+
48+
3649
@pytest.mark.parametrize(
3750
"transaction",
3851
(
@@ -193,6 +206,28 @@ def test_unmined_transaction_wait_for_receipt(w3):
193206
assert txn_receipt["blockHash"] is not None
194207

195208

209+
def test_eth_wait_for_transaction_receipt_transaction_indexing_in_progress(w3):
210+
result_middleware = construct_result_generator_middleware(
211+
{RPC.eth_getTransactionReceipt: lambda *_: {"status": 1}}
212+
)
213+
error_middleware = construct_error_generator_middleware(
214+
{
215+
RPC.eth_getTransactionReceipt: lambda *_: (
216+
_swap_error_middleware_for_result_middleware_and_return_error(
217+
w3, result_middleware
218+
)
219+
)
220+
}
221+
)
222+
w3.middleware_onion.inject(error_middleware, "error_middleware", layer=0)
223+
224+
receipt = w3.eth.wait_for_transaction_receipt(f"0x{'00' * 32}")
225+
assert receipt == {"status": 1}
226+
227+
assert "error_middleware" not in w3.middleware_onion.middlewares
228+
w3.middleware_onion.remove("result_middleware")
229+
230+
196231
def test_get_transaction_formatters(w3):
197232
non_checksummed_addr = "0xB2930B35844A230F00E51431ACAE96FE543A0347" # all uppercase
198233
unformatted_transaction = {
@@ -300,3 +335,30 @@ def test_get_transaction_formatters(w3):
300335

301336
assert received_tx == expected
302337
w3.middleware_onion.remove("result_middleware")
338+
339+
340+
# --- async --- #
341+
342+
343+
@pytest.mark.asyncio
344+
async def test_async_wait_for_transaction_receipt_transaction_indexing_in_progress(
345+
async_w3,
346+
):
347+
result_middleware = await async_construct_result_generator_middleware(
348+
{RPC.eth_getTransactionReceipt: lambda *_: {"status": 1}}
349+
)
350+
error_middleware = await async_construct_error_generator_middleware(
351+
{
352+
RPC.eth_getTransactionReceipt: lambda *_: (
353+
_swap_error_middleware_for_result_middleware_and_return_error(
354+
async_w3, result_middleware
355+
)
356+
)
357+
}
358+
)
359+
async_w3.middleware_onion.inject(error_middleware, "error_middleware", layer=0)
360+
361+
receipt = await async_w3.eth.wait_for_transaction_receipt(f"0x{'00' * 32}")
362+
assert receipt == {"status": 1}
363+
364+
async_w3.middleware_onion.remove("result_middleware")

web3/_utils/contract_error_handling.py renamed to web3/_utils/error_formatters_utils.py

+19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
ContractLogicError,
1313
ContractPanicError,
1414
OffchainLookup,
15+
TransactionIndexingInProgress,
1516
)
1617
from web3.types import (
1718
RPCResponse,
@@ -164,3 +165,21 @@ def raise_contract_logic_error_on_revert(response: RPCResponse) -> RPCResponse:
164165
raise ContractLogicError("execution reverted", data=data)
165166

166167
return response
168+
169+
170+
def raise_transaction_indexing_error_if_indexing(response: RPCResponse) -> RPCResponse:
171+
"""
172+
Raise an error if ``eth_getTransactionReceipt`` returns an error indicating that
173+
transactions are still being indexed.
174+
"""
175+
176+
error = response.get("error")
177+
if not isinstance(error, str) and error is not None:
178+
message = error.get("message")
179+
if message is not None:
180+
if all(
181+
idx_key_phrases in message for idx_key_phrases in ("index", "progress")
182+
):
183+
raise TransactionIndexingInProgress(message)
184+
185+
return response

web3/_utils/method_formatters.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,14 @@
5252
from web3._utils.abi import (
5353
is_length,
5454
)
55-
from web3._utils.contract_error_handling import (
56-
raise_contract_logic_error_on_revert,
57-
)
5855
from web3._utils.encoding import (
5956
hexstr_if_str,
6057
to_hex,
6158
)
59+
from web3._utils.error_formatters_utils import (
60+
raise_contract_logic_error_on_revert,
61+
raise_transaction_indexing_error_if_indexing,
62+
)
6263
from web3._utils.filters import (
6364
AsyncBlockFilter,
6465
AsyncLogFilter,
@@ -791,6 +792,7 @@ def subscription_formatter(value: Any) -> Union[HexBytes, HexStr, Dict[str, Any]
791792
ERROR_FORMATTERS: Dict[RPCEndpoint, Callable[..., Any]] = {
792793
RPC.eth_estimateGas: raise_contract_logic_error_on_revert,
793794
RPC.eth_call: raise_contract_logic_error_on_revert,
795+
RPC.eth_getTransactionReceipt: raise_transaction_indexing_error_if_indexing,
794796
}
795797

796798

web3/_utils/module_testing/eth_module.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@
4040
HexBytes,
4141
)
4242

43-
from web3._utils.contract_error_handling import (
44-
PANIC_ERROR_CODES,
45-
)
4643
from web3._utils.empty import (
4744
empty,
4845
)
4946
from web3._utils.ens import (
5047
ens_addresses,
5148
)
49+
from web3._utils.error_formatters_utils import (
50+
PANIC_ERROR_CODES,
51+
)
5252
from web3._utils.method_formatters import (
5353
to_hex_if_integer,
5454
)

web3/eth/async_eth.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
OffchainLookup,
6161
TimeExhausted,
6262
TooManyRequests,
63+
TransactionIndexingInProgress,
6364
TransactionNotFound,
6465
)
6566
from web3.method import (
@@ -517,7 +518,7 @@ async def _wait_for_tx_receipt_with_timeout(
517518
while True:
518519
try:
519520
tx_receipt = await self._transaction_receipt(_tx_hash)
520-
except TransactionNotFound:
521+
except (TransactionNotFound, TransactionIndexingInProgress):
521522
tx_receipt = None
522523
if tx_receipt is not None:
523524
break

web3/eth/eth.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
OffchainLookup,
6161
TimeExhausted,
6262
TooManyRequests,
63+
TransactionIndexingInProgress,
6364
TransactionNotFound,
6465
)
6566
from web3.method import (
@@ -485,7 +486,7 @@ def wait_for_transaction_receipt(
485486
while True:
486487
try:
487488
tx_receipt = self._transaction_receipt(transaction_hash)
488-
except TransactionNotFound:
489+
except (TransactionNotFound, TransactionIndexingInProgress):
489490
tx_receipt = None
490491
if tx_receipt is not None:
491492
break

web3/exceptions.py

+9
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,15 @@ class TransactionNotFound(Web3Exception):
223223
pass
224224

225225

226+
class TransactionIndexingInProgress(Web3Exception):
227+
"""
228+
Raised when a transaction receipt is not yet available due to transaction indexing
229+
still being in progress.
230+
"""
231+
232+
pass
233+
234+
226235
class BlockNotFound(Web3Exception):
227236
"""
228237
Raised when the block id used to lookup a block in a jsonrpc call cannot be found.

web3/providers/eth_tester/defaults.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from web3 import (
4646
Web3,
4747
)
48-
from web3._utils.contract_error_handling import (
48+
from web3._utils.error_formatters_utils import (
4949
OFFCHAIN_LOOKUP_FIELDS,
5050
PANIC_ERROR_CODES,
5151
)

0 commit comments

Comments
 (0)