-
-
Notifications
You must be signed in to change notification settings - Fork 806
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add runtime code layout to initcode (#3584)
this commit adds the runtime code layout to the initcode payload (as a suffix), so that the runtime code can be analyzed without source code. this is particularly important for disassemblers, which need demarcations for where the data section starts as distinct from the runtime code segment itself. the layout is: CBOR-encoded list: runtime code length [<length of data section> for data section in runtime data sections] immutable section length {"vyper": (major, minor, patch)} length of CBOR-encoded list + 2, encoded as two big-endian bytes. note the specific format for the CBOR payload was chosen to avoid changing the last 13 bytes of the signature compared to previous versions of vyper. that is, the last 13 bytes still look like b"\xa1evyper\x83...", this is because, as the last item in a list, its encoding does not change compared to being the only dict in the payload. this commit also changes the meaning of the two footer bytes: they now indicate the length of the entire footer (including the two bytes indicating the footer length). the sole purpose of this is to be more intuitive as the two footer bytes indicate offset-from-the-end where the CBOR-encoded metadata starts, rather than the length of the CBOR payload (without the two length bytes). lastly, this commit renames the internal `insert_vyper_signature=` kwarg to `insert_compiler_metadata=` as the metadata includes more than just the vyper version now.
- Loading branch information
1 parent
39a2313
commit 96d2042
Showing
5 changed files
with
180 additions
and
32 deletions.
There are no files selected for viewing
This file contains 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 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 |
---|---|---|
@@ -1,14 +1,135 @@ | ||
import vyper | ||
import cbor2 | ||
import pytest | ||
|
||
import vyper | ||
from vyper.compiler.settings import OptimizationLevel, Settings | ||
|
||
def test_bytecode_runtime(): | ||
code = """ | ||
simple_contract_code = """ | ||
@external | ||
def a() -> bool: | ||
return True | ||
""" | ||
""" | ||
|
||
many_functions = """ | ||
@external | ||
def foo1(): | ||
pass | ||
@external | ||
def foo2(): | ||
pass | ||
@external | ||
def foo3(): | ||
pass | ||
@external | ||
def foo4(): | ||
pass | ||
@external | ||
def foo5(): | ||
pass | ||
""" | ||
|
||
has_immutables = """ | ||
A_GOOD_PRIME: public(immutable(uint256)) | ||
@external | ||
def __init__(): | ||
A_GOOD_PRIME = 967 | ||
""" | ||
|
||
|
||
def _parse_cbor_metadata(initcode): | ||
metadata_ofst = int.from_bytes(initcode[-2:], "big") | ||
metadata = cbor2.loads(initcode[-metadata_ofst:-2]) | ||
return metadata | ||
|
||
out = vyper.compile_code(code, ["bytecode_runtime", "bytecode"]) | ||
|
||
def test_bytecode_runtime(): | ||
out = vyper.compile_code(simple_contract_code, ["bytecode_runtime", "bytecode"]) | ||
|
||
assert len(out["bytecode"]) > len(out["bytecode_runtime"]) | ||
assert out["bytecode_runtime"][2:] in out["bytecode"][2:] | ||
assert out["bytecode_runtime"].removeprefix("0x") in out["bytecode"].removeprefix("0x") | ||
|
||
|
||
def test_bytecode_signature(): | ||
out = vyper.compile_code(simple_contract_code, ["bytecode_runtime", "bytecode"]) | ||
|
||
runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x")) | ||
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x")) | ||
|
||
metadata = _parse_cbor_metadata(initcode) | ||
runtime_len, data_section_lengths, immutables_len, compiler = metadata | ||
|
||
assert runtime_len == len(runtime_code) | ||
assert data_section_lengths == [] | ||
assert immutables_len == 0 | ||
assert compiler == {"vyper": list(vyper.version.version_tuple)} | ||
|
||
|
||
def test_bytecode_signature_dense_jumptable(): | ||
settings = Settings(optimize=OptimizationLevel.CODESIZE) | ||
|
||
out = vyper.compile_code(many_functions, ["bytecode_runtime", "bytecode"], settings=settings) | ||
|
||
runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x")) | ||
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x")) | ||
|
||
metadata = _parse_cbor_metadata(initcode) | ||
runtime_len, data_section_lengths, immutables_len, compiler = metadata | ||
|
||
assert runtime_len == len(runtime_code) | ||
assert data_section_lengths == [5, 35] | ||
assert immutables_len == 0 | ||
assert compiler == {"vyper": list(vyper.version.version_tuple)} | ||
|
||
|
||
def test_bytecode_signature_sparse_jumptable(): | ||
settings = Settings(optimize=OptimizationLevel.GAS) | ||
|
||
out = vyper.compile_code(many_functions, ["bytecode_runtime", "bytecode"], settings=settings) | ||
|
||
runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x")) | ||
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x")) | ||
|
||
metadata = _parse_cbor_metadata(initcode) | ||
runtime_len, data_section_lengths, immutables_len, compiler = metadata | ||
|
||
assert runtime_len == len(runtime_code) | ||
assert data_section_lengths == [8] | ||
assert immutables_len == 0 | ||
assert compiler == {"vyper": list(vyper.version.version_tuple)} | ||
|
||
|
||
def test_bytecode_signature_immutables(): | ||
out = vyper.compile_code(has_immutables, ["bytecode_runtime", "bytecode"]) | ||
|
||
runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x")) | ||
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x")) | ||
|
||
metadata = _parse_cbor_metadata(initcode) | ||
runtime_len, data_section_lengths, immutables_len, compiler = metadata | ||
|
||
assert runtime_len == len(runtime_code) | ||
assert data_section_lengths == [] | ||
assert immutables_len == 32 | ||
assert compiler == {"vyper": list(vyper.version.version_tuple)} | ||
|
||
|
||
# check that deployed bytecode actually matches the cbor metadata | ||
@pytest.mark.parametrize("code", [simple_contract_code, has_immutables, many_functions]) | ||
def test_bytecode_signature_deployed(code, get_contract, w3): | ||
c = get_contract(code) | ||
deployed_code = w3.eth.get_code(c.address) | ||
|
||
initcode = c._classic_contract.bytecode | ||
|
||
metadata = _parse_cbor_metadata(initcode) | ||
runtime_len, data_section_lengths, immutables_len, compiler = metadata | ||
|
||
assert compiler == {"vyper": list(vyper.version.version_tuple)} | ||
|
||
# runtime_len includes data sections but not immutables | ||
assert len(deployed_code) == runtime_len + immutables_len |
This file contains 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 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 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