Skip to content

Commit

Permalink
tests/cancun/eip_4788: Update predeploy address(0xbEAC02..7d7a), add …
Browse files Browse the repository at this point in the history
…additional tests (ethereum#297)

* tests/cancun/eip_4788: Update naming to match spec.

* tests/cancun/eip_4788: Update history buffer length.

* tests/cancun/eip_4788: Add todo for remaining values to update.

* tests/cancun/eip_4788: Add test for zero calldata value.

* forks: Latest Beacon Root Update to 0xBEA...BCA

* tests/cancun: Latest Beacon Root Update to 0xBEA...BCA

* refactor: wrap specification params in a Spec class

* tests/cancun/eip_4788|src/forks: final beacon root address update.

* tests/cancun/eip_4788: add small self-destruct test.

* tests/cancun/eip-4788: deployer address in spec

---------

Co-authored-by: Mario Vega <[email protected]>
Co-authored-by: danceratopz <[email protected]>
  • Loading branch information
3 people authored Sep 19, 2023
1 parent 789de47 commit 805980d
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 134 deletions.
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

0 comments on commit 805980d

Please sign in to comment.