From f672f5dba205481dbc03688cb3e5650fd23f0eea Mon Sep 17 00:00:00 2001 From: Guruprasad Kamath Date: Tue, 9 Jul 2024 16:40:58 +0200 Subject: [PATCH] Implement EIP-7480 --- src/ethereum/prague/vm/eof.py | 8 ++ src/ethereum/prague/vm/gas.py | 3 + .../prague/vm/instructions/__init__.py | 4 + .../prague/vm/instructions/environment.py | 112 ++++++++++++++++++ tests/prague/test_eof.py | 1 + tests/prague/test_state_transition.py | 1 + 6 files changed, 129 insertions(+) diff --git a/src/ethereum/prague/vm/eof.py b/src/ethereum/prague/vm/eof.py index ed297bbc25..6d1dffea7d 100644 --- a/src/ethereum/prague/vm/eof.py +++ b/src/ethereum/prague/vm/eof.py @@ -428,6 +428,10 @@ def get_valid_instructions(code: bytes) -> Set[int]: if len(code) < counter + 2: raise InvalidEOF("CALLF target code section index missing") counter += 2 + elif opcode == Ops.DATALOADN: + if len(code) < counter + 2: + raise InvalidEOF("DATALOADN offset missing") + counter += 2 return valid_instructions @@ -509,6 +513,10 @@ def validate_code_section( elif opcode == Ops.RETF: if code_section_index == 0: 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") def validate_eof_code(eof_meta: EOFMetadata) -> None: diff --git a/src/ethereum/prague/vm/gas.py b/src/ethereum/prague/vm/gas.py index 49ce6a1cd6..c38a79f18a 100644 --- a/src/ethereum/prague/vm/gas.py +++ b/src/ethereum/prague/vm/gas.py @@ -73,6 +73,9 @@ GAS_RETF = Uint(3) EOF_CALL_MIN_RETAINED_GAS = Uint(5000) EOF_CALL_MIN_CALLEE_GAS = Uint(2300) +GAS_DATALOAD = Uint(4) +GAS_DATALOADN = Uint(3) +GAS_DATASIZE = Uint(2) TARGET_BLOB_GAS_PER_BLOCK = U64(393216) GAS_PER_BLOB = Uint(2**17) diff --git a/src/ethereum/prague/vm/instructions/__init__.py b/src/ethereum/prague/vm/instructions/__init__.py index 07c44873c8..717a391590 100644 --- a/src/ethereum/prague/vm/instructions/__init__.py +++ b/src/ethereum/prague/vm/instructions/__init__.py @@ -383,6 +383,10 @@ class Ops(enum.Enum): Ops.LOG2: log_instructions.log2, Ops.LOG3: log_instructions.log3, Ops.LOG4: log_instructions.log4, + Ops.DATALOAD: environment_instructions.dataload, + Ops.DATALOADN: environment_instructions.dataload_n, + Ops.DATASIZE: environment_instructions.datasize, + Ops.DATACOPY: environment_instructions.datacopy, Ops.RJUMP: control_flow_instructions.rjump, Ops.RJUMPI: control_flow_instructions.rjumpi, Ops.RJUMPV: control_flow_instructions.rjumpv, diff --git a/src/ethereum/prague/vm/instructions/environment.py b/src/ethereum/prague/vm/instructions/environment.py index e1b52ea4ea..f589f35f3a 100644 --- a/src/ethereum/prague/vm/instructions/environment.py +++ b/src/ethereum/prague/vm/instructions/environment.py @@ -27,6 +27,9 @@ GAS_BLOBHASH_OPCODE, GAS_COLD_ACCOUNT_ACCESS, GAS_COPY, + GAS_DATALOAD, + GAS_DATALOADN, + GAS_DATASIZE, GAS_FAST_STEP, GAS_RETURN_DATA_COPY, GAS_VERY_LOW, @@ -623,3 +626,112 @@ def returndataload(evm: Evm) -> None: # PROGRAM COUNTER evm.pc += 1 + + +def dataload(evm: Evm) -> None: + """ + Pushes 32-byte word to stack. + + Parameters + ---------- + evm : + The current EVM frame. + """ + # STACK + offset = pop(evm.stack) + + # GAS + charge_gas(evm, GAS_DATALOAD) + + # OPERATION + assert evm.eof_meta is not None + value = U256.from_be_bytes( + buffer_read(evm.eof_meta.data_section_contents, offset, U256(32)) + ) + push(evm.stack, value) + + # PROGRAM COUNTER + evm.pc += 1 + + +def dataload_n(evm: Evm) -> None: + """ + Pushes 32-byte word to stack where the word is addressed + by a static immediate argument + + Parameters + ---------- + evm : + The current EVM frame. + """ + # STACK + pass + + # GAS + charge_gas(evm, GAS_DATALOADN) + + # OPERATION + assert evm.eof_meta 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)) + ) + push(evm.stack, value) + + # PROGRAM COUNTER + # 1 + 2 bytes of immediate data + evm.pc += 3 + + +def datasize(evm: Evm) -> None: + """ + Pushes the data section size. + + Parameters + ---------- + evm : + The current EVM frame. + """ + # STACK + pass + + # GAS + charge_gas(evm, GAS_DATASIZE) + + # OPERATION + assert evm.eof_meta is not None + push(evm.stack, U256(len(evm.eof_meta.data_section_contents))) + + # PROGRAM COUNTER + evm.pc += 1 + + +def datacopy(evm: Evm) -> None: + """ + Copies a segment of data section to memory. + + Parameters + ---------- + evm : + The current EVM frame. + """ + # STACK + memory_start_index = pop(evm.stack) + offset = pop(evm.stack) + size = pop(evm.stack) + + # GAS + copy_gas_cost = 3 + 3 * ((Uint(size) + 31) // 32) + extend_memory = calculate_gas_extend_memory( + evm.memory, [(memory_start_index, size)] + ) + charge_gas(evm, copy_gas_cost + extend_memory.cost) + + # OPERATION + assert evm.eof_meta is not None + evm.memory += b"\x00" * extend_memory.expand_by + value = buffer_read(evm.eof_meta.data_section_contents, offset, size) + memory_write(evm.memory, memory_start_index, value) + + # PROGRAM COUNTER + evm.pc += 1 diff --git a/tests/prague/test_eof.py b/tests/prague/test_eof.py index f6befe145d..176ab0cf88 100644 --- a/tests/prague/test_eof.py +++ b/tests/prague/test_eof.py @@ -12,6 +12,7 @@ TEST_DIRS = ( "tests/fixtures/latest_fork_tests/eof_tests/prague/eip7692_eof_v1/eip3540_eof_v1", "tests/fixtures/latest_fork_tests/eof_tests/prague/eip7692_eof_v1/eip4200_relative_jumps", + "tests/fixtures/latest_fork_tests/eof_tests/prague/eip7692_eof_v1/eip7480_data_section", ) diff --git a/tests/prague/test_state_transition.py b/tests/prague/test_state_transition.py index 2bfa262133..44db2281e8 100644 --- a/tests/prague/test_state_transition.py +++ b/tests/prague/test_state_transition.py @@ -97,6 +97,7 @@ "tests/fixtures/latest_fork_tests/blockchain_tests/prague/eip7692_eof_v1/eip3540_eof_v1", "tests/fixtures/latest_fork_tests/blockchain_tests/prague/eip7692_eof_v1/eip4200_relative_jumps", "tests/fixtures/latest_fork_tests/blockchain_tests/prague/eip7692_eof_v1/eip7069_extcall", + "tests/fixtures/latest_fork_tests/blockchain_tests/prague/eip7692_eof_v1/eip7480_data_section", )