-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Parse revert reason #1585
Parse revert reason #1585
Conversation
0a74c2c
to
a41c527
Compare
Is this CI failure really caused by this PR? I don't see how the change could cause that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this change @karlb! I added a long comment about where I think the logic should reside. I think it would be good to add an integration test if you feel up to it, but I'm happy to add one if you don't. Let me know when this is ready for review!
@kclowes I'll gladly take you up on the offer of adding an integration test. I don't see how to fit it in nicely, right now. I'll take care of the other change tomorrow. |
Sounds good, thank you! |
I've moved the code to I'll probably have to fix something when there's a proper integration test, since I didn't test across different eth clients, yet. |
@karlb I will keep digging into this tomorrow. I realized I read the original issue wrong and misdirected you towards putting the formatting in I also don't think we need an integration test since |
It's not accidentally truncated. But I think web3py should detect reverts, parse the revert reason and raise that information as a proper Exception in that case, too. Getting SolidityError('not allowed to monitor'') is much more useful than 'Reverted 0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000166e6f7420616c6c6f77656420746f206d6f6e69746f7200000000000000000000'
Yes, eth-tester is special. IIRC, it does not return errors as the other eth clients do, but directly raises python exceptions on transaction failures. This is usually very helpful, but for this specific PR it is causing additional work, since it is just an additional case to handle.
Thanks! What is the easiest way (on a linux machine) to run that test trough geth and parity in addition to eth-tester (which seems to be what's used when I just use default pytest)? |
Yes, I agree!
In an effort to be backend-agnostic it changes errors that are specific to one EVM (like Revert) to a TransactionFailed error.
I think we'd have to do something like this using the revert contract, then add it to the geth fixture at the end of the web3.py/tests/integration/parity/conftest.py Line 127 in 6ce485f
module_testing files. I will probably be able to get to that this week, or if you get to it first, that's great. Let me know if you need more clarification!
|
One sensible addition to the RevertContract could be a revert without message, so that we can test that case, too.
I'm now catching the eth-tester exception to create the result I intended. We'll see whether that lines up with the results from other backends. I didn't find any time to look at setting up geth and parity. If you take care of that, I'll gladly have a look at any new failures. |
Good idea. I'll add that too.
Sounds good, thank you! |
Is it going to be merged soon? If not @karlb, can I continue your work on this PR? |
@karlb I just finished up the other PR that I was working on, so I should be able to get to this on Monday. Thanks for your patience! |
@karlb I was able to make some progress on this today. I got the geth integration fixture up with the revert contract, and a simple test written, but I haven't had time to figure out why it is failing. I don't yet have the parity tests running either, but I should be able to keep working on it tomorrow. I also can't remember how I got code to you last time, so feel free to take or leave the commits I pushed to this branch! |
Unfortunately, I don't get the same error as in the CI locally:
Full output: error.txt Apparently I get back
Please just push everything related into this branch! |
7772beb
to
528591a
Compare
528591a
to
12aae2e
Compare
Got some cleanup to do, but at long last, I think the base functionality is there 🎉 One stylistic choice to highlight: I applied Geth's convention to format all revert messages as |
Awesome!!! I think Geth's convention is good, especially since we're raising the same |
059e61a
to
3071442
Compare
Ready for review @kclowes (and anyone else interested).
Thanks! 🙏 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hooray! This will be so great to get in! I had mostly nitpicky comments and one about using assert
in the code.
Also, because I can't comment on the file, I don't think we want to commit tests/integration/tester.zip
:)
9d6aa67
to
9204d84
Compare
Hi everyone! I'm having an issue connected to this PR (#1781) |
@sobolev-igor just leaving space for any final reviews. Expecting to merge later today or tomorrow, but next release is TBD. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me! I just left a couple of thoughts on the style here & there, but nothing critical.
return '' | ||
|
||
reason_length = int(data[len(prefix):len(prefix) + 64], 16) | ||
reason = data[len(prefix) + 64:len(prefix) + 64 + reason_length * 2] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This all feels fragile. That might be "ok" but it still worries me. Any thoughts on just doing the simple check that the data contains "Reverted "
and then skipping this parsing logic and embedding the whole response message in the revert exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be misunderstanding, but I'd definitely like to include the parsed revert message and not put that on the end user's shoulders. Are you suggesting returning both the reason and the full error dict, just the error dict, or something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current thinking in support of :
- in related issues filed, users are generally looking for the plaintext reason.
- handling reverts is broken today, so even brittle parsing logic is an improvement.
- I don't expect clients to change this formatting on a whim.
- we can iterate on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm proposing that we return the string which we are trying to parse in full. basically this whole context block would become:
if data.startswith("Reverted "):
reason = data
I don't feel strongly about this. It just seems like it would be roughly the same usefulness to the user and would avoid the part that looks brittle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes good sense, but it feels like signing up for an endless trickle of "what is this? how do I parse this?" questions. I'd prefer to sign up for patching the logic on the hopefully very rare occasions required and deliver the superior UX. Will flag this as something to harden though.
ee93a31
to
e5237c6
Compare
e5237c6
to
33b72d4
Compare
class SolidityError(ValueError): | ||
# Inherits from ValueError for backwards compatibility | ||
""" | ||
Raised on a solidity require/revert | ||
""" | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a breaking change to the API of expected failures for web3. What was the reasoning for intercepting TransactionFailed
failures and converting to this error (which also seems poorly named, perhaps class AssertionError(TransactionFailed)
would be better considering Solidity isn't the only smart contract language out there that has an assertion feature e.g. Vyper, Fe, etc.)
What was wrong?
Solidity allows messages in
require
statements which can help a lotwhen debugging. This commit parses the revert reasons from failing
call
s and raises them asSolidityError
exceptions.Closes #941.
Todo:
eth-utils
(to account for thereplace_exceptions
change)Cute Animal Picture