Repository: vyperlang/vyper

Issue:
"Invalid Jump Destination"
I've noticed that ever since switching to a vyper version using #901 the only error I get (on transactions that should fail) is `eth.exceptions.InvalidJumpDestination: Invalid Jump Destination`

Here is my testing setup:
https://github.com/Uniswap/contracts-vyper/blob/master/tests/conftest.py

### Version Information

* Vyper Version: https://github.com/ethereum/vyper/commits/35038d20bd9946a35261c4c4fbcb27fe61e65f78
* OS: osx Mojave
* Python Version: 3.6.5
* Environment (pipe freeze): 
```
asn1crypto==0.24.0
atomicwrites==1.2.1
attrdict==2.0.0
attrs==18.2.0
certifi==2018.10.15
cffi==1.11.5
chardet==3.0.4
cryptography==2.3.1
cytoolz==0.9.0.1
eth-abi==1.2.2
eth-account==0.2.3
eth-bloom==1.0.1
eth-hash==0.2.0
eth-keyfile==0.5.1
eth-keys==0.2.0b3
eth-rlp==0.1.2
eth-tester==0.1.0b33
eth-typing==1.3.0
eth-utils==1.2.2
hexbytes==0.1.0
idna==2.7
lru-dict==1.1.6
more-itertools==4.3.0
parsimonious==0.8.1
pluggy==0.8.0
py==1.7.0
py-ecc==1.4.3
py-evm==0.2.0a33
pycparser==2.19
pycryptodome==3.6.6
pyethash==0.1.27
pytest==3.9.2
requests==2.20.0
rlp==1.0.3
semantic-version==2.6.0
six==1.11.0
toolz==0.9.0
trie==1.3.8
urllib3==1.24
vyper==0.1.0b3
web3==4.4.1
websockets==5.0.1
```

Error Output:

```
test_eth_to_token.py:18:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../env/lib/python3.6/site-packages/web3/eth.py:263: in sendTransaction
    get_buffered_gas_estimate(self.web3, transaction),
../../env/lib/python3.6/site-packages/web3/utils/transactions.py:84: in get_buffered_gas_estimate
    gas_estimate = web3.eth.estimateGas(gas_estimate_transaction)
../../env/lib/python3.6/site-packages/web3/eth.py:304: in estimateGas
    [transaction],
../../env/lib/python3.6/site-packages/web3/manager.py:107: in request_blocking
    response = self._make_request(method, params)
../../env/lib/python3.6/site-packages/web3/manager.py:90: in _make_request
    return request_func(method, params)
../../env/lib/python3.6/site-packages/web3/middleware/gas_price_strategy.py:18: in middleware
    return make_request(method, params)
cytoolz/functoolz.pyx:232: in cytoolz.functoolz.curry.__call__
    ???
../../env/lib/python3.6/site-packages/web3/middleware/formatting.py:48: in apply_formatters
    response = make_request(method, formatted_params)
../../env/lib/python3.6/site-packages/web3/middleware/attrdict.py:18: in middleware
    response = make_request(method, params)
cytoolz/functoolz.pyx:232: in cytoolz.functoolz.curry.__call__
    ???
../../env/lib/python3.6/site-packages/web3/middleware/formatting.py:48: in apply_formatters
    response = make_request(method, formatted_params)
../../env/lib/python3.6/site-packages/web3/middleware/normalize_errors.py:9: in middleware
    result = make_request(method, params)
cytoolz/functoolz.pyx:232: in cytoolz.functoolz.curry.__call__
    ???
../../env/lib/python3.6/site-packages/web3/middleware/formatting.py:48: in apply_formatters
    response = make_request(method, formatted_params)
cytoolz/functoolz.pyx:232: in cytoolz.functoolz.curry.__call__
    ???
../../env/lib/python3.6/site-packages/web3/middleware/formatting.py:48: in apply_formatters
    response = make_request(method, formatted_params)
../../env/lib/python3.6/site-packages/web3/providers/eth_tester/middleware.py:320: in middleware
    return make_request(method, [filled_transaction] + params[1:])
../../env/lib/python3.6/site-packages/web3/middleware/fixture.py:12: in middleware
    return make_request(method, params)
cytoolz/functoolz.pyx:232: in cytoolz.functoolz.curry.__call__
    ???
../../env/lib/python3.6/site-packages/web3/middleware/formatting.py:48: in apply_formatters
    response = make_request(method, formatted_params)
../../env/lib/python3.6/site-packages/web3/providers/eth_tester/main.py:46: in make_request
    response = delegator(self.ethereum_tester, params)
cytoolz/functoolz.pyx:232: in cytoolz.functoolz.curry.__call__
    ???
../../env/lib/python3.6/site-packages/web3/providers/eth_tester/defaults.py:36: in call_eth_tester
    return getattr(eth_tester, fn_name)(*fn_args, **fn_kwargs)
../../env/lib/python3.6/site-packages/eth_tester/main.py:445: in estimate_gas
    raw_gas_estimate = self.backend.estimate_gas(raw_transaction)
../../env/lib/python3.6/site-packages/eth_tester/utils/formatting.py:85: in wrapper
    return to_wrap(*args, **kwargs)
../../env/lib/python3.6/site-packages/eth_tester/backends/pyevm/main.py:511: in estimate_gas
    return self.chain.estimate_gas(spoofed_transaction)
../../env/lib/python3.6/site-packages/eth/chains/base.py:615: in estimate_gas
    return self.gas_estimator(state, transaction)
cytoolz/functoolz.pyx:232: in cytoolz.functoolz.curry.__call__
    ???
../../env/lib/python3.6/site-packages/eth/estimators/gas.py:65: in binary_gas_search
    raise error
../../env/lib/python3.6/site-packages/eth/vm/computation.py:560: in apply_computation
    opcode_fn(computation=computation)
../../env/lib/python3.6/site-packages/eth/vm/opcode.py:49: in wrapped_logic_fn
    return logic_fn(computation)

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

computation = <eth.vm.forks.byzantium.computation.ByzantiumComputation object at 0x10fcc6400>

    def jumpi(computation):
        jump_dest, check_value = computation.stack_pop(num_items=2, type_hint=constants.UINT256)

        if check_value:
            computation.code.pc = jump_dest

            next_opcode = computation.code.peek()

            if next_opcode != JUMPDEST:
>               raise InvalidJumpDestination("Invalid Jump Destination")
E               eth.exceptions.InvalidJumpDestination: Invalid Jump Destination

../../env/lib/python3.6/site-packages/eth/vm/logic/flow.py:39: InvalidJumpDestination
```


Discussion:
@haydenadams does this occur on `assert` statements? I think this is probably related to our `get_revert` function if so: https://github.com/ethereum/vyper/blob/master/vyper/compile_lll.py#L30
@jacqueswww It occurs on failing web3 function calls that are not constant. 
@jacqueswww  wait sorry I misunderstood. You mean Vyper asserts not pytest asserts.

Yeah I think thats the case. 

After a lot of digging, I have figured out what is causing this. Doing an invalid jump, was the old method of throwing in a contract, before REVERT came to be. `create_with_code_of` still uses this type of exception after the delegatcall returns a failure. So here are the steps to fix this issue:

- Fix `kode` variable in functions.py to be more readable, hex blobs should be avoided.
- Make the `create_with_code_of` use REVERT instead.


Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -29,7 +29,7 @@
         'pytest-cov',
         'py-evm==0.2.0a32',
         'eth-tester[py-evm]==0.1.0b32',
-        'web3==4.8.1',
+        'web3==4.8.2',
     ],
     scripts=[
         'bin/vyper',
diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py
--- a/vyper/functions/functions.py
+++ b/vyper/functions/functions.py
@@ -621,8 +621,48 @@ def shift(expr, args, kwargs, context):
     typ=BaseType('uint256'), pos=getpos(expr))
 
 
+def get_create_with_code_of_bytecode():
+    from vyper.compile_lll import (
+        assembly_to_evm,
+        num_to_bytearray
+    )
+    code_a = [
+        'PUSH1', 0x33,
+        'PUSH1', 0x0c,
+        'PUSH1', 0x00,
+        'CODECOPY',
+        'PUSH1', 0x33,
+        'PUSH1', 0x00,
+        'RETURN',
+        'CALLDATASIZE',
+        'PUSH1', 0x00,
+        'PUSH1', 0x00,
+        'CALLDATACOPY',
+        'PUSH2', num_to_bytearray(0x1000),
+        'PUSH1', 0x00,
+        'CALLDATASIZE',
+        'PUSH1', 0x00,
+        'PUSH20',  # [address to delegate to]
+    ]
+    code_b = [
+        'GAS',
+        'DELEGATECALL',
+        'PUSH1', 0x2c,  # jumpdest of whole program.
+        'JUMPI',
+        'PUSH1', 0x0,
+        'DUP1',
+        'REVERT',
+        'JUMPDEST',
+        'PUSH2', num_to_bytearray(0x1000),
+        'PUSH1', 0x00,
+        'RETURN'
+    ]
+    return assembly_to_evm(code_a)[0] + (b'\x00' * 20) + assembly_to_evm(code_b)[0]
+
+
 @signature('address', value=Optional('uint256', zero_value))
 def create_with_code_of(expr, args, kwargs, context):
+
     value = kwargs['value']
     if value != zero_value:
         enforce_units(value.typ, get_keyword(expr, 'value'),
@@ -630,15 +670,16 @@ def create_with_code_of(expr, args, kwargs, context):
     if context.is_constant:
         raise ConstancyViolationException("Cannot make calls from a constant function", expr)
     placeholder = context.new_placeholder(ByteArrayType(96))
-    kode = b'`.`\x0c`\x009`.`\x00\xf36`\x00`\x007a\x10\x00`\x006`\x00s\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Z\xf4\x15XWa\x10\x00`\x00\xf3'
-    assert len(kode) <= 64
+
+    kode = get_create_with_code_of_bytecode()
     high = bytes_to_int(kode[:32])
     low = bytes_to_int((kode + b'\x00' * 32)[47:79])
+
     return LLLnode.from_list(['seq',
                                 ['mstore', placeholder, high],
                                 ['mstore', ['add', placeholder, 27], ['mul', args[0], 2**96]],
                                 ['mstore', ['add', placeholder, 47], low],
-                                ['clamp_nonzero', ['create', value, placeholder, 64]]], typ=BaseType('address'), pos=getpos(expr), add_gas_estimate=10000)
+                                ['clamp_nonzero', ['create', value, placeholder, 96]]], typ=BaseType('address'), pos=getpos(expr), add_gas_estimate=11000)
 
 
 @signature(('int128', 'decimal', 'uint256'), ('int128', 'decimal', 'uint256'))


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -74,7 +74,7 @@ def set_evm_verbose_logging():
 
 
 # Useful options to comment out whilst working:
-# set_evm_verbose_logging()
+set_evm_verbose_logging()
 # from vdb import vdb
 # vdb.set_evm_opcode_debugger()
 
diff --git a/tests/parser/functions/test_create_with_code_of.py b/tests/parser/functions/test_create_with_code_of.py
new file mode 100644
--- /dev/null
+++ b/tests/parser/functions/test_create_with_code_of.py
@@ -0,0 +1,102 @@
+import logging
+import sys
+
+logger = logging.getLogger('evm')
+handler = logging.StreamHandler(sys.stdout)
+logger.setLevel(logging.TRACE)
+handler.setLevel(logging.TRACE)
+
+
+def test_create_with_code_of_create(get_contract):
+    code = """
+main: address
+
+@public
+def test() -> address:
+    self.main = create_with_code_of(self)
+    return self.main
+    """
+
+    c = get_contract(code)
+
+    assert c.test() == '0x4F9DA333DCf4E5A53772791B95c161B2FC041859'
+
+
+def test_create_with_code_of_call(get_contract, w3):
+    code = """
+
+contract SubContract:
+
+    def hello() -> bytes[100]: constant
+
+
+other: public(address)
+
+
+@public
+def test() -> address:
+    self.other = create_with_code_of(self)
+    return self.other
+
+
+@public
+def hello() -> bytes[100]:
+    return "hello world!"
+
+
+@public
+def test2() -> bytes[100]:
+    return SubContract(self.other).hello()
+
+    """
+
+    c = get_contract(code)
+
+    assert c.hello() == b'hello world!'
+    c.test(transact={})
+    assert c.test2() == b'hello world!'
+
+
+def test_create_with_code_exception(w3, get_contract, assert_tx_failed):
+    code = """
+
+contract SubContract:
+
+    def hello(a: uint256) -> bytes[100]: constant
+
+
+other: public(address)
+
+
+@public
+def test() -> address:
+    self.other = create_with_code_of(self)
+    return self.other
+
+
+@public
+def hello(a: uint256) -> bytes[100]:
+    assert a > 0, "invaliddddd"
+    return "hello world!"
+
+
+@public
+def test2(a: uint256) -> bytes[100]:
+    return SubContract(self.other).hello(a)
+    """
+
+    c = get_contract(code)
+
+    assert c.hello(1) == b'hello world!'
+    c.test(transact={})
+    assert c.test2(1) == b'hello world!'
+
+    assert_tx_failed(lambda: c.test2(0))
+
+    GAS_SENT = 30000
+    tx_hash = c.test2(0, transact={'gas': GAS_SENT})
+
+    receipt = w3.eth.getTransactionReceipt(tx_hash)
+
+    assert receipt['status'] == 0
+    assert receipt['gasUsed'] < GAS_SENT
diff --git a/tests/parser/functions/test_raw_call.py b/tests/parser/functions/test_raw_call.py
--- a/tests/parser/functions/test_raw_call.py
+++ b/tests/parser/functions/test_raw_call.py
@@ -1,3 +1,4 @@
+from vyper.functions import get_create_with_code_of_bytecode
 
 
 def test_caller_code(get_contract_with_gas_estimation):
@@ -48,12 +49,15 @@ def create_and_return_forwarder(inp: address) -> address:
     c2 = get_contract_with_gas_estimation(outer_code)
     assert c2.create_and_call_returnten(c.address) == 10
     c2.create_and_call_returnten(c.address, transact={})
-    expected_forwarder_code_mask = b'`.`\x0c`\x009`.`\x00\xf36`\x00`\x007a\x10\x00`\x006`\x00s\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Z\xf4\x15XWa\x10\x00`\x00\xf3'[12:]
+
+    expected_forwarder_code_mask = get_create_with_code_of_bytecode()[12:]
+
     c3 = c2.create_and_return_forwarder(c.address, call={})
     c2.create_and_return_forwarder(c.address, transact={})
 
     c3_contract_code = w3.toBytes(w3.eth.getCode(c3))
-    assert c3_contract_code[:15] == expected_forwarder_code_mask[:15]
+
+    assert c3_contract_code[:14] == expected_forwarder_code_mask[:14]
     assert c3_contract_code[35:] == expected_forwarder_code_mask[35:]
 
     print('Passed forwarder test')