-
-
Notifications
You must be signed in to change notification settings - Fork 850
feat[lang]: add ABIBuffer type #4561
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
Draft
charles-cooper
wants to merge
27
commits into
vyperlang:master
Choose a base branch
from
charles-cooper:feat/lang/add-abi-buffer-type
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
d01d143
handle raw return
charles-cooper cd20250
propagate thru type system
charles-cooper 970ce6b
fix convert
charles-cooper 775f6d3
disallow literal ABIBuffers
charles-cooper 2ec0b12
add a test
charles-cooper 5848506
add invalid locations
charles-cooper a5fd707
ban in structs and tuples
charles-cooper 8f7b50c
lint
charles-cooper 066fde6
add abibuffer tests
cyberthirst 30db056
add more abibuffer tests
cyberthirst 82c18e9
Merge pull request #69 from cyberthirst/fork/charles-cooper/feat/lang…
charles-cooper f6dce65
handle extcalls for ABIBuffer
charles-cooper c0e2a8b
rename a test
charles-cooper 3c140d8
rename ABIBuffer to ReturnBuffer
charles-cooper d2d7e46
allow returnbuffer in external call
charles-cooper ae6ad0e
lint
charles-cooper 26a8dc2
ban in darrays
charles-cooper 9e1d62a
strip from abi
charles-cooper 9a61d9f
fix bad import
charles-cooper 6982b8e
Merge branch 'master' into feat/lang/add-abi-buffer-type
charles-cooper 2ad2088
update tests for new void output type
charles-cooper a5f203d
add returndatasize test
cyberthirst b63ba35
cleanup
cyberthirst 88e0abf
add explanatory comment
cyberthirst 6bbb1e1
Merge pull request #72 from cyberthirst/fork/charles-cooper/feat/lang…
charles-cooper 4dcffc5
add convert rules from ReturnBufferT
charles-cooper a10e7e6
fix lint
charles-cooper File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
from typing import Any | ||
|
||
import pytest | ||
from eth.codecs import abi | ||
|
||
from tests.evm_backends.base_env import ExecutionReverted | ||
from vyper.compiler import compile_code | ||
|
||
from vyper.exceptions import InstantiationException, StructureException, TypeMismatch | ||
|
||
|
||
# call, but don't abi decode the output | ||
def _call_no_decode(contract_method: Any, *args, **kwargs) -> bytes: | ||
contract = contract_method.contract | ||
calldata = contract_method.prepare_calldata(*args, **kwargs) | ||
output = contract.env.message_call(contract.address, data=calldata) | ||
|
||
return output | ||
|
||
|
||
def test_buffer(get_contract, tx_failed): | ||
test_bytes = """ | ||
@external | ||
def foo(x: Bytes[100]) -> ReturnBuffer[100]: | ||
return convert(x, ReturnBuffer[100]) | ||
""" | ||
|
||
c = get_contract(test_bytes) | ||
return_data = b"cow" | ||
moo_result = _call_no_decode(c.foo, return_data) | ||
assert moo_result == return_data | ||
|
||
|
||
def test_buffer_in_interface(get_contract, tx_failed): | ||
caller_code = """ | ||
interface Foo: | ||
def foo() -> ReturnBuffer[100]: view | ||
|
||
@external | ||
def foo(target: Foo) -> ReturnBuffer[100]: | ||
return staticcall target.foo() | ||
""" | ||
|
||
return_data = abi.encode("(bytes)", (b"cow",)) | ||
target_code = f""" | ||
@external | ||
def foo() -> ReturnBuffer[100]: | ||
return convert(x"{return_data.hex()}", ReturnBuffer[100]) | ||
""" | ||
caller = get_contract(caller_code) | ||
target = get_contract(target_code) | ||
|
||
assert _call_no_decode(caller.foo, target.address) == return_data | ||
|
||
|
||
def test_buffer_str_convert(get_contract): | ||
test_bytes = """ | ||
@external | ||
def foo(x: Bytes[100]) -> ReturnBuffer[100]: | ||
return convert(convert(x, String[100]), ReturnBuffer[100]) | ||
""" | ||
|
||
c = get_contract(test_bytes) | ||
moo_result = _call_no_decode(c.foo, b"cow") | ||
assert moo_result == b"cow" | ||
|
||
|
||
def test_buffer_returndatasize_check(get_contract): | ||
test_bytes = """ | ||
interface Foo: | ||
def payload() -> ReturnBuffer[127]: view | ||
|
||
interface FooSanity: | ||
def payload() -> ReturnBuffer[128]: view | ||
|
||
payload: public(Bytes[33]) | ||
|
||
@external | ||
def set_payload(b: Bytes[33]): | ||
self.payload = b | ||
|
||
@external | ||
def bar() -> ReturnBuffer[127]: | ||
return staticcall Foo(self).payload() | ||
|
||
@external | ||
def sanity_check() -> ReturnBuffer[128]: | ||
b: ReturnBuffer[128] = staticcall FooSanity(self).payload() | ||
return b | ||
""" | ||
|
||
c = get_contract(test_bytes) | ||
payload = b"a" * 33 | ||
c.set_payload(payload) | ||
assert c.payload() == payload | ||
|
||
res = _call_no_decode(c.sanity_check) | ||
|
||
assert len(res) == 128 | ||
assert abi.decode("(bytes)", res) == (payload,) | ||
|
||
# revert due to returndatasize being too big | ||
# 32B head, 32B length, 32 bytes payload, 32 right-padded bytes payload | ||
with pytest.raises(ExecutionReverted): | ||
_call_no_decode(c.bar) | ||
|
||
|
||
def test_buffer_no_subscriptable(get_contract, tx_failed): | ||
code = """ | ||
@external | ||
def foo(x: Bytes[128]) -> bytes8: | ||
return convert(x, ReturnBuffer[128])[0] | ||
""" | ||
|
||
with pytest.raises(StructureException, match="Not an indexable type"): | ||
compile_code(code) | ||
|
||
|
||
def test_proxy_raw_return(get_contract): | ||
impl1 = """ | ||
@external | ||
def foo() -> String[32]: | ||
return "Hello" | ||
""" | ||
|
||
impl2 = """ | ||
@external | ||
def foo() -> Bytes[32]: | ||
return b"Goodbye" | ||
""" | ||
|
||
impl3 = """ | ||
@external | ||
def foo() -> DynArray[uint256, 2]: | ||
#return [1, 2] | ||
a: DynArray[uint256, 2] = [1, 2] | ||
return a | ||
""" | ||
|
||
proxy = """ | ||
target: address | ||
@external | ||
def set_implementation(target: address): | ||
self.target = target | ||
|
||
@external | ||
def foo() -> ReturnBuffer[128]: | ||
data: Bytes[128] = raw_call(self.target, msg.data, is_delegate_call=True, max_outsize=128) | ||
return convert(data, ReturnBuffer[128]) | ||
""" | ||
|
||
impl_c1 = get_contract(impl1) | ||
impl_c2 = get_contract(impl2) | ||
impl_c3 = get_contract(impl3) | ||
|
||
proxy_c = get_contract(proxy) | ||
|
||
proxy_c.set_implementation(impl_c1.address) | ||
res = _call_no_decode(proxy_c.foo) | ||
assert abi.decode("(bytes)", res) == (b"Hello",) | ||
|
||
proxy_c.set_implementation(impl_c2.address) | ||
res = _call_no_decode(proxy_c.foo) | ||
assert abi.decode("(string)", res) == ("Goodbye",) | ||
|
||
proxy_c.set_implementation(impl_c3.address) | ||
res = _call_no_decode(proxy_c.foo) | ||
assert abi.decode("(uint256[])", res) == ([1, 2],) | ||
|
||
|
||
fail_list = [ | ||
("b: ReturnBuffer[128]", InstantiationException), | ||
( | ||
"""b: immutable(ReturnBuffer[128]) | ||
|
||
@deploy | ||
def __init__(): | ||
helper: Bytes[128] = b'' | ||
b = convert(helper, ReturnBuffer[128]) | ||
""", | ||
InstantiationException, | ||
), | ||
( | ||
"b: constant(ReturnBuffer[128]) = b''", | ||
TypeMismatch, | ||
), # type mismatch for now until we allow buffer literals | ||
("b: transient(ReturnBuffer[128])", InstantiationException), | ||
("b: DynArray[ReturnBuffer[128], 2]", StructureException), | ||
( | ||
""" | ||
@external | ||
def foo(b: ReturnBuffer[128]): | ||
pass | ||
""", | ||
InstantiationException, | ||
), | ||
] | ||
|
||
|
||
# TODO: move these to syntax tests | ||
@pytest.mark.parametrize("bad_code,exc", fail_list) | ||
def test_abibuffer_fail(bad_code, exc): | ||
with pytest.raises(exc): | ||
compile_code(bad_code) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.