Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions src/ethereum_test_forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,11 @@ def pre_allocation(cls, block_number: int = 0, timestamp: int = 0) -> Mapping:
Cancun requires pre-allocation of the beacon root contract for EIP-4788
"""
new_allocation = {
0xBEAC00DDB15F3B6D645C48263DC93862413A222D: {
0xBEAC020008AFF7331C0A389CB2AAB67597567D7A: {
"nonce": 1,
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604457602036146024575f5f"
"fd5b620180005f350680545f35146037575f5ffd5b6201800001545f5260205ff35b620180004206"
"4281555f359062018000015500",
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5f"
"fd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f"
"5ffd5b62001fff42064281555f359062001fff015500",
}
}
return new_allocation | super(Cancun, cls).pre_allocation(block_number, timestamp)
Expand Down
53 changes: 0 additions & 53 deletions tests/cancun/eip4788_beacon_root/common.py

This file was deleted.

24 changes: 9 additions & 15 deletions tests/cancun/eip4788_beacon_root/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@
)
from ethereum_test_tools.vm.opcode import Opcodes as Op

from .common import (
BEACON_ROOT_CONTRACT_ADDRESS,
BEACON_ROOT_CONTRACT_CALL_GAS,
HISTORY_BUFFER_LENGTH,
SYSTEM_ADDRESS,
expected_storage,
)
from .spec import Spec, SpecHelpers

BLOB_COMMITMENT_VERSION_KZG = 1

Expand Down Expand Up @@ -88,7 +82,7 @@ def call_value() -> int: # noqa: D103

@pytest.fixture
def call_gas() -> int: # noqa: D103
return BEACON_ROOT_CONTRACT_CALL_GAS
return Spec.BEACON_ROOTS_CALL_GAS


@pytest.fixture
Expand All @@ -108,7 +102,7 @@ def contract_call_account(call_type: Op, call_value: int, call_gas: int) -> Acco
0x00, # store the result of the contract call in storage[0]
call_type(
call_gas,
BEACON_ROOT_CONTRACT_ADDRESS,
Spec.BEACON_ROOTS_ADDRESS,
call_value,
args_start,
args_length,
Expand All @@ -122,7 +116,7 @@ def contract_call_account(call_type: Op, call_value: int, call_gas: int) -> Acco
0x00,
call_type(
call_gas,
BEACON_ROOT_CONTRACT_ADDRESS,
Spec.BEACON_ROOTS_ADDRESS,
args_start,
args_length,
return_start,
Expand Down Expand Up @@ -197,7 +191,7 @@ def pre(
caller_address: contract_call_account,
}
if system_address_balance > 0:
pre_alloc[to_address(SYSTEM_ADDRESS)] = Account(
pre_alloc[to_address(Spec.SYSTEM_ADDRESS)] = Account(
nonce=0,
balance=system_address_balance,
)
Expand Down Expand Up @@ -225,10 +219,10 @@ def access_list(auto_access_list: bool, timestamp: int) -> List[AccessList]:
if auto_access_list:
return [
AccessList(
address=BEACON_ROOT_CONTRACT_ADDRESS,
address=Spec.BEACON_ROOTS_ADDRESS,
storage_keys=[
timestamp,
timestamp + HISTORY_BUFFER_LENGTH,
timestamp + Spec.HISTORY_BUFFER_LENGTH,
],
),
]
Expand Down Expand Up @@ -264,7 +258,7 @@ def tx(
"""
Prepares transaction to call the beacon root contract caller account.
"""
to = BEACON_ROOT_CONTRACT_ADDRESS if call_beacon_root_contract else tx_to_address
to = Spec.BEACON_ROOTS_ADDRESS if call_beacon_root_contract else tx_to_address
kwargs: Dict = {
"ty": tx_type,
"nonce": 0,
Expand Down Expand Up @@ -307,7 +301,7 @@ def post(
"""
storage = Storage()
if not call_beacon_root_contract:
storage = expected_storage(
storage = SpecHelpers.expected_storage(
beacon_root=beacon_root,
valid_call=valid_call,
valid_input=valid_input,
Expand Down
78 changes: 78 additions & 0 deletions tests/cancun/eip4788_beacon_root/spec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
Defines EIP-4788 specification constants and functions.
"""
from dataclasses import dataclass

from ethereum_test_tools import Storage


@dataclass(frozen=True)
class ReferenceSpec:
"""
Defines the reference spec version and git path.
"""

git_path: str
version: str


ref_spec_4788 = ReferenceSpec("EIPS/eip-4788.md", "e7608fe8ac8a60934ca874f5aab7d5c1f4ff7782")


# Constants
@dataclass(frozen=True)
class Spec:
"""
Parameters from the EIP-4788 specifications as defined at
https://eips.ethereum.org/EIPS/eip-4788#specification
"""

BEACON_ROOTS_ADDRESS = 0xBEAC020008AFF7331C0A389CB2AAB67597567D7A
BEACON_ROOTS_CALL_GAS = 100_000
BEACON_ROOTS_DEPLOYER_ADDRESS = 0x4F6DA9BA1CC8C37D9CE52311D4BAFFC43BA42D0E
HISTORY_BUFFER_LENGTH = 8_191
SYSTEM_ADDRESS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
FORK_TIMESTAMP = 15_000 # ShanghaiToCancun timestamp


@dataclass(frozen=True)
class SpecHelpers:
"""
Helper functions closely related to the EIP-4788 specification.
"""

def timestamp_index(self, timestamp: int) -> int:
"""
Derive the timestamp index into the timestamp ring buffer.
"""
return timestamp % Spec.HISTORY_BUFFER_LENGTH

def root_index(self, timestamp: int) -> int:
"""
Derive the root index into the root ring buffer.
"""
return self.timestamp_index(timestamp) + Spec.HISTORY_BUFFER_LENGTH

@staticmethod
def expected_storage(
*,
beacon_root: bytes,
valid_call: bool,
valid_input: bool,
) -> Storage:
"""
Derives the expected storage for a given beacon root contract call
dependent on:
- success or failure of the call
- validity of the timestamp input used within the call
"""
# By default assume the call is unsuccessful and all keys are zero
storage = Storage({k: 0 for k in range(4)})
if valid_call and valid_input:
# beacon root contract call is successful
storage[0] = 1
storage[1] = beacon_root
storage[2] = 32
storage[3] = beacon_root

return storage
84 changes: 77 additions & 7 deletions tests/cancun/eip4788_beacon_root/test_beacon_root_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,29 @@

import pytest

from ethereum_test_tools import Environment, StateTestFiller, Transaction
from ethereum_test_tools import (
Account,
Environment,
StateTestFiller,
Storage,
Transaction,
to_address,
)
from ethereum_test_tools.vm.opcode import Opcodes as Op

from .common import BEACON_ROOT_CONTRACT_CALL_GAS, REF_SPEC_4788_GIT_PATH, REF_SPEC_4788_VERSION
from .spec import Spec, ref_spec_4788

REFERENCE_SPEC_GIT_PATH = REF_SPEC_4788_GIT_PATH
REFERENCE_SPEC_VERSION = REF_SPEC_4788_VERSION
REFERENCE_SPEC_GIT_PATH = ref_spec_4788.git_path
REFERENCE_SPEC_VERSION = ref_spec_4788.version


@pytest.mark.parametrize(
"call_gas, valid_call",
[
pytest.param(BEACON_ROOT_CONTRACT_CALL_GAS, True),
pytest.param(BEACON_ROOT_CONTRACT_CALL_GAS + 1, True),
pytest.param(Spec.BEACON_ROOTS_CALL_GAS, True),
pytest.param(Spec.BEACON_ROOTS_CALL_GAS + 1, True),
pytest.param(
BEACON_ROOT_CONTRACT_CALL_GAS - 1,
Spec.BEACON_ROOTS_CALL_GAS - 1,
False,
marks=pytest.mark.xfail(reason="gas calculation is incorrect"), # TODO
),
Expand Down Expand Up @@ -220,3 +227,66 @@ def test_tx_to_beacon_root_contract(
txs=[tx],
post=post,
)


@pytest.mark.parametrize(
"tx_data",
[
pytest.param(int.to_bytes(0, length=32, byteorder="big"), id="zero_calldata"),
],
)
@pytest.mark.parametrize("valid_call,valid_input", [(False, False)])
@pytest.mark.parametrize("timestamp", [12])
@pytest.mark.valid_from("Cancun")
def test_invalid_beacon_root_calldata_value(
state_test: StateTestFiller,
env: Environment,
pre: Dict,
tx: Transaction,
post: Dict,
):
"""
Tests the beacon root contract call using invalid input values:
- zero calldata.

Contract should revert.
"""
state_test(
env=env,
pre=pre,
txs=[tx],
post=post,
)


@pytest.mark.parametrize("timestamp", [12])
@pytest.mark.valid_from("Cancun")
def test_beacon_root_selfdestruct(
state_test: StateTestFiller,
env: Environment,
pre: Dict,
tx: Transaction,
post: Dict,
):
"""
Tests that self destructing the beacon root address transfers actors balance correctly.
"""
# self destruct actor
pre[to_address(0x1337)] = Account(
code=Op.SELFDESTRUCT(Spec.BEACON_ROOTS_ADDRESS),
balance=0xBA1,
)
# self destruct caller
pre[to_address(0xCC)] = Account(
code=Op.CALL(100000, Op.PUSH20(to_address(0x1337)), 0, 0, 0, 0, 0)
+ Op.SSTORE(0, Op.BALANCE(Spec.BEACON_ROOTS_ADDRESS)),
)
post[to_address(0xCC)] = Account(
storage=Storage({0: 0xBA1}),
)
state_test(
env=env,
pre=pre,
txs=[tx, Transaction(nonce=1, to=to_address(0xCC), gas_limit=100000, gas_price=10)],
post=post,
)
Loading