diff --git a/src/ethereum/prague/fork.py b/src/ethereum/prague/fork.py index 6a835a6992..5626595de9 100644 --- a/src/ethereum/prague/fork.py +++ b/src/ethereum/prague/fork.py @@ -588,7 +588,7 @@ def apply_body( gas=SYSTEM_TRANSACTION_GAS, value=U256(0), data=parent_beacon_block_root, - container=beacon_block_roots_contract_code, + code=beacon_block_roots_contract_code, depth=Uint(0), current_target=BEACON_ROOTS_ADDRESS, code_address=BEACON_ROOTS_ADDRESS, diff --git a/src/ethereum/prague/utils/message.py b/src/ethereum/prague/utils/message.py index ee071104fa..c361554e62 100644 --- a/src/ethereum/prague/utils/message.py +++ b/src/ethereum/prague/utils/message.py @@ -81,11 +81,11 @@ def prepare_message( get_account(env.state, caller).nonce - U256(1), ) msg_data = Bytes(b"") - container = data + code = data elif isinstance(target, Address): current_target = target msg_data = data - container = get_account(env.state, target).code + code = get_account(env.state, target).code if code_address is None: code_address = target else: @@ -103,7 +103,7 @@ def prepare_message( gas=gas, value=value, data=msg_data, - container=container, + code=code, depth=Uint(0), current_target=current_target, code_address=code_address, diff --git a/src/ethereum/prague/vm/__init__.py b/src/ethereum/prague/vm/__init__.py index c2050d9897..81f9b0af03 100644 --- a/src/ethereum/prague/vm/__init__.py +++ b/src/ethereum/prague/vm/__init__.py @@ -23,19 +23,19 @@ from ..blocks import Log from ..fork_types import Address, VersionedHash from ..state import State, TransientStorage, account_exists_and_is_empty -from .exceptions import InvalidEOF +from .exceptions import InvalidEof from .precompiled_contracts import RIPEMD160_ADDRESS -__all__ = ("Environment", "Evm", "Message", "EOF") +__all__ = ("Environment", "Evm", "Message", "Eof") EOF_MAGIC = b"\xEF\x00" -EOF_MAGIC_LENGTH = 2 +EOF_MAGIC_LENGTH = len(EOF_MAGIC) MAX_CODE_SIZE = 0x6000 -class EOF(enum.Enum): +class Eof(enum.Enum): """ Enumeration of the different kinds of EOF containers. Legacy code is assigned zero. @@ -46,7 +46,7 @@ class EOF(enum.Enum): @dataclass -class EOFMetadata: +class EofMetadata: """ Dataclass to hold the metadata information of the EOF container. @@ -112,7 +112,7 @@ class Message: value: U256 data: Bytes code_address: Optional[Address] - container: Bytes + code: Bytes depth: Uint should_transfer_value: bool is_static: bool @@ -143,8 +143,9 @@ class Evm: error: Optional[Exception] accessed_addresses: Set[Address] accessed_storage_keys: Set[Tuple[Address, Bytes32]] - eof: EOF - eof_meta: Optional[EOFMetadata] + eof_version: Eof + eof_container: Optional[Bytes] + eof_metadata: Optional[EofMetadata] current_section_index: Uint return_stack: List[ReturnStackItem] @@ -201,9 +202,9 @@ def incorporate_child_on_error(evm: Evm, child_evm: Evm) -> None: evm.gas_left += child_evm.gas_left -def get_eof_version(code: bytes) -> EOF: +def get_eof_version(code: bytes) -> Eof: """ - Get the EOF version container. + Get the Eof container's version. Parameters ---------- @@ -212,13 +213,13 @@ def get_eof_version(code: bytes) -> EOF: Returns ------- - EOF - EOF Version of the container. + Eof + Eof Version of the container. """ if not code.startswith(EOF_MAGIC): - return EOF.LEGACY + return Eof.LEGACY if code[EOF_MAGIC_LENGTH] == 1: - return EOF.EOF1 + return Eof.EOF1 else: - raise InvalidEOF("Invalid EOF version") + raise InvalidEof("Invalid EOF version") diff --git a/src/ethereum/prague/vm/eof.py b/src/ethereum/prague/vm/eof.py index 6d1dffea7d..6178a12516 100644 --- a/src/ethereum/prague/vm/eof.py +++ b/src/ethereum/prague/vm/eof.py @@ -16,8 +16,8 @@ from ethereum.base_types import Uint -from . import EOF, EOF_MAGIC, EOF_MAGIC_LENGTH, MAX_CODE_SIZE, EOFMetadata -from .exceptions import InvalidEOF +from . import EOF_MAGIC, EOF_MAGIC_LENGTH, MAX_CODE_SIZE, Eof, EofMetadata +from .exceptions import InvalidEof from .instructions import ( OPCODES_INVALID_IN_EOF1, OPCODES_INVALID_IN_LEGACY, @@ -25,7 +25,7 @@ ) -def get_opcode(opcode: int, eof: EOF) -> Ops: +def map_int_to_op(opcode: int, eof: Eof) -> Ops: """ Get the opcode enum from the opcode value. @@ -33,7 +33,7 @@ def get_opcode(opcode: int, eof: EOF) -> Ops: ---------- opcode : `int` The opcode value. - eof : `EOF` + eof : `Eof` The version of the EOF. Returns @@ -43,129 +43,18 @@ def get_opcode(opcode: int, eof: EOF) -> Ops: """ try: op = Ops(opcode) - except ValueError: - raise ValueError(f"Invalid opcode: {opcode}") + except ValueError as e: + raise ValueError(f"Invalid opcode: {opcode}") from e - if eof == EOF.LEGACY and op in OPCODES_INVALID_IN_LEGACY: - raise ValueError(f"Invalid opcode: {op}") - elif eof == EOF.EOF1 and op in OPCODES_INVALID_IN_EOF1: - raise ValueError(f"Invalid opcode: {op}") + if eof == Eof.LEGACY and op in OPCODES_INVALID_IN_LEGACY: + raise ValueError(f"Invalid legacy opcode: {op}") + elif eof == Eof.EOF1 and op in OPCODES_INVALID_IN_EOF1: + raise ValueError(f"Invalid eof1 opcode: {op}") return op -def meta_from_valid_eof1_container(code: bytes) -> EOFMetadata: - """ - Extract the code from a valid EOF1 container. - - Parameters - ---------- - code : bytes - The EOF1 container. - - Returns - ------- - meta : EOFMetadata - The meta from the EOF1 container. - """ - # Skip the magic and version bytes - position = Uint(EOF_MAGIC_LENGTH + 1) - - # Skip the type marker - position += 1 - - # Read the type size - type_size = Uint.from_be_bytes(code[position : position + 2]) - position += 2 - - # Skip the code marker - code_size = Uint.from_be_bytes(code[position : position + 1]) - position += 1 - - # Read the number of code sections - num_code_sections = Uint.from_be_bytes(code[position : position + 2]) - position += 2 - code_sizes = [] - for _ in range(num_code_sections): - code_size = Uint.from_be_bytes(code[position : position + 2]) - position += 2 - code_sizes.append(code_size) - - total_container_size = 0 - if code[position] == 3: - # Skip container section marker - position += 1 - - # Read the number of container sections - num_container_sections = Uint.from_be_bytes( - code[position : position + 2] - ) - position += 2 - - # Skip the container sizes - container_sizes = [] - for _ in range(num_container_sections): - container_size = Uint.from_be_bytes(code[position : position + 2]) - total_container_size += container_size - container_sizes.append(container_size) - position += 2 - else: - num_container_sections = Uint(0) - container_sizes = None - - # Skip the data marker - position += 1 - # Read the data size - data_size = Uint.from_be_bytes(code[position : position + 2]) - position += 2 - - # Skip the terminator - position += 1 - body_start_index = position - - # Read the type section - type_section_contents = [] - num_types = type_size // 4 - for _ in range(num_types): - type_section_contents.append(code[position : position + 4]) - position += 4 - - # Read the code section - code_section_contents = [] - for code_size in code_sizes: - code_section_contents.append(code[position : position + code_size]) - position += code_size - - if container_sizes: - # Read the container section - container_section_contents = [] - for container_size in container_sizes: - container_section_contents.append( - code[position : position + container_size] - ) - position += container_size - else: - container_section_contents = None - - # Read the data section - data_section_contents = code[position : position + data_size] - - return EOFMetadata( - type_size=type_size, - num_code_sections=num_code_sections, - code_sizes=code_sizes, - num_container_sections=num_container_sections, - container_sizes=container_sizes, - data_size=data_size, - body_start_index=body_start_index, - type_section_contents=type_section_contents, - code_section_contents=code_section_contents, - container_section_contents=container_section_contents, - data_section_contents=data_section_contents, - ) - - -def validate_header(container: bytes) -> EOFMetadata: +def parse_container_metadata(container: bytes, validate: bool) -> EofMetadata: """ Validate the header of the EOF container. @@ -173,96 +62,102 @@ def validate_header(container: bytes) -> EOFMetadata: ---------- container : bytes The EOF container to validate. + validate : bool + Whether to validate the EOF container. If the container is simply read + from an existing account, it is assumed to be validated. However, if + the container is being created, it should be validated first. Returns ------- - EOFMetadata + EofMetadata The metadata of the EOF container. Raises ------ - InvalidEOF + InvalidEof If the header of the EOF container is invalid. """ counter = len(EOF_MAGIC) + 1 # Get the one byte kind type - if len(container) < counter + 1: - raise InvalidEOF("Kind type not specified in header") + if validate and len(container) < counter + 1: + raise InvalidEof("Kind type not specified in header") kind_type = container[counter] counter += 1 - if kind_type != 1: - raise InvalidEOF("Invalid kind type") + if validate and kind_type != 1: + raise InvalidEof("Invalid kind type") # Get the 2 bytes type_size - if len(container) < counter + 2: - raise InvalidEOF("Type size not specified in header") + if validate and len(container) < counter + 2: + raise InvalidEof("Type size not specified in header") type_size = Uint.from_be_bytes(container[counter : counter + 2]) counter += 2 - if type_size < 4 or type_size > 4096 or type_size % 4 != 0: - raise InvalidEOF("Invalid type size") + if validate and (type_size < 4 or type_size > 4096 or type_size % 4 != 0): + raise InvalidEof("Invalid type size") num_types = type_size // 4 # Get the 1 byte kind_code - if len(container) < counter + 1: - raise InvalidEOF("Kind code not specified in header") + if validate and len(container) < counter + 1: + raise InvalidEof("Kind code not specified in header") kind_code = container[counter] counter += 1 - if kind_code != 2: - raise InvalidEOF("Invalid kind code") + if validate and kind_code != 2: + raise InvalidEof("Invalid kind code") # Get the 2 bytes num_code_sections - if len(container) < counter + 2: - raise InvalidEOF("Number of code sections not specified in header") + if validate and len(container) < counter + 2: + raise InvalidEof("Number of code sections not specified in header") num_code_sections = Uint.from_be_bytes(container[counter : counter + 2]) counter += 2 - if ( + if validate and ( num_code_sections < 1 or num_code_sections > 1024 or num_code_sections != num_types ): - raise InvalidEOF("Invalid number of code sections") + raise InvalidEof("Invalid number of code sections") code_sizes = [] for i in range(num_code_sections): # Get the 2 bytes code_size - if len(container) < counter + 2: - raise InvalidEOF( + if validate and len(container) < counter + 2: + raise InvalidEof( f"Code section {i} does not have a size specified in header" ) code_size = Uint.from_be_bytes(container[counter : counter + 2]) counter += 2 - if code_size == 0: - raise InvalidEOF(f"Invalid code size for code section {i}") + if validate and code_size == 0: + raise InvalidEof(f"Invalid code size for code section {i}") code_sizes.append(code_size) # Check if the container section is present - if len(container) < counter + 1: - raise InvalidEOF("Kind data not specified in header") + if validate and len(container) < counter + 1: + raise InvalidEof("Kind data not specified in header") if container[counter] == 3: container_sizes = [] counter += 1 # Get the 2 bytes num_container_sections - if len(container) < counter + 2: - raise InvalidEOF("Number of container sections not specified") + if validate and len(container) < counter + 2: + raise InvalidEof("Number of container sections not specified") num_container_sections = Uint.from_be_bytes( container[counter : counter + 2] ) counter += 2 - if num_container_sections < 1 or num_container_sections > 256: - raise InvalidEOF("Invalid number of container sections") + if validate and ( + num_container_sections < 1 or num_container_sections > 256 + ): + raise InvalidEof("Invalid number of container sections") for i in range(num_container_sections): # Get the 2 bytes container_size - if len(container) < counter + 2: - raise InvalidEOF( + if validate and len(container) < counter + 2: + raise InvalidEof( f"Container section {i} does not have a size specified" ) container_size = Uint.from_be_bytes( container[counter : counter + 2] ) counter += 2 - if container_size == 0: - raise InvalidEOF("Invalid container size") + if validate and container_size == 0: + raise InvalidEof("Invalid container size") container_sizes.append(container_size) else: num_container_sections = Uint(0) @@ -271,40 +166,40 @@ def validate_header(container: bytes) -> EOFMetadata: # Get 1 byte kind_data kind_data = container[counter] counter += 1 - if kind_data != 4: - raise InvalidEOF("Invalid kind data") + if validate and kind_data != 4: + raise InvalidEof("Invalid kind data") # Get 2 bytes data_size - if len(container) < counter + 2: - raise InvalidEOF("Data size not specified in the header") + if validate and len(container) < counter + 2: + raise InvalidEof("Data size not specified in the header") data_size = Uint.from_be_bytes(container[counter : counter + 2]) counter += 2 # Get 1 byte terminator - if len(container) < counter + 1: - raise InvalidEOF("Header missing terminator byte") + if validate and len(container) < counter + 1: + raise InvalidEof("Header missing terminator byte") terminator = container[counter] counter += 1 - if terminator != 0: - raise InvalidEOF("Invalid terminator") + if validate and terminator != 0: + raise InvalidEof("Invalid terminator") body_start_index = Uint(counter) - if len(container) < counter + type_size: - raise InvalidEOF("Type section size does not match header") + if validate and len(container) < counter + type_size: + raise InvalidEof("Type section size does not match header") type_section_contents = [] for _ in range(type_size // 4): type_section_contents.append(container[counter : counter + 4]) counter += 4 - if len(container) < counter + sum(code_sizes): - raise InvalidEOF("Code section size does not match header") + if validate and len(container) < counter + sum(code_sizes): + raise InvalidEof("Code section size does not match header") code_section_contents = [] for code_size in code_sizes: code_section_contents.append(container[counter : counter + code_size]) counter += code_size if container_sizes: - if len(container) < counter + sum(container_sizes): - raise InvalidEOF("Container section size does not match header") + if validate and len(container) < counter + sum(container_sizes): + raise InvalidEof("Container section size does not match header") container_section_contents = [] for container_size in container_sizes: container_section_contents.append( @@ -314,16 +209,16 @@ def validate_header(container: bytes) -> EOFMetadata: else: container_section_contents = None - if len(container) < counter + data_size: - raise InvalidEOF("Data section size does not match header") + if validate and len(container) < counter + data_size: + raise InvalidEof("Data section size does not match header") data_section_contents = container[counter : counter + data_size] counter += data_size # Check for stray bytes after the data section - if len(container) > counter: - raise InvalidEOF("Stray bytes found after data section") + if validate and len(container) > counter: + raise InvalidEof("Stray bytes found after data section") - return EOFMetadata( + return EofMetadata( type_size=type_size, num_code_sections=num_code_sections, code_sizes=code_sizes, @@ -338,45 +233,45 @@ def validate_header(container: bytes) -> EOFMetadata: ) -def validate_body(eof_meta: EOFMetadata) -> None: +def validate_body(eof_meta: EofMetadata) -> None: """ Validate the body of the EOF container. Parameters ---------- - eof_meta : EOFMetadata + eof_meta : EofMetadata The metadata of the EOF container. Raises ------ - InvalidEOF + InvalidEof If the EOF container is invalid. """ for i, type_section in enumerate(eof_meta.type_section_contents): num_inputs = type_section[0] if num_inputs > 127: - raise InvalidEOF(f"Invalid number of inputs for type {i}") + raise InvalidEof(f"Invalid number of inputs for type {i}") num_outputs = type_section[1] if num_outputs > 128: - raise InvalidEOF(f"Invalid number of outputs for type {i}") + raise InvalidEof(f"Invalid number of outputs for type {i}") max_stack_height = Uint.from_be_bytes(type_section[2:]) if max_stack_height > 1023: - raise InvalidEOF(f"Invalid max stack height for type {i}") + raise InvalidEof(f"Invalid max stack height for type {i}") # 4750: Input and output for first section should # be 0 and 128 respectively if i == 0 and (num_inputs != 0 or num_outputs != 0x80): - raise InvalidEOF("Invalid input/output for first section") + raise InvalidEof("Invalid input/output for first section") -def get_valid_instructions(code: bytes) -> Set[int]: +def get_valid_opcode_positions(code: bytes) -> Set[int]: """ - Get the valid instructions for the code. These will also be the - positions within the code to which, jumps can be performed. - The immediate bytesof the PUSH, RJUMP, RJUMPI, RJUMPV opcodes - are invalid as jump destinations. + Get the positions of the valid opcodes for the code. These will + also be the positions within the code to which, jumps can be + performed. The immediate bytes of the PUSH, RJUMP, RJUMPI, + RJUMPV opcodes are invalid as jump destinations. Parameters ---------- @@ -385,17 +280,17 @@ def get_valid_instructions(code: bytes) -> Set[int]: Returns ------- - valid_instructions : Set[int] + valid_opcode_positions : Set[int] The valid jump destinations in the code. """ counter = 0 - valid_instructions = set() + valid_opcode_positions = set() while counter < len(code): try: - opcode = get_opcode(code[counter], EOF.EOF1) + opcode = map_int_to_op(code[counter], Eof.EOF1) except ValueError: - raise InvalidEOF("Invalid opcode in code section") - valid_instructions.add(counter) + raise InvalidEof("Invalid opcode in code section") + valid_opcode_positions.add(counter) counter += 1 @@ -405,39 +300,39 @@ def get_valid_instructions(code: bytes) -> Set[int]: ): push_data_size = opcode.value - Ops.PUSH1.value + 1 if len(code) < counter + push_data_size: - raise InvalidEOF("Push data missing") + raise InvalidEof("Push data missing") counter += push_data_size elif opcode in (Ops.RJUMP, Ops.RJUMPI): if len(code) < counter + 2: - raise InvalidEOF("Relative jump offset missing") + raise InvalidEof("Relative jump offset missing") counter += 2 elif opcode == Ops.RJUMPV: if len(code) < counter + 1: - raise InvalidEOF("max_index missing for RJUMPV") + raise InvalidEof("max_index missing for RJUMPV") max_index = code[counter] num_relative_indices = max_index + 1 counter += 1 for _ in range(num_relative_indices): if len(code) < counter + 2: - raise InvalidEOF("Relative jump indices missing") + raise InvalidEof("Relative jump indices missing") counter += 2 elif opcode == Ops.CALLF: if len(code) < counter + 2: - raise InvalidEOF("CALLF target code section index missing") + raise InvalidEof("CALLF target code section index missing") counter += 2 elif opcode == Ops.DATALOADN: if len(code) < counter + 2: - raise InvalidEOF("DATALOADN offset missing") + raise InvalidEof("DATALOADN offset missing") counter += 2 - return valid_instructions + return valid_opcode_positions def validate_code_section( - eof_meta: EOFMetadata, + eof_meta: EofMetadata, code_section_index: Uint, reached_code_sections: Set[Uint], ) -> None: @@ -446,7 +341,7 @@ def validate_code_section( Parameters ---------- - eof_meta : EOFMetadata + eof_meta : EofMetadata The metadata of the EOF container. code_section_index : Uint The index of the code section to validate. @@ -455,14 +350,14 @@ def validate_code_section( Raises ------ - InvalidEOF + InvalidEof If the code section is invalid. """ code = eof_meta.code_section_contents[code_section_index] - valid_instructions = get_valid_instructions(code) + valid_opcode_positions = get_valid_opcode_positions(code) - for counter in valid_instructions: - opcode = get_opcode(code[counter], EOF.EOF1) + for counter in valid_opcode_positions: + opcode = map_int_to_op(code[counter], Eof.EOF1) # Make sure the bytes encoding relative offset # are available @@ -475,9 +370,9 @@ def validate_code_section( if ( jump_destination < 0 or len(code) < jump_destination + 1 - or jump_destination not in valid_instructions + or jump_destination not in valid_opcode_positions ): - raise InvalidEOF("Invalid jump destination") + raise InvalidEof("Invalid jump destination") elif opcode == Ops.RJUMPV: num_relative_indices = code[counter + 1] + 1 @@ -499,9 +394,9 @@ def validate_code_section( if ( jump_destination < 0 or len(code) < jump_destination + 1 - or jump_destination not in valid_instructions + or jump_destination not in valid_opcode_positions ): - raise InvalidEOF("Invalid jump destination") + raise InvalidEof("Invalid jump destination") elif opcode == Ops.CALLF: target_section_index = Uint.from_be_bytes( @@ -509,28 +404,28 @@ def validate_code_section( ) reached_code_sections.add(target_section_index) if target_section_index >= eof_meta.num_code_sections: - raise InvalidEOF("Invalid target code section index") + raise InvalidEof("Invalid target code section index") elif opcode == Ops.RETF: if code_section_index == 0: - raise InvalidEOF("First code section cannot return") + raise InvalidEof("First code section cannot return") elif opcode == Ops.DATALOADN: offset = Uint.from_be_bytes(code[counter + 1 : counter + 3]) if offset >= eof_meta.data_size: - raise InvalidEOF("Invalid DATALOADN offset") + raise InvalidEof("Invalid DATALOADN offset") -def validate_eof_code(eof_meta: EOFMetadata) -> None: +def validate_eof_code(eof_meta: EofMetadata) -> None: """ Validate the code section of the EOF container. Parameters ---------- - eof_meta : EOFMetadata + eof_meta : EofMetadata The metadata of the EOF container. Raises ------ - InvalidEOF + InvalidEof If the code section is invalid. """ reached_code_sections = {Uint(0)} @@ -541,7 +436,7 @@ def validate_eof_code(eof_meta: EOFMetadata) -> None: for i in range(eof_meta.num_code_sections): if i not in reached_code_sections: - raise InvalidEOF(f"Code section {i} not reachable") + raise InvalidEof(f"Code section {i} not reachable") def validate_eof_container(container: bytes) -> None: @@ -555,7 +450,7 @@ def validate_eof_container(container: bytes) -> None: Raises ------ - InvalidEOF + InvalidEof If the EOF container is invalid. """ # Validate the magic @@ -563,17 +458,17 @@ def validate_eof_container(container: bytes) -> None: len(container) < EOF_MAGIC_LENGTH or container[:EOF_MAGIC_LENGTH] != EOF_MAGIC ): - raise InvalidEOF("Invalid magic") + raise InvalidEof("Invalid magic") if len(container) < EOF_MAGIC_LENGTH + 1: - raise InvalidEOF("EOF version not specified") + raise InvalidEof("EOF version not specified") elif container[EOF_MAGIC_LENGTH] != 1: - raise InvalidEOF("Invalid EOF version") + raise InvalidEof("Invalid EOF version") if len(container) > 2 * MAX_CODE_SIZE: - raise InvalidEOF("EOF container size too long") + raise InvalidEof("EOF container size too long") - eof_meta = validate_header(container) + eof_meta = parse_container_metadata(container, validate=True) validate_body(eof_meta) diff --git a/src/ethereum/prague/vm/exceptions.py b/src/ethereum/prague/vm/exceptions.py index 226e9b8c2b..073ecfedbc 100644 --- a/src/ethereum/prague/vm/exceptions.py +++ b/src/ethereum/prague/vm/exceptions.py @@ -140,7 +140,7 @@ class KZGProofError(ExceptionalHalt): pass -class InvalidEOF(ExceptionalHalt): +class InvalidEof(ExceptionalHalt): """ Raised when the code is not valid EOF. """ diff --git a/src/ethereum/prague/vm/instructions/control_flow.py b/src/ethereum/prague/vm/instructions/control_flow.py index 0531270baa..4476a6a1e8 100644 --- a/src/ethereum/prague/vm/instructions/control_flow.py +++ b/src/ethereum/prague/vm/instructions/control_flow.py @@ -297,11 +297,13 @@ def callf(evm: Evm) -> None: charge_gas(evm, GAS_CALLF) # OPERATION - assert evm.eof_meta is not None + assert evm.eof_metadata is not None target_section_index = Uint.from_be_bytes( evm.code[evm.pc + 1 : evm.pc + 3] ) - target_section = evm.eof_meta.type_section_contents[target_section_index] + target_section = evm.eof_metadata.type_section_contents[ + target_section_index + ] target_inputs = Uint(target_section[0]) target_max_stack_height = Uint.from_be_bytes(target_section[2:]) @@ -319,7 +321,7 @@ def callf(evm: Evm) -> None: # PROGRAM COUNTER evm.current_section_index = target_section_index - evm.code = evm.eof_meta.code_section_contents[target_section_index] + evm.code = evm.eof_metadata.code_section_contents[target_section_index] evm.pc = Uint(0) @@ -342,8 +344,10 @@ def retf(evm: Evm) -> None: pass # PROGRAM COUNTER - assert evm.eof_meta is not None + assert evm.eof_metadata is not None return_stack_item = evm.return_stack.pop() evm.current_section_index = return_stack_item.code_section_index - evm.code = evm.eof_meta.code_section_contents[evm.current_section_index] + evm.code = evm.eof_metadata.code_section_contents[ + evm.current_section_index + ] evm.pc = return_stack_item.offset diff --git a/src/ethereum/prague/vm/instructions/environment.py b/src/ethereum/prague/vm/instructions/environment.py index 8a5816eb82..d6b922c45a 100644 --- a/src/ethereum/prague/vm/instructions/environment.py +++ b/src/ethereum/prague/vm/instructions/environment.py @@ -20,7 +20,7 @@ from ...state import get_account from ...utils.address import to_address from ...vm.memory import buffer_read, memory_write -from .. import EOF, Evm, get_eof_version +from .. import Eof, Evm, get_eof_version from ..exceptions import OutOfBoundsRead from ..gas import ( GAS_BASE, @@ -265,7 +265,7 @@ def codesize(evm: Evm) -> None: charge_gas(evm, GAS_BASE) # OPERATION - push(evm.stack, U256(len(evm.message.container))) + push(evm.stack, U256(len(evm.message.code))) # PROGRAM COUNTER evm.pc += 1 @@ -299,7 +299,7 @@ def codecopy(evm: Evm) -> None: # OPERATION evm.memory += b"\x00" * extend_memory.expand_by - value = buffer_read(evm.message.container, code_start_index, size) + value = buffer_read(evm.message.code, code_start_index, size) memory_write(evm.memory, memory_start_index, value) # PROGRAM COUNTER @@ -353,7 +353,7 @@ def extcodesize(evm: Evm) -> None: # Non-existent accounts default to EMPTY_ACCOUNT, which has empty code. code = get_account(evm.env.state, address).code target_eof_version = get_eof_version(code) - if target_eof_version == EOF.EOF1: + if target_eof_version == Eof.EOF1: codesize = U256(2) else: codesize = U256(len(code)) @@ -399,7 +399,7 @@ def extcodecopy(evm: Evm) -> None: evm.memory += b"\x00" * extend_memory.expand_by code = get_account(evm.env.state, address).code eof_version = get_eof_version(code) - if eof_version == EOF.EOF1: + if eof_version == Eof.EOF1: value = b"\xEF\x00" else: value = buffer_read(code, code_start_index, size) @@ -452,9 +452,9 @@ def returndatacopy(evm: Evm) -> None: evm.memory, [(memory_start_index, size)] ) charge_gas(evm, GAS_VERY_LOW + copy_gas_cost + extend_memory.cost) - if evm.eof == EOF.LEGACY and Uint(return_data_start_position) + Uint( - size - ) > len(evm.return_data): + if evm.eof_version == Eof.LEGACY and Uint( + return_data_start_position + ) + Uint(size) > len(evm.return_data): raise OutOfBoundsRead evm.memory += b"\x00" * extend_memory.expand_by @@ -490,7 +490,7 @@ def extcodehash(evm: Evm) -> None: if account == EMPTY_ACCOUNT: codehash = U256(0) - elif get_eof_version(account.code) == EOF.EOF1: + elif get_eof_version(account.code) == Eof.EOF1: codehash = U256.from_be_bytes(keccak256(b"\xEF\x00")) else: codehash = U256.from_be_bytes(keccak256(account.code)) @@ -640,9 +640,9 @@ def dataload(evm: Evm) -> None: charge_gas(evm, GAS_DATALOAD) # OPERATION - assert evm.eof_meta is not None + assert evm.eof_metadata is not None value = U256.from_be_bytes( - buffer_read(evm.eof_meta.data_section_contents, offset, U256(32)) + buffer_read(evm.eof_metadata.data_section_contents, offset, U256(32)) ) push(evm.stack, value) @@ -667,10 +667,10 @@ def dataload_n(evm: Evm) -> None: charge_gas(evm, GAS_DATALOADN) # OPERATION - assert evm.eof_meta is not None + assert evm.eof_metadata is not None offset = U256.from_be_bytes(evm.code[evm.pc + 1 : evm.pc + 3]) value = U256.from_be_bytes( - buffer_read(evm.eof_meta.data_section_contents, offset, U256(32)) + buffer_read(evm.eof_metadata.data_section_contents, offset, U256(32)) ) push(evm.stack, value) @@ -695,8 +695,8 @@ def datasize(evm: Evm) -> None: charge_gas(evm, GAS_DATASIZE) # OPERATION - assert evm.eof_meta is not None - push(evm.stack, U256(len(evm.eof_meta.data_section_contents))) + assert evm.eof_metadata is not None + push(evm.stack, U256(len(evm.eof_metadata.data_section_contents))) # PROGRAM COUNTER evm.pc += 1 @@ -724,9 +724,9 @@ def datacopy(evm: Evm) -> None: charge_gas(evm, copy_gas_cost + extend_memory.cost) # OPERATION - assert evm.eof_meta is not None + assert evm.eof_metadata is not None evm.memory += b"\x00" * extend_memory.expand_by - value = buffer_read(evm.eof_meta.data_section_contents, offset, size) + value = buffer_read(evm.eof_metadata.data_section_contents, offset, size) memory_write(evm.memory, memory_start_index, value) # PROGRAM COUNTER diff --git a/src/ethereum/prague/vm/instructions/system.py b/src/ethereum/prague/vm/instructions/system.py index 4e66750499..5f28c140bc 100644 --- a/src/ethereum/prague/vm/instructions/system.py +++ b/src/ethereum/prague/vm/instructions/system.py @@ -31,8 +31,8 @@ to_address_without_mask, ) from .. import ( - EOF, MAX_CODE_SIZE, + Eof, Evm, Message, get_eof_version, @@ -121,7 +121,7 @@ def generic_create( gas=create_message_gas, value=endowment, data=b"", - container=call_data, + code=call_data, current_target=contract_address, depth=evm.message.depth + 1, code_address=None, @@ -306,7 +306,7 @@ def generic_call( gas=gas, value=value, data=call_data, - container=container, + code=container, current_target=to, depth=evm.message.depth + 1, code_address=code_address, @@ -795,7 +795,7 @@ def ext_call(evm: Evm) -> None: gas=Uint(message_call_gas), value=value, data=call_data, - container=container, + code=container, current_target=target_address, depth=evm.message.depth + 1, code_address=target_address, @@ -872,7 +872,7 @@ def ext_delegatecall(evm: Evm) -> None: if ( message_call_gas < EOF_CALL_MIN_CALLEE_GAS or evm.message.depth + 1 > STACK_DEPTH_LIMIT - or get_eof_version(container) == EOF.LEGACY + or get_eof_version(container) == Eof.LEGACY ): push(evm.stack, U256(1)) else: @@ -884,7 +884,7 @@ def ext_delegatecall(evm: Evm) -> None: gas=Uint(message_call_gas), value=U256(0), data=call_data, - container=container, + code=container, current_target=evm.message.current_target, depth=evm.message.depth + 1, code_address=target_address, @@ -971,7 +971,7 @@ def ext_staticcall(evm: Evm) -> None: gas=Uint(message_call_gas), value=U256(0), data=call_data, - container=container, + code=container, current_target=target_address, depth=evm.message.depth + 1, code_address=target_address, diff --git a/src/ethereum/prague/vm/interpreter.py b/src/ethereum/prague/vm/interpreter.py index fd604582ca..c52f52f8b4 100644 --- a/src/ethereum/prague/vm/interpreter.py +++ b/src/ethereum/prague/vm/interpreter.py @@ -43,17 +43,17 @@ ) from ..vm.gas import GAS_CODE_DEPOSIT, charge_gas from ..vm.precompiled_contracts.mapping import PRE_COMPILED_CONTRACTS -from . import EOF, MAX_CODE_SIZE, Environment, Evm, Message, get_eof_version +from . import MAX_CODE_SIZE, Environment, Eof, Evm, Message, get_eof_version from .eof import ( - get_opcode, - meta_from_valid_eof1_container, + map_int_to_op, + parse_container_metadata, validate_eof_container, ) from .exceptions import ( AddressCollision, ExceptionalHalt, InvalidContractPrefix, - InvalidEOF, + InvalidEof, InvalidOpcode, OutOfGasError, Revert, @@ -187,7 +187,7 @@ def process_create_message(message: Message, env: Environment) -> Evm: contract_code_gas = len(contract_code) * GAS_CODE_DEPOSIT try: if len(contract_code) > 0: - if get_eof_version(contract_code) == EOF.EOF1: + if get_eof_version(contract_code) == Eof.EOF1: validate_eof_container(contract_code) elif contract_code[0] == 0xEF: raise InvalidContractPrefix @@ -262,19 +262,26 @@ def execute_code(message: Message, env: Environment) -> Evm: evm: `ethereum.vm.EVM` Items containing execution specific objects """ - container = message.container - valid_jump_destinations = get_valid_jump_destinations(container) + code_or_container = message.code - eof_version = get_eof_version(container) + eof_version = get_eof_version(code_or_container) - if eof_version == EOF.EOF1: - eof_meta = meta_from_valid_eof1_container(container) - code = eof_meta.code_section_contents[0] - elif eof_version == EOF.LEGACY: - eof_meta = None - code = container + if eof_version == Eof.EOF1: + container = code_or_container + eof_metadata = parse_container_metadata( + code_or_container, validate=False + ) + code = eof_metadata.code_section_contents[0] + valid_jump_destinations = set() + elif eof_version == Eof.LEGACY: + container = None + eof_metadata = None + code = code_or_container + valid_jump_destinations = get_valid_jump_destinations( + code_or_container + ) else: - raise InvalidEOF + raise InvalidEof evm = Evm( pc=Uint(0), @@ -295,8 +302,9 @@ def execute_code(message: Message, env: Environment) -> Evm: error=None, accessed_addresses=message.accessed_addresses, accessed_storage_keys=message.accessed_storage_keys, - eof=eof_version, - eof_meta=eof_meta, + eof_version=eof_version, + eof_container=container, + eof_metadata=eof_metadata, current_section_index=Uint(0), return_stack=[], ) @@ -309,7 +317,7 @@ def execute_code(message: Message, env: Environment) -> Evm: while evm.running and evm.pc < len(evm.code): try: - op = get_opcode(evm.code[evm.pc], evm.eof) + op = map_int_to_op(evm.code[evm.pc], evm.eof_version) except ValueError: raise InvalidOpcode(evm.code[evm.pc]) diff --git a/src/ethereum/prague/vm/runtime.py b/src/ethereum/prague/vm/runtime.py index d0055f829c..67a7cda453 100644 --- a/src/ethereum/prague/vm/runtime.py +++ b/src/ethereum/prague/vm/runtime.py @@ -15,11 +15,10 @@ from ethereum.base_types import Uint -from . import EOF, get_eof_version from .instructions import Ops -def get_valid_jump_destinations(container: bytes) -> Set[Uint]: +def get_valid_jump_destinations(code: bytes) -> Set[Uint]: """ Analyze the evm code to obtain the set of valid jump destinations. @@ -33,22 +32,20 @@ def get_valid_jump_destinations(container: bytes) -> Set[Uint]: Parameters ---------- - container : - The container for the code. For non-EOF contracts, this is the code. + code : + The EVM code which is to be executed. Returns ------- valid_jump_destinations: `Set[Uint]` The set of valid jump destinations in the code. """ - valid_jump_destinations: Set[Uint] = set() - if get_eof_version(container) != EOF.LEGACY: - return valid_jump_destinations + valid_jump_destinations = set() pc = Uint(0) - while pc < len(container): + while pc < len(code): try: - current_opcode = Ops(container[pc]) + current_opcode = Ops(code[pc]) except ValueError: # Skip invalid opcodes, as they don't affect the jumpdest # analysis. Nevertheless, such invalid opcodes would be caught diff --git a/tests/helpers/__init__.py b/tests/helpers/__init__.py index 77cfbd04c6..99ad2740ef 100644 --- a/tests/helpers/__init__.py +++ b/tests/helpers/__init__.py @@ -17,7 +17,7 @@ }, "latest_fork_tests": { "url": "https://github.com/gurukamath/latest_fork_tests.git", - "commit_hash": "45600f3", + "commit_hash": "7a15a20", "fixture_path": "tests/fixtures/latest_fork_tests", }, }