Skip to content

Commit

Permalink
Merge pull request #2447 from charles-cooper/abi_refactor
Browse files Browse the repository at this point in the history
rewrite internal calling convention
  • Loading branch information
charles-cooper authored Oct 3, 2021
2 parents 030bde6 + 3e036aa commit 93287e5
Show file tree
Hide file tree
Showing 56 changed files with 1,832 additions and 2,430 deletions.
2 changes: 1 addition & 1 deletion docs/built-in-functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ Utilities
Once this function has seen more use we provisionally plan to put it into the ``ethereum.abi`` namespace.

* ``*args``: Arbitrary arguments
* ``ensure_tuple``: If set to True, ensures that even a single argument is encoded as a tuple. In other words, ``bytes`` gets encoded as ``(bytes,)``. This is the calling convention for Vyper and Solidity functions. Except for very specific use cases, this should be set to True. Must be a literal.
* ``ensure_tuple``: If set to True, ensures that even a single argument is encoded as a tuple. In other words, ``bytes`` gets encoded as ``(bytes,)``, and ``(bytes,)`` gets encoded as ``((bytes,),)`` This is the calling convention for Vyper and Solidity functions. Except for very specific use cases, this should be set to True. Must be a literal.
* ``method_id``: A literal hex or Bytes[4] value to append to the beginning of the abi-encoded bytestring.

Returns a bytestring whose max length is determined by the arguments. For example, encoding a ``Bytes[32]`` results in a ``Bytes[64]`` (first word is the length of the bytestring variable).
Expand Down
7 changes: 6 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@
packages=find_packages(exclude=("tests", "docs")),
python_requires=">=3.6",
py_modules=["vyper"],
install_requires=["asttokens==2.0.4", "pycryptodome>=3.5.1,<4", "semantic-version==2.8.5"],
install_requires=[
"asttokens==2.0.4",
"pycryptodome>=3.5.1,<4",
"semantic-version==2.8.5",
"cached-property==1.5.2 ; python_version<'3.8'",
],
setup_requires=["pytest-runner"],
tests_require=extras_require["test"],
extras_require=extras_require,
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/outputs/test_storage_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ def public_bar():
"slot": 3,
},
"baz": {"type": "Bytes[65]", "location": "storage", "slot": 4},
"bar": {"type": "uint256", "location": "storage", "slot": 9},
"bar": {"type": "uint256", "location": "storage", "slot": 8},
}
2 changes: 1 addition & 1 deletion tests/compiler/test_source_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_jump_map():
pos_map = source_map["pc_pos_map"]
jump_map = source_map["pc_jump_map"]

assert len([v for v in jump_map.values() if v == "o"]) == 3
assert len([v for v in jump_map.values() if v == "o"]) == 1
assert len([v for v in jump_map.values() if v == "i"]) == 2

code_lines = [i + "\n" for i in TEST_CODE.split("\n")]
Expand Down
7 changes: 5 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import pytest
from eth_tester import EthereumTester
from eth_utils import setup_DEBUG2_logging
from hexbytes import HexBytes
from web3 import Web3
from web3.providers.eth_tester import EthereumTesterProvider
Expand All @@ -26,12 +27,14 @@


def set_evm_verbose_logging():
logger = logging.getLogger("evm")
logger.setLevel("TRACE")
logger = logging.getLogger("eth.vm.computation.Computation")
setup_DEBUG2_logging()
logger.setLevel("DEBUG2")


# Useful options to comment out whilst working:
# set_evm_verbose_logging()
#
# from vdb import vdb
# vdb.set_evm_opcode_debugger()

Expand Down
2 changes: 1 addition & 1 deletion tests/functional/codegen/test_abi_encode.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def abi_encode(
pet_metadata: bytes32,
ensure_tuple: bool,
include_method_id: bool
) -> Bytes[260]:
) -> Bytes[548]:
human: Human = Human({
name: name,
pet: Animal({
Expand Down
28 changes: 12 additions & 16 deletions tests/functional/codegen/test_struct_return.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,29 @@ def test_nested_tuple(get_contract):
code = """
struct Animal:
location: address
fur: uint256
fur: String[32]
struct Human:
location: address
height: uint256
animal: Animal
@external
def return_nested_tuple() -> (Animal, Human):
animal: Animal = Animal({
location: 0x1234567890123456789012345678901234567890,
fur: 123
})
human: Human = Human({
location: 0x1234567890123456789012345678900000000000,
height: 456
})
def modify_nested_tuple(_human: Human) -> Human:
human: Human = _human
# do stuff, edit the structs
animal.fur += 1
human.height += 1
# (13 is the length of the result)
human.animal.fur = slice(concat(human.animal.fur, " is great"), 0, 13)
return animal, human
return human
"""
c = get_contract(code)
addr1 = "0x1234567890123456789012345678901234567890"
addr2 = "0x1234567890123456789012345678900000000000"
assert c.return_nested_tuple() == [(addr1, 124), (addr2, 457)]
# assert c.modify_nested_tuple([addr1, 123], [addr2, 456]) == [[addr1, 124], [addr2, 457]]
assert c.modify_nested_tuple(
{"location": addr1, "animal": {"location": addr2, "fur": "wool"}}
) == (addr1, (addr2, "wool is great"),)


@pytest.mark.parametrize("string", ["a", "abc", "abcde", "potato"])
Expand Down Expand Up @@ -61,4 +57,4 @@ def test_values(a: address) -> Person:
"""

c2 = get_contract(code)
assert c2.test_values(c1.address) == [string, 42]
assert c2.test_values(c1.address) == (string, 42)
3 changes: 1 addition & 2 deletions tests/functional/context/types/test_size_in_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ def test_array_value_types(build_node, type_str, location, length, size):
node = build_node(f"{type_str}[{length}]")
type_definition = get_type_from_annotation(node, location)

# TODO once storage of bytes is optimized, remove the +32
assert type_definition.size_in_bytes == size + 32
assert type_definition.size_in_bytes == size


@pytest.mark.parametrize("type_str", BASE_TYPES)
Expand Down
16 changes: 8 additions & 8 deletions tests/functional/test_storage_slots.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@ def with_other_lock():

def test_storage_slots(get_contract):
c = get_contract(code)
assert c.a() == ["ok", [4, 5, 6]]
assert c.a() == ("ok", [4, 5, 6])
assert [c.b(i) for i in range(2)] == [7, 8]
assert c.c() == b"thisisthirtytwobytesokhowdoyoudo"
assert [c.d(i) for i in range(4)] == [-1, -2, -3, -4]
assert c.e() == "A realllllly long string but we wont use it all"
assert c.f(0) == 33
assert c.g(0) == [b"hello", [-66, 420], "another string"]
assert c.g(1) == [
assert c.g(0) == (b"hello", [-66, 420], "another string")
assert c.g(1) == (
b"gbye",
[1337, 888],
"whatifthisstringtakesuptheentirelengthwouldthatbesobadidothinkso",
]
)
assert [c.foo(0, i) for i in range(3)] == [987, 654, 321]
assert [c.foo(1, i) for i in range(3)] == [123, 456, 789]
assert c.h(0) == 123456789
Expand All @@ -80,18 +80,18 @@ def test_reentrancy_lock(get_contract):
c.with_lock()
c.with_other_lock()

assert c.a() == ["ok", [4, 5, 6]]
assert c.a() == ("ok", [4, 5, 6])
assert [c.b(i) for i in range(2)] == [7, 8]
assert c.c() == b"thisisthirtytwobytesokhowdoyoudo"
assert [c.d(i) for i in range(4)] == [-1, -2, -3, -4]
assert c.e() == "A realllllly long string but we wont use it all"
assert c.f(0) == 33
assert c.g(0) == [b"hello", [-66, 420], "another string"]
assert c.g(1) == [
assert c.g(0) == (b"hello", [-66, 420], "another string")
assert c.g(1) == (
b"gbye",
[1337, 888],
"whatifthisstringtakesuptheentirelengthwouldthatbesobadidothinkso",
]
)
assert [c.foo(0, i) for i in range(3)] == [987, 654, 321]
assert [c.foo(1, i) for i in range(3)] == [123, 456, 789]
assert c.h(0) == 123456789
25 changes: 0 additions & 25 deletions tests/parser/exceptions/test_argument_exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,31 +89,6 @@ def foo():
for i in range(1, 2, 3, 4):
pass
""",
"""
struct Foo:
a: Bytes[32]
@external
def foo(a: Foo):
pass
""",
"""
struct Foo:
a: String[32]
@external
def foo(a: Foo):
pass
""",
"""
struct Foo:
b: uint256
a: String[32]
@external
def foo(a: Foo):
pass
""",
]


Expand Down
2 changes: 1 addition & 1 deletion tests/parser/features/decorators/test_private.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ def foo() -> A:
return self._foo([1, 2, 3, 4], 5)
""",
(),
[[1, 2, 3, 4], 5],
([1, 2, 3, 4], 5),
),
(
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ def test(addr: address) -> (int128, address):
c1 = get_contract_with_gas_estimation(contract_1)
c2 = get_contract_with_gas_estimation(contract_2)

assert c1.out_literals() == [1, "0x0000000000000000000000000000000000012345"]
assert c1.out_literals() == (1, "0x0000000000000000000000000000000000012345")
assert c2.test(c1.address) == list(c1.out_literals())


Expand Down Expand Up @@ -862,7 +862,7 @@ def test(addr: address) -> (int128, String[{ln}], Bytes[{ln}]):
c1 = get_contract_with_gas_estimation(contract_1)
c2 = get_contract_with_gas_estimation(contract_2)

assert c1.get_struct_x() == [i, s, bytes(s, "utf-8")]
assert c1.get_struct_x() == (i, s, bytes(s, "utf-8"))
assert c2.test(c1.address) == list(c1.get_struct_x())


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ def wrap_get_my_struct_BROKEN(_e1: decimal) -> MyStruct:
return self.get_my_struct(_e1, block.timestamp)
"""
c = get_contract(code)
assert c.wrap_get_my_struct_WORKING(Decimal("0.1")) == [
assert c.wrap_get_my_struct_WORKING(Decimal("0.1")) == (
Decimal("0.1"),
w3.eth.getBlock(w3.eth.blockNumber)["timestamp"],
]
assert c.wrap_get_my_struct_BROKEN(Decimal("0.1")) == [
)
assert c.wrap_get_my_struct_BROKEN(Decimal("0.1")) == (
Decimal("0.1"),
w3.eth.getBlock(w3.eth.blockNumber)["timestamp"],
]
)
6 changes: 4 additions & 2 deletions tests/parser/features/iteration/test_repeater.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,10 @@ def test_return_inside_repeater(get_contract, typ):
@internal
def _final(a: {typ}) -> {typ}:
for i in range(10):
if i > a:
return i
for j in range(10):
if j > 5:
if i > a:
return i
return 31337
@internal
Expand Down
5 changes: 3 additions & 2 deletions tests/parser/features/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ def __init__(a: uint256):

assert c.val() == 123

# Make sure the init signature has no unecessary CALLDATLOAD copy.
# Make sure the init code does not access calldata
opcodes = vyper.compile_code(code, ["opcodes"])["opcodes"].split(" ")
lll_return_idx = opcodes.index("JUMP")

assert "CALLDATALOAD" in opcodes
assert "CALLDATACOPY" in opcodes
assert "CALLDATACOPY" not in opcodes[:lll_return_idx]
assert "CALLDATALOAD" not in opcodes[:lll_return_idx]
20 changes: 14 additions & 6 deletions tests/parser/functions/test_abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,21 @@ def foo(s: MyStruct) -> MyStruct:
func_abi = abi[0]

assert func_abi["name"] == "foo"
expected = {

expected_output = [
{
"type": "tuple",
"name": "",
"components": [{"type": "address", "name": "a"}, {"type": "uint256", "name": "b"}],
}
]

assert func_abi["outputs"] == expected_output

expected_input = {
"type": "tuple",
"name": "",
"name": "s",
"components": [{"type": "address", "name": "a"}, {"type": "uint256", "name": "b"}],
}

assert func_abi["outputs"] == expected["components"]

expected["name"] = "s"
assert func_abi["inputs"][0] == expected
assert func_abi["inputs"][0] == expected_input
12 changes: 6 additions & 6 deletions tests/parser/functions/test_return_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test() -> Voter:

c = get_contract_with_gas_estimation(code)

assert c.test() == [123, True]
assert c.test() == (123, True)


def test_struct_return(get_contract_with_gas_estimation):
Expand Down Expand Up @@ -74,13 +74,13 @@ def pub6() -> Foo:
foo: Foo = Foo({x: 123, y: 456})
return self.return_arg(foo)
"""
foo = [123, 456]
foo = (123, 456)

c = get_contract_with_gas_estimation(code)

assert c.pub1() == [1, 2]
assert c.pub2() == [3, 4]
assert c.pub3() == [5, 6]
assert c.pub4() == [7, 8]
assert c.pub1() == (1, 2)
assert c.pub2() == (3, 4)
assert c.pub3() == (5, 6)
assert c.pub4() == (7, 8)
assert c.pub5(foo) == foo
assert c.pub6() == foo
5 changes: 3 additions & 2 deletions tests/parser/types/numbers/test_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,12 @@ def test_constant_folds(search_for_sublist):
def test() -> uint256:
# calculate some constant which is really unlikely to be randomly
# in bytecode
return 2**SOME_CONSTANT * SOME_PRIME
ret: uint256 = 2**SOME_CONSTANT * SOME_PRIME
return ret
"""

lll = compile_code(code, ["ir"])["ir"]
assert search_for_sublist(lll, ["mstore", [0], [2 ** 12 * some_prime]])
assert search_for_sublist(lll, ["mstore", [320], [2 ** 12 * some_prime]])


def test_constant_lists(get_contract):
Expand Down
4 changes: 2 additions & 2 deletions tests/parser/types/test_node_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ def test_canonicalize_type():

def test_get_size_of_type():
assert get_size_of_type(BaseType("int128")) == 1
assert get_size_of_type(ByteArrayType(12)) == 3
assert get_size_of_type(ByteArrayType(33)) == 4
assert get_size_of_type(ByteArrayType(12)) == 2
assert get_size_of_type(ByteArrayType(33)) == 3
assert get_size_of_type(ListType(BaseType("int128"), 10)) == 10

_tuple = TupleType([BaseType("int128"), BaseType("decimal")])
Expand Down
Loading

0 comments on commit 93287e5

Please sign in to comment.