Repository: vyperlang/vyper

Issue:
Deployable bytecode attempts to access calldata
Hi There,

I'm trying to understand vyper constructor usage patterns. When compiling the example Vyper [erc20 contract](https://github.com/ethereum/vyper/blob/master/examples/tokens/ERC20.vy), the output (deployed) bytecode looks like this:

```
0x600035...000f3
```

The second opcode `0x35` is `CALLDATALOAD`. Given that in the EVM specification, contract creation calls assume `calldata == []`, how is calldata used in this case?

In Solidity, a comparable example would not include `CALLDATALOAD` in the executable part of deployed bytecode, reserving it instead for the runtime bytecode portion.

Let me know if I'm misunderstanding!


Discussion:
@Pet3ris the default `-f bytecode` includes the init code to deploy the contract. So if you have a `__init__` function it would pass the information from calldata. I think that answers the question? 
Hi @jacqueswww - this is precisely why I'm asking. According to the yellow paper, contract creation transactions come with empty calldata:

![image](https://user-images.githubusercontent.com/224585/56365010-d4f9ef80-61e7-11e9-9c49-fe1f78de5e5d.png)

In section "Contract Creation", the specific environment set-up is listed here (note that `I_d`, the calldata string is empty).

![image](https://user-images.githubusercontent.com/224585/56365230-4043c180-61e8-11e9-8c14-c9e8ea6ed1d4.png)

The Solidity convention is to embed constructor arguments as part of the original bytecode that gets passed during contract creation (`i` in the yellow paper). See [here](https://ipfs-sec.stackexchange.cloudflare-ipfs.com/ethereum/A/question/9203.html).

Has Vyper adopted a different convention where the contract call assumes constructor arguments are passed during the contract creation phase?
@Pet3ris I don't think we use anything different, also you can check our tests also show we use standard web3 to deploy and test __init__.

given example program:

```python
val: public(uint256)

@public
def __init__(a: uint256):
    self.val = a
```
Check the IR output (-if ir):

```
[seq,
  [mstore, 28, [calldataload, 0]],
  [mstore, 32, 1461501637330902918203684832716283019655932542976],
  [mstore, 64, 170141183460469231731687303715884105727],
  [mstore, 96, -170141183460469231731687303715884105728],
  [mstore, 128, 1701411834604692317316873037158841057270000000000],
  [mstore, 160, -1701411834604692317316873037158841057280000000000],
  # Line 3
  [codecopy, 320, ~codelen, 32],
  [assert, [iszero, callvalue]],
  # Line 5
  [sstore,
    '0' <self.val>,
    [mload, '320' <a>]],
  [return,
    0,
    [lll,
      [seq,
        [mstore, 28, [calldataload, 0]],
        [mstore, 32, 1461501637330902918203684832716283019655932542976],
        [mstore, 64, 170141183460469231731687303715884105727],
        [mstore, 96, -170141183460469231731687303715884105728],
        [mstore, 128, 1701411834604692317316873037158841057270000000000],
        [mstore, 160, -1701411834604692317316873037158841057280000000000],
        # Line 1
        [if,
          [eq, [mload, 0], '1013691446' <val()>],
          [seq,
            [assert, [iszero, callvalue]],
            # Line 3
            [mstore, 0, [sload, '0' <self.val>]],
            [return, 0, 32],
            # Line 1
            stop]],
        /* Default function */ [revert, 0, 0]],
      0]]]
```

Specifically:

```
[codecopy, 320, ~codelen, 32],
```
Seems to be in order ? ( I realise I answered incorrectly about calldata copying at first, sorry!).

Looking at the above `-f asm` and `-f opcodes` output ... I have no idea what the CALLDATALOAD is doing there and I believe we can remove it. `  [mstore, 28, [calldataload, 0]],`
Location of bug: https://github.com/ethereum/vyper/blob/master/vyper/parser/parser.py#L101
@jacqueswww thanks a lot for the clarification and clearly wrong to assume you're violating the spec :)
@Pet3ris  No problem! Great catch! So I just did a fix:

```
[seq,
  [mstore, 32, 1461501637330902918203684832716283019655932542976],
  [mstore, 64, 170141183460469231731687303715884105727],
  [mstore, 96, -170141183460469231731687303715884105728],
  [mstore, 128, 1701411834604692317316873037158841057270000000000],
  [mstore, 160, -1701411834604692317316873037158841057280000000000],
  # Line 3
  [codecopy, 320, ~codelen, 32],
  [assert, [iszero, callvalue]],
  # Line 5
  [sstore,
    '0' <self.val>,
    [mload, '320' <a>]],
  [return,
    0,
    [lll,
      [seq,
        [mstore, 28, [calldataload, 0]],
        [mstore, 32, 1461501637330902918203684832716283019655932542976],
        [mstore, 64, 170141183460469231731687303715884105727],
        [mstore, 96, -170141183460469231731687303715884105728],
        [mstore, 128, 1701411834604692317316873037158841057270000000000],
        [mstore, 160, -1701411834604692317316873037158841057280000000000],
        # Line 1
        [if,
          [eq, [mload, 0], '1013691446' <val()>],
          [seq,
            [assert, [iszero, callvalue]],
            # Line 3
            [mstore, 0, [sload, '0' <self.val>]],
            [return, 0, 32],
            # Line 1
            stop]],
        /* Default function */ [revert, 0, 0]],
      0]]]
```
Looks much better :) (calldata load for the function), nothing for the __init__.

Resolving patch:
diff --git a/vyper/parser/parser.py b/vyper/parser/parser.py
--- a/vyper/parser/parser.py
+++ b/vyper/parser/parser.py
@@ -1,5 +1,6 @@
 import ast
 from typing import (
+    Any,
     List,
     cast,
 )
@@ -46,6 +47,20 @@
 if not hasattr(ast, 'AnnAssign'):
     raise Exception("Requires python 3.6 or higher for annotation support")
 
+# Header code
+STORE_CALLDATA: List[Any] = ['seq', ['mstore', 28, ['calldataload', 0]]]
+# Store limit constants at fixed addresses in memory.
+LIMIT_MEMORY_SET: List[Any] = [
+    ['mstore', pos, limit_size]
+    for pos, limit_size in LOADED_LIMIT_MAP.items()
+]
+FUNC_INIT_LLL = LLLnode.from_list(
+    STORE_CALLDATA + LIMIT_MEMORY_SET, typ=None
+)
+INIT_FUNC_INIT_LLL = LLLnode.from_list(
+    ['seq'] + LIMIT_MEMORY_SET, typ=None
+)
+
 
 # Header code
 INITIALIZER_LIST = ['seq', ['mstore', 28, ['calldataload', 0]]]
@@ -133,8 +148,9 @@ def parse_other_functions(o,
                           global_ctx,
                           default_function,
                           runtime_only):
-    sub = ['seq', INITIALIZER_LLL]
-    add_gas = INITIALIZER_LLL.gas
+    sub = ['seq', FUNC_INIT_LLL]
+    add_gas = FUNC_INIT_LLL.gas
+
     for _def in otherfuncs:
         sub.append(
             parse_function(_def, {**{'self': sigs}, **external_contracts}, origcode, global_ctx)
@@ -203,15 +219,14 @@ def parse_tree_to_lll(code, origcode, runtime_only=False, interface_codes=None):
         external_contracts = parse_external_contracts(external_contracts, global_ctx)
     # If there is an init func...
     if initfunc:
-        o.append(INITIALIZER_LLL)
-        o.append(
-            parse_function(
-                initfunc[0],
-                {**{'self': sigs}, **external_contracts},
-                origcode,
-                global_ctx,
-            )
-        )
+        o.append(INIT_FUNC_INIT_LLL)
+        o.append(parse_function(
+            initfunc[0],
+            {**{'self': sigs}, **external_contracts},
+            origcode,
+            global_ctx,
+        ))
+
     # If there are regular functions...
     if otherfuncs or defaultfunc:
         o = parse_other_functions(


Test patch:
diff --git a/tests/parser/features/test_gas.py b/tests/parser/features/test_gas.py
--- a/tests/parser/features/test_gas.py
+++ b/tests/parser/features/test_gas.py
@@ -31,5 +31,5 @@ def __init__():
     """
     parser_utils.LLLnode.repr_show_gas = True
     out = parse_to_lll(code)
-    assert '35273' in str(out)[:28]
+    assert '35261' in str(out)[:28]
     parser_utils.LLLnode.repr_show_gas = False
diff --git a/tests/parser/features/test_init.py b/tests/parser/features/test_init.py
new file mode 100644
--- /dev/null
+++ b/tests/parser/features/test_init.py
@@ -0,0 +1,22 @@
+import vyper
+
+
+def test_basic_init_function(get_contract):
+    code = """
+val: public(uint256)
+
+@public
+def __init__(a: uint256):
+    self.val = a
+    """
+
+    c = get_contract(code, *[123])
+
+    assert c.val() == 123
+
+    # Make sure the init signature has no unecessary CALLDATLOAD copy.
+    opcodes = vyper.compile_code(code, ['opcodes'])['opcodes'].split(' ')
+    lll_return_idx = opcodes.index('JUMP')
+
+    assert 'CALLDATALOAD' in opcodes
+    assert 'CALLDATALOAD' not in opcodes[:lll_return_idx]