Skip to content

added waitForTransactionReceipt to web3.eth which returns the transac… #669

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

Merged
merged 3 commits into from
Apr 6, 2018

Conversation

scottydelta
Copy link

…tion after its mined

What was wrong?

Fixes #574

How was it fixed?

Added waitForTransactionReceipt to web3.eth which waits for transaction to mine and returns the transaction once it is mined.

Cute Animal Picture

Cute animal picture

@scottydelta
Copy link
Author

Test and docs yet to be added. please add Work in Progress tag to it. thanks

@scottydelta
Copy link
Author

scottydelta commented Feb 23, 2018

how can I go about adding a test since it needs to have a pending transaction before it returns receipt? i did test it manually on Ropsten Network by creating a transaction manually and then running the function to test if it actually waits and returns the receipt after transaction is mined. thanks

@scottydelta scottydelta reopened this Feb 23, 2018
@carver
Copy link
Contributor

carver commented Feb 23, 2018

There is some new functionality in eth-tester to enable this. One approach would be something like:

eth_tester_provider = list(web3.providers).pop()
assert hasattr(eth_tester_provider, 'ethereum_tester')
eth_tester = eth_tester_provider.ethereum_tester

def disable_in_1s():
    time.sleep(1)
    eth_tester.enable_auto_mine_transactions()

eth_tester.disable_auto_mine_transactions()
txn_hash = w3.eth.sendTransaction(...)

with pytest.raises(TimeoutException):
    w3.eth.waitForTransactionReceipt(txn_hash, timeout=0.5)

Thread(disable_in_1s, daemon=True).start()

txn_receipt = eth.waitForTransactionReceipt(txn_hash, timeout=2)
assert txn_receipt is not None and txn_receipt['block_hash']

But yeah, it's a little hairy with the thread blocking. There's probably a better approach, but that would get it done.

@pipermerriam
Copy link
Member

@carver what do you think about testing via something like this using middleware to simulate a transaction not yet being mined.

counter = itertools.counter()
def unmined_receipt_simulator_middleware(make_request, web3):
    receipt_counters = collections.defaultdict(itertools.counter)

    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

@carver
Copy link
Contributor

carver commented Feb 23, 2018

Yes, I like that much better 👍 It's simpler, and takes less time to run.

For @scottydelta: itertools.count instead of counter

It would be nice to confirm that the middleware is working with a manual check of the empty result first, then the wait (but with a short timeout so that the test completes quickly even if it fails):

assert w3.eth.getTransactionReceipt(txn_hash) == None

receipt = w3.eth.waitForTransactionReceipt(txn_hash, timeout=1)
assert receipt['transaction_hash'] == txn_hash
assert receipt['block_hash']

@carver
Copy link
Contributor

carver commented Mar 1, 2018

You can run make lint-roll locally to cleanup and check for linting issues.

Based on the circle lint run, the only issue left is the ordering of the imports:

ERROR: /home/circleci/repo/web3/eth.py Imports are incorrectly sorted.
--- /home/circleci/repo/web3/eth.py:before	2018-02-26 07:03:47.343238
+++ /home/circleci/repo/web3/eth.py:after	2018-02-26 07:04:02.690568
@@ -41,9 +41,9 @@
     assert_valid_transaction_params,
     extract_valid_transaction_params,
     get_buffered_gas_estimate,
-    wait_for_transaction_receipt,
     get_required_transaction,
     replace_transaction,
+    wait_for_transaction_receipt,

@scottydelta
Copy link
Author

@carver I have fixed it on my end, just trying to make some time to add a test and complete this pull request. It will be done soon, thanks.

web3/eth.py Outdated
@@ -187,6 +188,10 @@ def getTransactionFromBlock(self, block_identifier, transaction_index):
[block_identifier, transaction_index],
)

def waitForTransactionReceipt(self, transaction_hash):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect this to have a timeout, like the underlying method.

Copy link
Author

@scottydelta scottydelta Mar 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had that in mind, missed it somehow. I will add that, thanks.

@scottydelta
Copy link
Author

@carver while I understand your initial suggested method of testing, I am finding @pipermerriam middleware method to stimulate un-mined transaction hard to understand. I haven't worked with tests alot.

is the actual test executed like this?

make_request = Mock()
txn_receipt = unmined_receipt_simulator_middleware(make_request, web3)
txn_hash = web3.eth.sendTransaction({
    'from': web3.eth.coinbase,
    'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601',
})
assert txn_receipt('eth_getTransactionReceipt', [txn_hash]) is None
..

@pipermerriam
Copy link
Member

@scottydelta

Not exactly. You would add the unmined_receipt_simulator_middleware to the web3 middlewares:

w3.middleware_stack.add(unmined_receipt_simulator_middleware)

Then you can call web3.eth.getTransactionReceipt and the first two calls will always return None, after which it will return the receipt.

@scottydelta
Copy link
Author

@pipermerriam thanks for the guidance. Wouldn't this test getTransactionReceipt instead of newly added method waitForTransactionReceipt? I know in the end getTransactionReceipt get called when waitForTransactionReceipt is invoked.

@pipermerriam
Copy link
Member

@scottydelta the middlewares don't know the difference between web3.eth.getTransactionReceipt and web3.eth.waitForTransactionReceipt. The only thing that middlewares are aware of are the underlying JSON-RPC methods.

So, by constructing this middleware which responds to eth_getTransactionReceipt JSON-RPC requests, you're emulating the behavior of an unmined transaction by intercepting the request and returning None.

So, the idea is that you are testing web3.eth.waitForTransactionReceipt by artificially creating the JSON-RPC behavior for an unmined transaction.

Make sense?

@scottydelta scottydelta reopened this Mar 14, 2018
@scottydelta
Copy link
Author

@pipermerriam It makes sense but the method waitForTransaction which I just added never gets tested, instead the JSON-RPC method eth_getTransactionReceipt gets tested which must already be getting tested in some other test. is that a normal behavior?

Copy link
Member

@pipermerriam pipermerriam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comments on how to change the tests.

'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601',
'value': 123457
})
assert web3.eth.getTransactionReceipt(txn_hash) is None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave this assertion in place. This verifies that the transaction is showing as unmined.

})
assert web3.eth.getTransactionReceipt(txn_hash) is None
assert web3.eth.getTransactionReceipt(txn_hash) is None
txn_receipt = web3.eth.getTransactionReceipt(txn_hash)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be changed to use the web3.eth.waitForTransactionReceipt API, and the line above this should be removed.

def middleware(method, params):
if method == 'eth_getTransactionReceipt':
txn_hash = params[0]
if next(receipt_counters[txn_hash]) < 2:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would go ahead and bump this number up to something like 5 to ensure that the waitForTransactionReceipt code hits this a few times prior to getting the actual receipt.

@carver
Copy link
Contributor

carver commented Apr 3, 2018

@scottydelta it looks like this is almost at the finish line. Are you able to work on it more?

- stimulate -> simulate
- poll latency reduced to 100ms
- plenty of invocations for test to try out
- doc fixups
@carver carver merged commit c25be34 into ethereum:master Apr 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add function w3.eth.waitForTransactionReceipt(tx_hash)
3 participants