From bb13367106119a4283206ef15083ea1fe0af05dc Mon Sep 17 00:00:00 2001 From: Vikash Bajaj Date: Thu, 22 Feb 2018 23:49:34 -0700 Subject: [PATCH 1/3] added docs entry for new waitForTransactionReceipt method in web3.eth --- docs/web3.eth.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/web3.eth.rst b/docs/web3.eth.rst index 6259f668ed..4eacc6bb56 100644 --- a/docs/web3.eth.rst +++ b/docs/web3.eth.rst @@ -328,6 +328,31 @@ The following methods are available on the ``web3.eth`` namespace. }) +.. py:method:: Eth.waitForTransactionReceipt(transaction_hash) + + * Delegates to ``wait_for_transaction_receipt`` Method from ``web3.utils.transactions`` + + Returns the transaction receipt specified by ``transaction_hash``. It waits for transaction to mine before returning the receipt. + + .. code-block:: python + + >>> web3.eth.waitForTransactionReceipt('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') # not yet mined + # waits for transaction to mine before returning transaction data + AttributeDict({ + 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', + 'blockNumber': 46147, + 'contractAddress': None, + 'cumulativeGasUsed': 21000, + 'from': '0xa1e4380a3b1f749673e270229993ee55f35663b4', + 'gasUsed': 21000, + 'logs': [], + 'root': '96a8e009d2b88b1483e6941e6812e32263b05683fac202abc622a3e31aed1957', + 'to': '0x5df9b87991262f6ba471f09758cde1c0fc1de734', + 'transactionHash': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', + 'transactionIndex': 0, + }) + + .. py:method:: Eth.getTransactionReceipt(transaction_hash) * Delegates to ``eth_getTransactionReceipt`` RPC Method From 9538777e06e8bb6b810c39330515aee14c1bdd43 Mon Sep 17 00:00:00 2001 From: Vikash Bajaj Date: Fri, 9 Mar 2018 01:40:57 -0700 Subject: [PATCH 2/3] added test for new waitForTransaction method in web3.eth --- conftest.py | 1 + tests/core/eth-module/test_transactions.py | 19 ++++++++++++++++++- web3/eth.py | 4 ++++ .../stimulate_unmined_transaction.py | 16 ++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 web3/middleware/stimulate_unmined_transaction.py diff --git a/conftest.py b/conftest.py index 9b5e56ef9b..f757a21614 100644 --- a/conftest.py +++ b/conftest.py @@ -111,3 +111,4 @@ def web3(): w3.eth.enable_unaudited_features() return w3 + diff --git a/tests/core/eth-module/test_transactions.py b/tests/core/eth-module/test_transactions.py index 4489f2f0de..faff29bb03 100644 --- a/tests/core/eth-module/test_transactions.py +++ b/tests/core/eth-module/test_transactions.py @@ -3,6 +3,9 @@ from web3.exceptions import ( ValidationError, ) +from web3.middleware.stimulate_unmined_transaction import ( + unmined_receipt_simulator_middleware, +) @pytest.mark.parametrize( @@ -31,7 +34,6 @@ def test_send_transaction_with_valid_chain_id(web3, make_chain_id, expect_succes 'to': web3.eth.accounts[1], 'chainId': make_chain_id(web3), } - if expect_success: # just be happy that we didn't crash web3.eth.sendTransaction(transaction) @@ -40,3 +42,18 @@ def test_send_transaction_with_valid_chain_id(web3, make_chain_id, expect_succes web3.eth.sendTransaction(transaction) assert 'chain ID' in str(exc_info.value) + + +def test_unmined_transaction_wait_for_receipt(web3, extra_accounts): + web3.middleware_stack.add(unmined_receipt_simulator_middleware) + txn_hash = web3.eth.sendTransaction({ + 'from': web3.eth.coinbase, + 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + 'value': 123457 + }) + assert web3.eth.getTransactionReceipt(txn_hash) is None + assert web3.eth.getTransactionReceipt(txn_hash) is None + txn_receipt = web3.eth.getTransactionReceipt(txn_hash) + print(txn_receipt) + assert txn_receipt['transactionHash'] == txn_hash + assert txn_receipt['blockHash'] diff --git a/web3/eth.py b/web3/eth.py index 2ebf2739c0..0d5e0b6137 100644 --- a/web3/eth.py +++ b/web3/eth.py @@ -202,6 +202,10 @@ def getTransactionFromBlock(self, block_identifier, transaction_index): [block_identifier, transaction_index], ) + def waitForTransactionReceipt(self, transaction_hash, timeout=120): + txn_receipt = wait_for_transaction_receipt(self.web3, transaction_hash, timeout) + return txn_receipt + def getTransactionReceipt(self, transaction_hash): return self.web3.manager.request_blocking( "eth_getTransactionReceipt", diff --git a/web3/middleware/stimulate_unmined_transaction.py b/web3/middleware/stimulate_unmined_transaction.py new file mode 100644 index 0000000000..87ed371dfa --- /dev/null +++ b/web3/middleware/stimulate_unmined_transaction.py @@ -0,0 +1,16 @@ +import collections +import itertools + +counter = itertools.count() + + +def unmined_receipt_simulator_middleware(make_request, web3): + receipt_counters = collections.defaultdict(itertools.count) + + def middleware(method, params): + if method == 'eth_getTransactionReceipt': + txn_hash = params[0] + if next(receipt_counters[txn_hash]) < 2: + return {'result': None} + return make_request(method, params) + return middleware From 870339917fc1d820ef84ebcc2664a1fdf7ed1663 Mon Sep 17 00:00:00 2001 From: Jason Carver Date: Thu, 5 Apr 2018 17:46:59 -0700 Subject: [PATCH 3/3] Clean up waitForTransactionReceipt for merging - stimulate -> simulate - poll latency reduced to 100ms - plenty of invocations for test to try out - doc fixups --- conftest.py | 1 - docs/web3.eth.rst | 14 +++++++++----- tests/core/eth-module/test_transactions.py | 9 ++++----- web3/eth.py | 4 ++-- ...nsaction.py => simulate_unmined_transaction.py} | 9 +++++++-- web3/utils/transactions.py | 5 ++--- 6 files changed, 24 insertions(+), 18 deletions(-) rename web3/middleware/{stimulate_unmined_transaction.py => simulate_unmined_transaction.py} (60%) diff --git a/conftest.py b/conftest.py index f757a21614..9b5e56ef9b 100644 --- a/conftest.py +++ b/conftest.py @@ -111,4 +111,3 @@ def web3(): w3.eth.enable_unaudited_features() return w3 - diff --git a/docs/web3.eth.rst b/docs/web3.eth.rst index 4eacc6bb56..bdfda41eb7 100644 --- a/docs/web3.eth.rst +++ b/docs/web3.eth.rst @@ -328,16 +328,20 @@ The following methods are available on the ``web3.eth`` namespace. }) -.. py:method:: Eth.waitForTransactionReceipt(transaction_hash) +.. py:method:: Eth.waitForTransactionReceipt(transaction_hash, timeout=120) - * Delegates to ``wait_for_transaction_receipt`` Method from ``web3.utils.transactions`` + Waits for the transaction specified by ``transaction_hash`` to be included in a block, then + returns its transaction receipt. - Returns the transaction receipt specified by ``transaction_hash``. It waits for transaction to mine before returning the receipt. + Optionally, specify a ``timeout`` in seconds. If timeout elapses before the transaction + is added to a block, then :meth:`~Eth.waitForTransactionReceipt` returns None. .. code-block:: python - >>> web3.eth.waitForTransactionReceipt('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') # not yet mined - # waits for transaction to mine before returning transaction data + >>> web3.eth.waitForTransactionReceipt('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') + # If transaction is not yet in a block, time passes, while the thread sleeps... + # ... + # Then when the transaction is added to a block, its receipt is returned: AttributeDict({ 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', 'blockNumber': 46147, diff --git a/tests/core/eth-module/test_transactions.py b/tests/core/eth-module/test_transactions.py index faff29bb03..67c0a82205 100644 --- a/tests/core/eth-module/test_transactions.py +++ b/tests/core/eth-module/test_transactions.py @@ -3,7 +3,7 @@ from web3.exceptions import ( ValidationError, ) -from web3.middleware.stimulate_unmined_transaction import ( +from web3.middleware.simulate_unmined_transaction import ( unmined_receipt_simulator_middleware, ) @@ -52,8 +52,7 @@ def test_unmined_transaction_wait_for_receipt(web3, extra_accounts): 'value': 123457 }) assert web3.eth.getTransactionReceipt(txn_hash) is None - assert web3.eth.getTransactionReceipt(txn_hash) is None - txn_receipt = web3.eth.getTransactionReceipt(txn_hash) - print(txn_receipt) + + txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) assert txn_receipt['transactionHash'] == txn_hash - assert txn_receipt['blockHash'] + assert txn_receipt['blockHash'] is not None diff --git a/web3/eth.py b/web3/eth.py index 0d5e0b6137..90a01baeed 100644 --- a/web3/eth.py +++ b/web3/eth.py @@ -43,6 +43,7 @@ get_buffered_gas_estimate, get_required_transaction, replace_transaction, + wait_for_transaction_receipt, ) @@ -203,8 +204,7 @@ def getTransactionFromBlock(self, block_identifier, transaction_index): ) def waitForTransactionReceipt(self, transaction_hash, timeout=120): - txn_receipt = wait_for_transaction_receipt(self.web3, transaction_hash, timeout) - return txn_receipt + return wait_for_transaction_receipt(self.web3, transaction_hash, timeout) def getTransactionReceipt(self, transaction_hash): return self.web3.manager.request_blocking( diff --git a/web3/middleware/stimulate_unmined_transaction.py b/web3/middleware/simulate_unmined_transaction.py similarity index 60% rename from web3/middleware/stimulate_unmined_transaction.py rename to web3/middleware/simulate_unmined_transaction.py index 87ed371dfa..21b8321880 100644 --- a/web3/middleware/stimulate_unmined_transaction.py +++ b/web3/middleware/simulate_unmined_transaction.py @@ -3,6 +3,8 @@ counter = itertools.count() +INVOCATIONS_BEFORE_RESULT = 5 + def unmined_receipt_simulator_middleware(make_request, web3): receipt_counters = collections.defaultdict(itertools.count) @@ -10,7 +12,10 @@ def unmined_receipt_simulator_middleware(make_request, web3): def middleware(method, params): if method == 'eth_getTransactionReceipt': txn_hash = params[0] - if next(receipt_counters[txn_hash]) < 2: + if next(receipt_counters[txn_hash]) < INVOCATIONS_BEFORE_RESULT: return {'result': None} - return make_request(method, params) + else: + return make_request(method, params) + else: + return make_request(method, params) return middleware diff --git a/web3/utils/transactions.py b/web3/utils/transactions.py index bffc4a4615..b32587442f 100644 --- a/web3/utils/transactions.py +++ b/web3/utils/transactions.py @@ -1,5 +1,4 @@ import math -import random from cytoolz import ( assoc, @@ -50,13 +49,13 @@ def fill_transaction_defaults(web3, transaction): return merge(defaults, transaction) -def wait_for_transaction_receipt(web3, txn_hash, timeout=120): +def wait_for_transaction_receipt(web3, txn_hash, timeout=120, poll_latency=0.1): with Timeout(timeout) as _timeout: while True: txn_receipt = web3.eth.getTransactionReceipt(txn_hash) if txn_receipt is not None: break - _timeout.sleep(random.random()) + _timeout.sleep(poll_latency) return txn_receipt