Skip to content
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

Raw revert for custom errors #3136

Merged
merged 21 commits into from
Nov 10, 2022
Merged

Conversation

z80dev
Copy link
Contributor

@z80dev z80dev commented Oct 31, 2022

What I did

Added raw_revert as a builtin function, working similarly to raw_log and raw_call

How I did it

Created a new RawRevert class. This represents a built-in function that stores any data passed to it to memory, and then passes the right offset and size to revert in IR.

How to verify it

Run ./quicktest.sh tests/parser/features/test_reverting.py

I also tested this in Foundry using VyperDeployer and verified that custom errors coming from solidity and the equivalent vyper raw_revert arguments created the same error data

Commit message

Implement raw_revert built-in function

Description for the changelog

Add raw_revert

Cute Animal Picture

Put a link to a cute animal picture inside the parenthesis-->

@codecov-commenter
Copy link

codecov-commenter commented Oct 31, 2022

Codecov Report

Merging #3136 (9d25627) into master (b13595f) will decrease coverage by 0.27%.
The diff coverage is 95.23%.

@@            Coverage Diff             @@
##           master    #3136      +/-   ##
==========================================
- Coverage   88.49%   88.21%   -0.28%     
==========================================
  Files          95       95              
  Lines       10760    10777      +17     
  Branches     2266     2268       +2     
==========================================
- Hits         9522     9507      -15     
- Misses        796      829      +33     
+ Partials      442      441       -1     
Impacted Files Coverage Δ
vyper/builtin_functions/functions.py 89.92% <94.44%> (-0.62%) ⬇️
vyper/codegen/external_call.py 97.36% <100.00%> (ø)
.../codegen/function_definitions/external_function.py 100.00% <100.00%> (ø)
vyper/codegen/stmt.py 88.16% <100.00%> (ø)
vyper/codegen/arithmetic.py 74.35% <0.00%> (-10.26%) ⬇️
vyper/semantics/types/value/numeric.py 80.45% <0.00%> (-4.60%) ⬇️
vyper/semantics/types/function.py 86.51% <0.00%> (-0.66%) ⬇️
vyper/ast/nodes.py 93.05% <0.00%> (-0.17%) ⬇️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

Comment on lines 12 to 22
def build_revert_bytestring(errorDecl, *data):
# revert bytes should be 4-byte selector (from keccak of error definition)
# followed by abi-encoded data
error_selector = keccak256(bytes(errorDecl, "utf-8"))[:4]
match = re.search(r"\((.*?)\)", errorDecl) # .group(1)
if match and match.group(1) != "":
arg_types = match.group(1)
encoded_data = encode(arg_types.split(","), data)
return b"".join([error_selector, encoded_data])
else:
return error_selector
Copy link
Member

Choose a reason for hiding this comment

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

you probably want abi.encode_single, not abi.encode -- the latter is going to encode them individually, instead of together as a tuple (the encodings are different)

Copy link
Contributor

Choose a reason for hiding this comment

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

That is now deprecated. So use encode wherever possible instead

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will add a test case encoding some more complex data and see how it handles it

Copy link
Member

@charles-cooper charles-cooper Nov 1, 2022

Choose a reason for hiding this comment

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

honestly, i don't even think it needs to be that complicated, since we don't really need to test arbitrary/generic revert data generation. when i originally said to construct the revert data using utility functions, i was thinking more just like method_id("NoFives()") + (5).to_bytes(32, "big"). (the point is for it to be clear, not necessarily generic)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

got it, I had to implement my own method_id function but otherwise followed what you said and made it simpler

Comment on lines 9 to 10
def method_id(method_str: str) -> bytes:
return keccak256(bytes(method_str, "utf-8"))[:4]
Copy link
Member

Choose a reason for hiding this comment

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

this feels like it should go in vyper/utils.py. i'm ok with it as it is as long as there is a note like # TODO should move to utils.py

Copy link
Contributor Author

Choose a reason for hiding this comment

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

moving it there makes sense imo, but there's already a function in there called abi_method_id

the difference between that one and this one is that abi_method_id returns an int instead of bytes. this probably works in other places in the compiler, but it breaks something in the usage here.

when method_id returns bytes, we can concatenate them to other bytes like we do in the test:

revert_bytes = method_id("NoFives(uint256)") + (5).to_bytes(32, "big")

with a returned integer, this doesn't work

I think a function called method_id/abi_method_id should return bytes, and we should rename abi_method_id to something like method_id_int. happy to take suggestions on naming convention here, I'm not the best at coming up with names

Copy link
Contributor Author

Choose a reason for hiding this comment

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

for now:

  • method_id returns bytes. I think this is the best name (instead of abi_method_id) mainly because it matches the name and the behavior of the builtin function usable from within Vyper, keeping things clear and consistent
  • method_id_int returns an int. The only difference in behavior between method_id and method_id_int is the return type

I also added a type annotation to method_id_int to reflect that it returns an int, even though the name is already clear, for editor integration purposes

raw_revert(data)
"""

revert_bytes = method_id("NoFives(uint256)") + (5).to_bytes(32, "big")
Copy link
Member

Choose a reason for hiding this comment

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

maybe for clarity, use abi.encode_single("(uint256)", 5)

@charles-cooper charles-cooper enabled auto-merge (squash) November 10, 2022 20:20
@charles-cooper charles-cooper merged commit 886488f into vyperlang:master Nov 10, 2022
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.

4 participants