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

[WIP] EOF support #3457

Draft
wants to merge 99 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 93 commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
7a62724
add RJUMP and RJUMPI instructions and create a new EVM version to rep…
harkal Dec 20, 2022
43b969a
get_opcode() utility function to get the opcode for a mnemonic
harkal Dec 20, 2022
d4edd29
implement RJUMP
harkal Dec 20, 2022
669d6f4
properly output RJUMP enabled opcode formating
harkal Dec 20, 2022
2d53ed1
refactor symbol to be before RJUMP for consistency
harkal Dec 20, 2022
e369505
if supported emmit RJUMPI
harkal Dec 20, 2022
819f337
fix pc progression for RJUMP RJUMPI
harkal Dec 20, 2022
27afb04
emit RJUMP for if and repeat
harkal Dec 20, 2022
85c6835
assert for very lange offsets
harkal Dec 20, 2022
25897e2
enable RJUMP ineffiecient optimization
harkal Dec 20, 2022
7db3f38
update evm version and proper checking
harkal Dec 20, 2022
9cccc90
add CALLF, RETF, JUMPF definitions
harkal Dec 20, 2022
ea9a620
fix tests
harkal Dec 20, 2022
c57154e
rename EOF evm version to shanghai
harkal Dec 20, 2022
c755e09
add RJUMPV
harkal Dec 20, 2022
1e55fd5
add output for new opcodes
harkal Dec 21, 2022
d5eec27
add CALLF emission and JUMPF CALLF handling
harkal Dec 21, 2022
cc067b5
add validate_eof.py cli util
harkal Dec 24, 2022
4f6700a
update EOFv1 validation script
harkal Dec 24, 2022
482bf87
track proper size
harkal Dec 24, 2022
e3de205
validate code
harkal Dec 24, 2022
af9ea71
EOFv1 enabled emits retf
harkal Dec 28, 2022
0753717
refactor EOF_ENABLED to EOFv1_ENABLED
harkal Dec 28, 2022
ccba119
generate callf
harkal Dec 28, 2022
82bb715
disable return rewrites and only handle gotos that are not simple jumps
harkal Dec 28, 2022
ac316a9
skip JUMPDESTs when EOFv1
harkal Dec 28, 2022
eb7ec67
exit_to wip
harkal Dec 28, 2022
adb5606
goto wip
harkal Dec 28, 2022
124e1a7
fix ident
harkal Dec 29, 2022
883b64d
eofv1 header output wip
harkal Dec 29, 2022
05c0936
file should be handled higher
harkal Dec 29, 2022
a7d3624
eof handling code
harkal Dec 29, 2022
3e7b717
function to get opcode's immediate size
harkal Dec 29, 2022
6d94f01
eof reader class
harkal Dec 29, 2022
03f867b
fix naming
harkal Dec 29, 2022
c347b2c
validator
harkal Dec 29, 2022
4f994d6
create reverse mapping from opcode to mnemonic
harkal Jan 2, 2023
7c34f9d
read code sections and validate wip
harkal Jan 2, 2023
a7c2a78
use class members
harkal Jan 2, 2023
152a53e
update cli verification tool to use vyper
harkal Jan 2, 2023
f23cf30
remove size
harkal Jan 2, 2023
cddfea1
use eof constants
harkal Jan 2, 2023
e0148d9
remove double size
harkal Jan 2, 2023
31bc229
remove JUMPF and PC for eof
harkal Jan 2, 2023
c4f01eb
convert callf offsets to function ids
harkal Jan 3, 2023
a0cca00
compute and emit code section sizes
harkal Jan 3, 2023
c73f44b
update type section
harkal Jan 3, 2023
471ceb3
disable code validation for now
harkal Jan 3, 2023
e7febb0
emit types
harkal Jan 3, 2023
1392120
wip
harkal Jan 4, 2023
da04e64
remove decorateEOFHeader
harkal Jan 4, 2023
9141ba1
append headers at bytecode start for deploy and runtime
harkal Jan 4, 2023
27ee59c
proper size for deploy code
harkal Jan 4, 2023
3abb990
handle consecutive eofv1 containers
harkal Jan 4, 2023
963e57a
eofv1 disassemble debuging support
harkal Jan 4, 2023
385e8e5
temp
harkal Jan 4, 2023
c2119c5
print separators
harkal Jan 4, 2023
22495b3
remove unused return value
harkal Jan 4, 2023
870d028
output update
harkal Jan 4, 2023
54db685
fix return opcodes
harkal Jan 4, 2023
2aff266
convert revert jump to function call
harkal Jan 5, 2023
f4b5b92
enable code validation
harkal Jan 5, 2023
5fb73d0
add ending new lines
harkal Jan 5, 2023
6e6fab6
runnable
harkal Jan 9, 2023
48dfb03
properly order functions breaks
harkal Jan 9, 2023
4aff4fd
fix revert call
harkal Jan 9, 2023
e343a56
remove return pc from stack
harkal Jan 9, 2023
de18ce3
pop arg buffer address on retf
harkal Jan 10, 2023
09b4e7b
outout 1 input for all functions exept main
harkal Jan 10, 2023
f5f26f0
refactoring, add eof enable parameter, proper output
harkal Jan 10, 2023
e9d70cf
add in old parameter for tests
harkal Jan 10, 2023
c929510
fix legacy case
harkal Jan 10, 2023
2a87cfb
update readme
harkal Jan 10, 2023
b36f388
more detail in the exception
harkal Jan 11, 2023
4f7e280
assert push size gt 0
harkal Jan 11, 2023
76d37f4
update shanghai to cancun
harkal Jan 12, 2023
9213915
shanghai -> cancun
harkal Jan 12, 2023
fa5c573
get opcode metadata helper
harkal Jan 18, 2023
18d42f4
calculate_max_stack_height() implementation
harkal Jan 18, 2023
b7486cd
append proper max stack heights to eof header
harkal Jan 18, 2023
7137cb4
leftout import
harkal Jan 18, 2023
bd00287
Add back rebase leftout
harkal May 25, 2023
49a97c1
change default evm version back to shanghai
charles-cooper May 26, 2023
12c20de
no longer assert for push0
harkal May 30, 2023
3b054d9
revert changes to notes
harkal May 30, 2023
856a2aa
remove old active version variable
harkal May 30, 2023
cebf70f
more upstream merges
harkal May 30, 2023
210280c
more upstream merges
harkal May 30, 2023
6a3fb55
black formatter fixes
harkal May 30, 2023
c81d391
isort fixes
harkal May 30, 2023
1e5d4c4
fix flake8 reported issues
harkal May 30, 2023
25b0cee
more blake8 reformatting
harkal May 30, 2023
d9a33ae
Merge branch 'master' into feature/eof-support
harkal Jun 25, 2024
e752a23
weekend eof stuff
harkal Jul 9, 2024
4275e43
lint
harkal Jul 9, 2024
aa88fcc
Merge remote-tracking branch 'origin-vyper/master' into feature/eof-s…
harkal Jul 9, 2024
50c732e
Merge branch 'master' into feature/eof-support
harkal Jul 29, 2024
06fb2fa
no metadata at runtime
harkal Jul 29, 2024
da8d6c0
lint
harkal Jul 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ make dev-init
python setup.py test
```

## Experimental EOFv1 support

This version of the Vyper compiler supports the upcoming EOFv1 bytecode format. This is currently under
development and in alpha state. To enable the experimental EOFv1 support you use the
``--experimental-eof`` command line option. Additionally a greater or equal to ``cancun`` evm version should
be used.

Usage example:
```bash
vyper --experimental-eof --evm-version=cancun -f opcodes_runtime contract.vy
```

## Developing (working on the compiler)

A useful script to have in your PATH is something like the following:
Expand Down
25 changes: 25 additions & 0 deletions vyper/cli/validate_eof.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
import argparse
import sys

from vyper.evm.eof import EOFReader


def _parse_args(argv):
parser = argparse.ArgumentParser(
description="Vyper EOFv1 validation utility", formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument(
"input_file", help="Input file containing the EOFv1 formated bytecode", nargs="?"
)

args = parser.parse_args(argv)

if args.input_file:
with open(args.input_file, "r") as f:
code = bytes.fromhex(f.read())
EOFReader(code)


if __name__ == "__main__":
_parse_args(sys.argv[1:])
6 changes: 6 additions & 0 deletions vyper/cli/vyper_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ def _parse_args(argv):
dest="experimental_codegen",
)
parser.add_argument("--enable-decimals", help="Enable decimals", action="store_true")
parser.add_argument(
"--experimental-eof",
help="The compiler will emit EOFv1 formated bytecode",
action="store_true",
)

args = parser.parse_args(argv)

Expand Down Expand Up @@ -244,6 +249,7 @@ def _parse_args(argv):
settings,
args.storage_layout,
args.no_bytecode_metadata,
args.experimental_eof,
)

mode = "w"
Expand Down
9 changes: 7 additions & 2 deletions vyper/cli/vyper_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,13 @@
evm["object"] = data["bytecode_runtime"]
if "opcodes_runtime" in data:
evm["opcodes"] = data["opcodes_runtime"]
if "source_map_runtime" in data:
evm["sourceMap"] = data["source_map_runtime"]
if "source_map" in data:
evm["sourceMap"] = data["source_map"]["pc_pos_map_compressed"]
if "source_map_full" in data:
evm["sourceMapFull"] = data["source_map_full"]
# MERGE: revisit
# if "source_map_runtime" in data:
# evm["sourceMap"] = data["source_map_runtime"]
Comment on lines +365 to +366

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.

return output_dict

Expand Down
11 changes: 8 additions & 3 deletions vyper/codegen/self_call.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from vyper.codegen.core import _freshname, eval_once_check, make_setter
from vyper.codegen.ir_node import IRnode
from vyper.evm.address_space import MEMORY
from vyper.evm.opcodes import is_eof_enabled
from vyper.exceptions import StateAccessViolation
from vyper.semantics.types.subscriptable import TupleT

Expand Down Expand Up @@ -93,11 +94,15 @@ def ir_for_self_call(stmt_expr, context):
if return_buffer is not None:
goto_op += [return_buffer]
# pass return label to subroutine
goto_op.append(["symbol", return_label])

if not is_eof_enabled():
goto_op.append(["symbol", return_label])

call_sequence = ["seq"]
call_sequence.append(eval_once_check(_freshname(stmt_expr.node_source_code)))
call_sequence.extend([copy_args, goto_op, ["label", return_label, ["var_list"], "pass"]])
if is_eof_enabled():
call_sequence.extend([copy_args, goto_op])
else:
call_sequence.extend([copy_args, goto_op, ["label", return_label, ["var_list"], "pass"]])
if return_buffer is not None:
# push return buffer location to stack
call_sequence += [return_buffer]
Expand Down
2 changes: 1 addition & 1 deletion vyper/compiler/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from pathlib import Path
from typing import Callable, Dict, Optional
from typing import Callable, Optional

import vyper.codegen.core as codegen
import vyper.compiler.output as output
Expand Down
34 changes: 31 additions & 3 deletions vyper/compiler/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
from vyper.compiler.output_bundle import SolcJSONWriter, VyperArchiveWriter
from vyper.compiler.phases import CompilerData
from vyper.compiler.utils import build_gas_estimates
from vyper.evm import opcodes
from vyper.exceptions import VyperException
from vyper.evm import eof, opcodes
from vyper.evm.opcodes import is_eof_enabled
from vyper.exceptions import CompilerPanic, VyperException
from vyper.ir import compile_ir
from vyper.semantics.types.function import FunctionVisibility, StateMutability
from vyper.typing import StorageLayout
Expand Down Expand Up @@ -396,15 +397,25 @@ def build_opcodes_runtime_output(compiler_data: CompilerData) -> str:
return _build_opcodes(compiler_data.bytecode_runtime)


def _build_opcodes(bytecode: bytes) -> str:
def _build_legacy_opcodes(bytecode: bytes) -> str:
bytecode_sequence = deque(bytecode)

opcode_map = dict((v[0], k) for k, v in opcodes.get_opcodes().items())
opcode_output = []

while bytecode_sequence:
op = bytecode_sequence.popleft()
mnemonic = opcode_map.get(op)

if mnemonic is None:
raise CompilerPanic(f"Unsupported opcode {hex(op)} after {opcode_output}")

opcode_output.append(opcode_map.get(op, f"VERBATIM_{hex(op)}"))
if mnemonic in ["RJUMP", "RJUMPI", "CALLF"]:
offset = int.from_bytes(
[bytecode_sequence.popleft() for _i in range(2)], "big", signed=True
)
opcode_output.append(hex(offset))
if "PUSH" in opcode_output[-1] and opcode_output[-1] != "PUSH0":
push_len = int(opcode_map[op][4:])
# we can have push_len > len(bytecode_sequence) when there is data
Expand All @@ -415,3 +426,20 @@ def _build_opcodes(bytecode: bytes) -> str:
opcode_output.append(f"0x{''.join(push_values)}")

return " ".join(opcode_output)


def _build_eof_opcodes(bytecode: bytes) -> str:
eofReader = eof.EOFReader(bytecode)
output = eofReader.disassemble()
if eofReader.bytecode_size != len(bytecode):
runtimeEofReader = eof.EOFReader(bytecode[eofReader.bytecode_size :])
output = "--- DEPLOY CODE ---\n" + output
output += "--- RUNTIME CODE ---\n" + runtimeEofReader.disassemble()
return output


def _build_opcodes(bytecode: bytes) -> str:
if is_eof_enabled():
return _build_eof_opcodes(bytecode)
else:
return _build_legacy_opcodes(bytecode)
31 changes: 28 additions & 3 deletions vyper/compiler/phases.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
storage_layout: StorageLayout = None,
show_gas_estimates: bool = False,
no_bytecode_metadata: bool = False,
experimental_eof: bool = False,
) -> None:
"""
Initialization method.
Expand Down Expand Up @@ -249,12 +250,25 @@

@cached_property
def bytecode(self) -> bytes:
insert_compiler_metadata = not self.no_bytecode_metadata
return generate_bytecode(self.assembly, insert_compiler_metadata=insert_compiler_metadata)
if self.experimental_eof:
return generate_EOFv1(self.assembly, no_bytecode_metadata=self.no_bytecode_metadata)
else:
return generate_bytecode(
self.assembly, is_runtime=False, no_bytecode_metadata=self.no_bytecode_metadata
)
Fixed Show fixed Hide fixed

@cached_property
def bytecode_runtime(self) -> bytes:
return generate_bytecode(self.assembly_runtime, insert_compiler_metadata=False)
if self.experimental_eof:
return generate_EOFv1(
self.assembly_runtime, no_bytecode_metadata=self.no_bytecode_metadata
)
else:
return generate_bytecode(
self.assembly_runtime,
is_runtime=True,
no_bytecode_metadata=self.no_bytecode_metadata,
)
Fixed Show fixed Hide fixed

@cached_property
def blueprint_bytecode(self) -> bytes:
Expand Down Expand Up @@ -368,3 +382,14 @@
return compile_ir.assembly_to_evm(assembly, insert_compiler_metadata=insert_compiler_metadata)[
0
]


def generate_EOFv1(assembly: list, no_bytecode_metadata: bool = False) -> bytes:
bytecode, _ = compile_ir.assembly_to_evm(
assembly,
emit_headers=True,
disable_bytecode_metadata=no_bytecode_metadata,
eof_enabled=True,
)
Fixed Show fixed Hide fixed

return bytecode
Loading
Loading