From 3f4b674e0d39195836d75d9fc23cfb87c44574bc Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 25 Feb 2025 13:54:30 -0300 Subject: [PATCH 01/24] test Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/__init__.py | 6 + tests/verkle/eip7748/accounts.py | 203 +++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 tests/verkle/eip7748/__init__.py create mode 100644 tests/verkle/eip7748/accounts.py diff --git a/tests/verkle/eip7748/__init__.py b/tests/verkle/eip7748/__init__.py new file mode 100644 index 00000000000..2b7292f02eb --- /dev/null +++ b/tests/verkle/eip7748/__init__.py @@ -0,0 +1,6 @@ +""" +abstract: Tests [EIP-7748: State conversion to Verkle Tree] +(https://eips.ethereum.org/EIPS/eip-7748) + Tests for [EIP-7748: State conversion to Verkle Tree] + (https://eips.ethereum.org/EIPS/eip-7748). +""" diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py new file mode 100644 index 00000000000..e34a0a15cd7 --- /dev/null +++ b/tests/verkle/eip7748/accounts.py @@ -0,0 +1,203 @@ +""" +abstract: Tests [EIP-7748: State conversion to Verkle Tree] +(https://eips.ethereum.org/EIPS/eip-7748) + Tests for [EIP-7748: State conversion to Verkle Tree] + (https://eips.ethereum.org/EIPS/eip-7748). +""" + +import pytest + +from ethereum_test_tools import ( + Account, + Address, + Block, + BlockchainTestFiller, + Environment, +) +from ethereum_test_tools.vm.opcode import Opcodes as Op + +REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7748.md" +REFERENCE_SPEC_VERSION = "TODO" + +# List of addressed ordered by MPT tree key. +# 03601462093b5945d1676df093446790fd31b20e7b12a2e8e5e09d068109616b +Account0 = Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b") +# 0e195438d9f92eb191032b5f660d42a22255c9c417248f092c1f83f3a36b29ba +Account1 = Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0e") +# 6a7fc6037f7a0dca7004c2cd41d87bfd929be7eb0d31903b238839e8e7aaf897 +Account2 = Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0a") +# 6a8737909ea3e92b0d47328d70aff338c526832b32362eca8692126c1f399846 +Account3 = Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0d") +# d3bd43970708294fd4d78893c4e7c2fed43c8cd505e9c9516e1f11e79f574598 +Account4 = Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0f") + + +@pytest.mark.valid_from("Verkle") +@pytest.mark.parametrize( + "stride, num_expected_blocks", + [ + (1, 3), + (2, 2), + (3, 1), + ], +) +def test_eoa(blockchain_test: BlockchainTestFiller, stride: int, num_expected_blocks: int): + """ + Test only EOA account conversion. + """ + pre_state = { + Account0: Account(balance=1000), + Account1: Account(balance=2000), + Account2: Account(balance=3000), + } + _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) + + +@pytest.mark.valid_from("Verkle") +@pytest.mark.parametrize( + "contract_length", + [ + 0, + 1, + 128 * 31, + 130 * 31, + ], + ids=[ + "empty", + "in_header", + "header_perfect_fit", + "bigger_than_header", + ], +) +@pytest.mark.parametrize( + "fcb, stride, num_expected_blocks", + [ + (True, 1, 6), + (True, 2, 3), + (True, 3, 2), + (True, 4, 2), + (True, 5, 2), + (True, 6, 1), + (False, 1, 3), + (False, 2, 2), + (False, 3, 1), + ], +) +def test_full_contract( + blockchain_test: BlockchainTestFiller, + contract_length: int, + fcb: bool, + stride: int, + num_expected_blocks: int, +): + """ + Test contract account full/partial migration cases. + """ + pre_state = {} + if not fcb: + pre_state[Account0] = Account(balance=1000) + pre_state[Account1] = Account(balance=1001) + pre_state[Account2] = Account(balance=1002) + + pre_state[Account3] = Account( + balance=2000, + code=Op.STOP * contract_length, + storage={0: 0x1, 1: 0x2}, + ) + + _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) + + +@pytest.mark.valid_from("Verkle") +@pytest.mark.parametrize( + "fcb, stride, num_expected_blocks", + [ + (True, 1, 2), + (True, 2, 1), + (False, 1, 1), + (False, 2, 1), + ], +) +def test_empty_account( + blockchain_test: BlockchainTestFiller, + fcb: bool, + stride: int, + num_expected_blocks: int, +): + """ + Test EIP-161 accounts. + """ + pre_state = {} + if not fcb: + pre_state[Account0] = Account(balance=1000) + + # Empty account (EIP-161) + pre_state[Account1] = Account( + balance=0, + nonce=0, + storage={0: 0x1, 1: 0x2}, + ) + + pre_state[Account2] = Account(balance=1001) + + _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) + + +@pytest.mark.valid_from("Verkle") +@pytest.mark.parametrize( + "fcb, stride, num_expected_blocks", + [ + (True, 1, 2), + (True, 2, 1), + (False, 1, 1), + ], +) +def test_last_conversion_block( + blockchain_test: BlockchainTestFiller, + fcb: bool, + stride: int, + num_expected_blocks: int, +): + """ + Test last conversion block scenario. + """ + pre_state = {} + if not fcb: + pre_state[Account0] = Account(balance=1000) + + # Empty account (EIP-161) + pre_state[Account1] = Account( + balance=0, + nonce=0, + storage={0: 0x1, 1: 0x2}, + ) + + _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) + + +def _state_conversion( + blockchain_test: BlockchainTestFiller, + pre_state: dict[Address, Account], + stride: int, + num_expected_blocks: int, +): + # TODO: test library should allow passing stride + env = Environment( + fee_recipient="0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + difficulty=0x20000, + gas_limit=10000000000, + ) + + blocks: list[Block] = [] + for i in range(num_expected_blocks): + blocks.append(Block(txs=[])) + + # TODO: witness assertion + # TODO: see if possible last block switch to finished conversion + + blockchain_test( + genesis_environment=env, + pre=pre_state, + post=pre_state.copy(), + blocks=blocks, + ) From c5205f6fb92511524cfed0835c761be1d0a09959 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 27 Feb 2025 11:38:41 +0000 Subject: [PATCH 02/24] temp: disable system contract addition Signed-off-by: Ignacio Hagopian --- src/ethereum_test_forks/forks/forks.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ethereum_test_forks/forks/forks.py b/src/ethereum_test_forks/forks/forks.py index a5f5d426a8a..404f7f02a65 100644 --- a/src/ethereum_test_forks/forks/forks.py +++ b/src/ethereum_test_forks/forks/forks.py @@ -989,13 +989,13 @@ def pre_allocation_blockchain(cls) -> Mapping: type tests. """ new_allocation = { - Address(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE): { - "nonce": 1, - "code": ( - "0x60203611603157600143035f35116029575f35612000014311602957612000" - "5f3506545f5260205ff35b5f5f5260205ff35b5f5ffd00" - ), - } + # Address(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE): { + # "nonce": 1, + # "code": ( + # "0x60203611603157600143035f35116029575f35612000014311602957612000" + # "5f3506545f5260205ff35b5f5f5260205ff35b5f5ffd00" + # ), + # } } # TODO: Utilize when testing for large init MPT # return VERKLE_PRE_ALLOCATION | super(Shanghai, cls).pre_allocation() From c97902a3077be5bd33bb1e6ca217980eaa9c0b48 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 27 Feb 2025 12:00:05 +0000 Subject: [PATCH 03/24] eip7748: add eoa test cases Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/accounts.py | 275 ++++++++++++++++--------------- 1 file changed, 144 insertions(+), 131 deletions(-) diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index e34a0a15cd7..3b8d32f27b7 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -6,6 +6,8 @@ """ import pytest +import math +import sys from ethereum_test_tools import ( Account, @@ -31,155 +33,166 @@ # d3bd43970708294fd4d78893c4e7c2fed43c8cd505e9c9516e1f11e79f574598 Account4 = Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0f") +accounts = [Account0, Account1, Account2, Account3, Account4] -@pytest.mark.valid_from("Verkle") -@pytest.mark.parametrize( - "stride, num_expected_blocks", - [ - (1, 3), - (2, 2), - (3, 1), - ], -) -def test_eoa(blockchain_test: BlockchainTestFiller, stride: int, num_expected_blocks: int): - """ - Test only EOA account conversion. - """ - pre_state = { - Account0: Account(balance=1000), - Account1: Account(balance=2000), - Account2: Account(balance=3000), - } - _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) - - -@pytest.mark.valid_from("Verkle") -@pytest.mark.parametrize( - "contract_length", - [ - 0, - 1, - 128 * 31, - 130 * 31, - ], - ids=[ - "empty", - "in_header", - "header_perfect_fit", - "bigger_than_header", - ], -) -@pytest.mark.parametrize( - "fcb, stride, num_expected_blocks", - [ - (True, 1, 6), - (True, 2, 3), - (True, 3, 2), - (True, 4, 2), - (True, 5, 2), - (True, 6, 1), - (False, 1, 3), - (False, 2, 2), - (False, 3, 1), - ], -) -def test_full_contract( - blockchain_test: BlockchainTestFiller, - contract_length: int, - fcb: bool, - stride: int, - num_expected_blocks: int, -): - """ - Test contract account full/partial migration cases. - """ - pre_state = {} - if not fcb: - pre_state[Account0] = Account(balance=1000) - pre_state[Account1] = Account(balance=1001) - pre_state[Account2] = Account(balance=1002) - - pre_state[Account3] = Account( - balance=2000, - code=Op.STOP * contract_length, - storage={0: 0x1, 1: 0x2}, - ) - _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) - - -@pytest.mark.valid_from("Verkle") +@pytest.mark.valid_from("EIP6800Transition") @pytest.mark.parametrize( - "fcb, stride, num_expected_blocks", + "stride", [ - (True, 1, 2), - (True, 2, 1), - (False, 1, 1), - (False, 2, 1), + 3, ], ) -def test_empty_account( - blockchain_test: BlockchainTestFiller, - fcb: bool, - stride: int, - num_expected_blocks: int, -): - """ - Test EIP-161 accounts. - """ - pre_state = {} - if not fcb: - pre_state[Account0] = Account(balance=1000) - - # Empty account (EIP-161) - pre_state[Account1] = Account( - balance=0, - nonce=0, - storage={0: 0x1, 1: 0x2}, - ) - - pre_state[Account2] = Account(balance=1001) - - _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) - - -@pytest.mark.valid_from("Verkle") @pytest.mark.parametrize( - "fcb, stride, num_expected_blocks", + "num_accounts", [ - (True, 1, 2), - (True, 2, 1), - (False, 1, 1), + 1, + 2, + 3, + 4, ], ) -def test_last_conversion_block( - blockchain_test: BlockchainTestFiller, - fcb: bool, - stride: int, - num_expected_blocks: int, -): +def test_eoa(blockchain_test: BlockchainTestFiller, stride: int, num_accounts: int): """ - Test last conversion block scenario. + Test only EOA account conversion. """ pre_state = {} - if not fcb: - pre_state[Account0] = Account(balance=1000) - - # Empty account (EIP-161) - pre_state[Account1] = Account( - balance=0, - nonce=0, - storage={0: 0x1, 1: 0x2}, - ) - - _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) + for i in range(num_accounts): + pre_state[accounts[i]] = Account(balance=100 + 1000 * i) + + _state_conversion(blockchain_test, pre_state, stride, math.ceil(num_accounts / stride)) + + +# @pytest.mark.skip("stride config not supported yet") +# @pytest.mark.valid_from("EIP6800Transition") +# @pytest.mark.parametrize( +# "contract_length", +# [ +# 0, +# 1, +# 128 * 31, +# 130 * 31, +# ], +# ids=[ +# "empty", +# "in_header", +# "header_perfect_fit", +# "bigger_than_header", +# ], +# ) +# @pytest.mark.parametrize( +# "fcb, stride, num_expected_blocks", +# [ +# (True, 1, 6), +# (True, 2, 3), +# (True, 3, 2), +# (True, 4, 2), +# (True, 5, 2), +# (True, 6, 1), +# (False, 1, 3), +# (False, 2, 2), +# (False, 3, 1), +# ], +# ) +# def test_full_contract( +# blockchain_test: BlockchainTestFiller, +# contract_length: int, +# fcb: bool, +# stride: int, +# num_expected_blocks: int, +# ): +# """ +# Test contract account full/partial migration cases. +# """ +# pre_state = {} +# if not fcb: +# pre_state[Account0] = Account(balance=1000) +# pre_state[Account1] = Account(balance=1001) +# pre_state[Account2] = Account(balance=1002) + +# pre_state[Account3] = Account( +# balance=2000, +# code=Op.STOP * contract_length, +# storage={0: 0x1, 1: 0x2}, +# ) + +# _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) + + +# @pytest.mark.skip("stride config not supported yet") +# @pytest.mark.valid_from("EIP6800Transition") +# @pytest.mark.parametrize( +# "fcb, stride, num_expected_blocks", +# [ +# (True, 1, 2), +# (True, 2, 1), +# (False, 1, 1), +# (False, 2, 1), +# ], +# ) +# def test_empty_account( +# blockchain_test: BlockchainTestFiller, +# fcb: bool, +# stride: int, +# num_expected_blocks: int, +# ): +# """ +# Test EIP-161 accounts. +# """ +# pre_state = {} +# if not fcb: +# pre_state[Account0] = Account(balance=1000) + +# # Empty account (EIP-161) +# pre_state[Account1] = Account( +# balance=0, +# nonce=0, +# storage={0: 0x1, 1: 0x2}, +# ) + +# pre_state[Account2] = Account(balance=1001) + +# _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) + + +# @pytest.mark.skip("stride config not supported yet") +# @pytest.mark.valid_from("EIP6800Transition") +# @pytest.mark.parametrize( +# "fcb, stride, num_expected_blocks", +# [ +# (True, 1, 2), +# (True, 2, 1), +# (False, 1, 1), +# ], +# ) +# def test_last_conversion_block( +# blockchain_test: BlockchainTestFiller, +# fcb: bool, +# stride: int, +# num_expected_blocks: int, +# ): +# """ +# Test last conversion block scenario. +# """ +# pre_state = {} +# if not fcb: +# pre_state[Account0] = Account(balance=1000) + +# # Empty account (EIP-161) +# pre_state[Account1] = Account( +# balance=0, +# nonce=0, +# storage={0: 0x1, 1: 0x2}, +# ) + +# _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) def _state_conversion( blockchain_test: BlockchainTestFiller, pre_state: dict[Address, Account], stride: int, - num_expected_blocks: int, + num_blocks: int, ): # TODO: test library should allow passing stride env = Environment( @@ -189,7 +202,7 @@ def _state_conversion( ) blocks: list[Block] = [] - for i in range(num_expected_blocks): + for i in range(num_blocks): blocks.append(Block(txs=[])) # TODO: witness assertion From 394407695b986ac09d3fefecf1c1050f68260ef5 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 27 Feb 2025 13:19:44 +0000 Subject: [PATCH 04/24] hack the hack Signed-off-by: Ignacio Hagopian --- src/ethereum_test_specs/blockchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethereum_test_specs/blockchain.py b/src/ethereum_test_specs/blockchain.py index e8cdbbf275a..1a45c14b421 100644 --- a/src/ethereum_test_specs/blockchain.py +++ b/src/ethereum_test_specs/blockchain.py @@ -582,7 +582,7 @@ def generate_block_data( ) transition_tool_output.alloc = previous_alloc # TODO: hack for now, replace with actual witness output once available from t8n - if transition_tool_output.result.verkle_conversion_ended: + if transition_tool_output.result.verkle_conversion_ended and False: witness_parent_root = transition_tool_output.result.parent_state_root transition_tool_output.witness = Witness( verkle_proof=transition_tool_output.result.verkle_proof, From 88c37f13ac382c7b86037cc77f8ce8d0ea6351e5 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 27 Feb 2025 15:51:33 +0000 Subject: [PATCH 05/24] eip7748: generalize test Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/accounts.py | 142 +++++++++++++------------------ 1 file changed, 61 insertions(+), 81 deletions(-) diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index 3b8d32f27b7..357a5794508 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -22,101 +22,81 @@ REFERENCE_SPEC_VERSION = "TODO" # List of addressed ordered by MPT tree key. -# 03601462093b5945d1676df093446790fd31b20e7b12a2e8e5e09d068109616b -Account0 = Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b") -# 0e195438d9f92eb191032b5f660d42a22255c9c417248f092c1f83f3a36b29ba -Account1 = Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0e") -# 6a7fc6037f7a0dca7004c2cd41d87bfd929be7eb0d31903b238839e8e7aaf897 -Account2 = Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0a") -# 6a8737909ea3e92b0d47328d70aff338c526832b32362eca8692126c1f399846 -Account3 = Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0d") -# d3bd43970708294fd4d78893c4e7c2fed43c8cd505e9c9516e1f11e79f574598 -Account4 = Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0f") - -accounts = [Account0, Account1, Account2, Account3, Account4] +accounts = [ + # 03601462093b5945d1676df093446790fd31b20e7b12a2e8e5e09d068109616b + Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"), + # 1fe26fd0b8a197e7b85ed1ead2b52700041c5d465673aa744f3afc4704f83c03 + Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0d"), + # 257f371320e4696a5debc64a489e651fc4565eb07ce0e4d2ce5b6d5b1896d89a + Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0f"), + # 6a7fc6037f7a0dca7004c2cd41d87bfd929be7eb0d31903b238839e8e7aaf897 + Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0a"), + # 85174d7e61a36094fc9b58640ad245d4ab61d888699f3659137171ff2910b6cb + Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0e"), + # b4102152d5f5995b7a017c9db0e186028190faafa4326ac1ecfb2bc817c423c9 + Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf05"), + # e72525e3842ed775ed5c52ffc1520247deae64a217fcb3fb3ddbe59ffeb5949c + Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf03"), +] + + +class AccountConfig: + def __init__(self, code_length: int, storage_slot_count: int): + self.code_length = code_length + self.storage_slots_count = storage_slot_count @pytest.mark.valid_from("EIP6800Transition") @pytest.mark.parametrize( - "stride", + "account_configs", [ - 3, + [AccountConfig(0, 0)], + [AccountConfig(0, 0), AccountConfig(0, 0)], + [AccountConfig(0, 0), AccountConfig(0, 0), AccountConfig(0, 0)], + ], + ids=[ + "One EOA", + "Two EOAs", + "Three EOAs", ], ) @pytest.mark.parametrize( - "num_accounts", + "fill_first_block, stride", [ - 1, - 2, - 3, - 4, + (False, 3), + (True, 3), ], ) -def test_eoa(blockchain_test: BlockchainTestFiller, stride: int, num_accounts: int): +def test_conversions( + blockchain_test: BlockchainTestFiller, + account_configs: list[AccountConfig], + fill_first_block: bool, + stride: int, +): """ - Test only EOA account conversion. + Test conversion cases. """ + conversion_units = 0 pre_state = {} - for i in range(num_accounts): - pre_state[accounts[i]] = Account(balance=100 + 1000 * i) - - _state_conversion(blockchain_test, pre_state, stride, math.ceil(num_accounts / stride)) - - -# @pytest.mark.skip("stride config not supported yet") -# @pytest.mark.valid_from("EIP6800Transition") -# @pytest.mark.parametrize( -# "contract_length", -# [ -# 0, -# 1, -# 128 * 31, -# 130 * 31, -# ], -# ids=[ -# "empty", -# "in_header", -# "header_perfect_fit", -# "bigger_than_header", -# ], -# ) -# @pytest.mark.parametrize( -# "fcb, stride, num_expected_blocks", -# [ -# (True, 1, 6), -# (True, 2, 3), -# (True, 3, 2), -# (True, 4, 2), -# (True, 5, 2), -# (True, 6, 1), -# (False, 1, 3), -# (False, 2, 2), -# (False, 3, 1), -# ], -# ) -# def test_full_contract( -# blockchain_test: BlockchainTestFiller, -# contract_length: int, -# fcb: bool, -# stride: int, -# num_expected_blocks: int, -# ): -# """ -# Test contract account full/partial migration cases. -# """ -# pre_state = {} -# if not fcb: -# pre_state[Account0] = Account(balance=1000) -# pre_state[Account1] = Account(balance=1001) -# pre_state[Account2] = Account(balance=1002) - -# pre_state[Account3] = Account( -# balance=2000, -# code=Op.STOP * contract_length, -# storage={0: 0x1, 1: 0x2}, -# ) - -# _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) + if fill_first_block: + for i in range(stride): + conversion_units += 1 + pre_state[accounts[i]] = Account(balance=100 + 1000 * i) + + for i, account_config in enumerate(account_configs, start=len(pre_state)): + storage = {} + for j in range(account_config.storage_slots_count): + conversion_units += 1 + storage[j] = j + + pre_state[accounts[i]] = Account( + balance=100 + 1000 * i, + code=Op.STOP * account_config.code_length, + storage=storage, + ) + conversion_units += 1 + math.ceil(account_config.code_length / 31) + len(storage) + + _state_conversion(blockchain_test, pre_state, stride, math.ceil(conversion_units / stride)) # @pytest.mark.skip("stride config not supported yet") From 0bbd4f6d2a4c2b7f8b2f101a6a5c962fbb680ccc Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 27 Feb 2025 16:17:52 +0000 Subject: [PATCH 06/24] eip7748: fixes and more cases Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/accounts.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index 357a5794508..807dd3361b5 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -50,21 +50,23 @@ def __init__(self, code_length: int, storage_slot_count: int): @pytest.mark.parametrize( "account_configs", [ - [AccountConfig(0, 0)], - [AccountConfig(0, 0), AccountConfig(0, 0)], + # [AccountConfig(0, 0)], + # [AccountConfig(0, 0), AccountConfig(0, 0)], [AccountConfig(0, 0), AccountConfig(0, 0), AccountConfig(0, 0)], + # [AccountConfig(15, 1)], ], ids=[ - "One EOA", - "Two EOAs", + # "One EOA", + # "Two EOAs", "Three EOAs", + # "Small contract" ], ) @pytest.mark.parametrize( "fill_first_block, stride", [ (False, 3), - (True, 3), + # (True, 3), ], ) def test_conversions( @@ -91,10 +93,10 @@ def test_conversions( pre_state[accounts[i]] = Account( balance=100 + 1000 * i, - code=Op.STOP * account_config.code_length, + code=Op.JUMPDEST * account_config.code_length, storage=storage, ) - conversion_units += 1 + math.ceil(account_config.code_length / 31) + len(storage) + conversion_units += 1 + math.ceil(account_config.code_length / 31) _state_conversion(blockchain_test, pre_state, stride, math.ceil(conversion_units / stride)) From 08bd4d18c5c10f7212f9c5d9dff25539f1b71323 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 27 Feb 2025 17:11:41 +0000 Subject: [PATCH 07/24] enable problematic test case Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/accounts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index 807dd3361b5..f28c688c183 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -52,8 +52,8 @@ def __init__(self, code_length: int, storage_slot_count: int): [ # [AccountConfig(0, 0)], # [AccountConfig(0, 0), AccountConfig(0, 0)], - [AccountConfig(0, 0), AccountConfig(0, 0), AccountConfig(0, 0)], - # [AccountConfig(15, 1)], + # [AccountConfig(0, 0), AccountConfig(0, 0), AccountConfig(0, 0)], + [AccountConfig(15, 1)], ], ids=[ # "One EOA", From 31f350db7e6155a5dd43ae8db2f197db3bae0801 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 11 Mar 2025 11:48:08 +0100 Subject: [PATCH 08/24] fixes --- tests/verkle/eip7748/accounts.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index f28c688c183..f6a5b6e0601 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -50,23 +50,18 @@ def __init__(self, code_length: int, storage_slot_count: int): @pytest.mark.parametrize( "account_configs", [ - # [AccountConfig(0, 0)], - # [AccountConfig(0, 0), AccountConfig(0, 0)], - # [AccountConfig(0, 0), AccountConfig(0, 0), AccountConfig(0, 0)], + [AccountConfig(0, 0)], + [AccountConfig(0, 0), AccountConfig(0, 0)], + [AccountConfig(0, 0), AccountConfig(0, 0), AccountConfig(0, 0)], [AccountConfig(15, 1)], ], - ids=[ - # "One EOA", - # "Two EOAs", - "Three EOAs", - # "Small contract" - ], + ids=["One EOA", "Two EOAs", "Three EOAs", "Small contract"], ) @pytest.mark.parametrize( "fill_first_block, stride", [ (False, 3), - # (True, 3), + (True, 3), ], ) def test_conversions( @@ -89,7 +84,7 @@ def test_conversions( storage = {} for j in range(account_config.storage_slots_count): conversion_units += 1 - storage[j] = j + storage[j] = j + 1 pre_state[accounts[i]] = Account( balance=100 + 1000 * i, From 4a6a8d08f6081fdd9fc32923c24ab43a457ae988 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 11 Mar 2025 15:03:42 +0100 Subject: [PATCH 09/24] more test coverage and dynamic prepared accounts --- tests/verkle/eip7748/accounts.py | 68 ++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index f6a5b6e0601..16666ed65f2 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -21,23 +21,7 @@ REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7748.md" REFERENCE_SPEC_VERSION = "TODO" -# List of addressed ordered by MPT tree key. -accounts = [ - # 03601462093b5945d1676df093446790fd31b20e7b12a2e8e5e09d068109616b - Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"), - # 1fe26fd0b8a197e7b85ed1ead2b52700041c5d465673aa744f3afc4704f83c03 - Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0d"), - # 257f371320e4696a5debc64a489e651fc4565eb07ce0e4d2ce5b6d5b1896d89a - Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0f"), - # 6a7fc6037f7a0dca7004c2cd41d87bfd929be7eb0d31903b238839e8e7aaf897 - Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0a"), - # 85174d7e61a36094fc9b58640ad245d4ab61d888699f3659137171ff2910b6cb - Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf0e"), - # b4102152d5f5995b7a017c9db0e186028190faafa4326ac1ecfb2bc817c423c9 - Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf05"), - # e72525e3842ed775ed5c52ffc1520247deae64a217fcb3fb3ddbe59ffeb5949c - Address("0xd94f5374fce5edbc8e2a8697c15331677e6ebf03"), -] +accounts = sorted([Address(i) for i in range(0, 50)], key=lambda x: x.keccak256()) class AccountConfig: @@ -51,28 +35,62 @@ def __init__(self, code_length: int, storage_slot_count: int): "account_configs", [ [AccountConfig(0, 0)], - [AccountConfig(0, 0), AccountConfig(0, 0)], - [AccountConfig(0, 0), AccountConfig(0, 0), AccountConfig(0, 0)], - [AccountConfig(15, 1)], + [AccountConfig(0, 0)] * 2, + [AccountConfig(0, 0)] * 7, + [AccountConfig(15, 2)], + [AccountConfig(31 * 2 + 1, 3)], # 3 code-chunks + 3 slots + account data = 7 + [AccountConfig(0, 0), AccountConfig(15, 2)], + [ + AccountConfig(0, 0), + AccountConfig(31 + 1, 3), + ], + [ + AccountConfig(15, 2), + AccountConfig(0, 0), + ], + [ + AccountConfig(31 + 1, 3), + AccountConfig(0, 0), + ], + [ + AccountConfig(5, 1), + AccountConfig(8, 1), + ], + [ + AccountConfig(5, 2), + AccountConfig(8, 1), + ], + ], + ids=[ + "EOA", + "EOAs under-fit", + "EOAs perfect-fit", + "Contract under-fit", + "Contract perfect-fit", + "EOA and Contract under-fit", + "EOA and Contract perfect-fit", + "Contract and EOA under-fit", + "Contract and EOA perfect-fit", + "Contract and Contract under-fit", + "Contract and Contract perfect-fit", ], - ids=["One EOA", "Two EOAs", "Three EOAs", "Small contract"], ) @pytest.mark.parametrize( - "fill_first_block, stride", + "fill_first_block", [ - (False, 3), - (True, 3), + False, + True, ], ) def test_conversions( blockchain_test: BlockchainTestFiller, account_configs: list[AccountConfig], fill_first_block: bool, - stride: int, ): """ Test conversion cases. """ + stride = 7 conversion_units = 0 pre_state = {} if fill_first_block: From 9678dbada4ce424e6acc536176cea02f3b44d6ab Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Wed, 12 Mar 2025 13:48:50 +0100 Subject: [PATCH 10/24] add partial conversion test cases Signed-off-by: Ignacio Hagopian --- .../test_balance.py | 2 +- tests/verkle/eip7748/accounts.py | 99 ++++++++++++++++--- 2 files changed, 88 insertions(+), 13 deletions(-) diff --git a/tests/verkle/eip4762_verkle_gas_witness/test_balance.py b/tests/verkle/eip4762_verkle_gas_witness/test_balance.py index 91578c07a3b..48a6fd7510e 100644 --- a/tests/verkle/eip4762_verkle_gas_witness/test_balance.py +++ b/tests/verkle/eip4762_verkle_gas_witness/test_balance.py @@ -57,7 +57,7 @@ def test_balance(blockchain_test: BlockchainTestFiller, fork: Fork, target, warm ], ) @pytest.mark.parametrize( - " gas, exp_target_basic_data", + "gas, exp_target_basic_data", [ (21_203 + 2099, False), (21_203 + 2100, True), diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index 16666ed65f2..bc5461e67c2 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -21,7 +21,8 @@ REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7748.md" REFERENCE_SPEC_VERSION = "TODO" -accounts = sorted([Address(i) for i in range(0, 50)], key=lambda x: x.keccak256()) +stride = 7 +accounts = sorted([Address(i) for i in range(0, 100)], key=lambda x: x.keccak256()) class AccountConfig: @@ -30,15 +31,16 @@ def __init__(self, code_length: int, storage_slot_count: int): self.storage_slots_count = storage_slot_count +@pytest.mark.skip("TEMPORAL") @pytest.mark.valid_from("EIP6800Transition") @pytest.mark.parametrize( "account_configs", [ [AccountConfig(0, 0)], [AccountConfig(0, 0)] * 2, - [AccountConfig(0, 0)] * 7, + [AccountConfig(0, 0)] * stride, [AccountConfig(15, 2)], - [AccountConfig(31 * 2 + 1, 3)], # 3 code-chunks + 3 slots + account data = 7 + [AccountConfig(31 * 2 + 1, 3)], # 3 code-chunks + 3 slots + account data = stride [AccountConfig(0, 0), AccountConfig(15, 2)], [ AccountConfig(0, 0), @@ -77,39 +79,112 @@ def __init__(self, code_length: int, storage_slot_count: int): ) @pytest.mark.parametrize( "fill_first_block", + [False, True], +) +@pytest.mark.parametrize( + "fill_last_block", + [False, True], +) +def test_non_partial( + blockchain_test: BlockchainTestFiller, + account_configs: list[AccountConfig], + fill_first_block: bool, + fill_last_block: bool, +): + """ + Test non-partial account conversions. + """ + _generic_conversion(blockchain_test, account_configs, fill_first_block, fill_last_block) + + +@pytest.mark.valid_from("EIP6800Transition") +@pytest.mark.parametrize( + "account_configs", [ - False, - True, + # No prefix + [AccountConfig(31 * 2 + 1, stride + 2)], + [AccountConfig(31 * 2 + 1, stride + 1)], + [AccountConfig(31 + 2 + 1, stride)], + # EOA prefix + [AccountConfig(0, 0), AccountConfig(42, -1 + stride + 2)], + [AccountConfig(0, 0), AccountConfig(42, -1 + stride + 1)], + [AccountConfig(0, 0), AccountConfig(42, -1 + stride)], + # Contract prefix + [AccountConfig(10, 1), AccountConfig(42, -3 + stride + 2)], + [AccountConfig(10, 1), AccountConfig(42, -3 + stride + 1)], + [AccountConfig(10, 1), AccountConfig(42, -3 + stride)], + ], + ids=[ + "No prefix & boundary at two storage slots before finishing storage trie", + "No prefix & boundary at one storage slot before finishing storage trie", + "No prefix & boundary matching exactly the end of the storage trie", + "EOA prefix & boundary at two storage slots before finishing storage trie", + "EOA prefix & boundary at one storage slot before finishing storage trie", + "EOA prefix & boundary matching exactly the end of the storage trie", + "Contract prefix & boundary at two storage slots before finishing storage trie", + "Contract prefix & boundary at one storage slot before finishing storage trie", + "Contract prefix & boundary matching exactly the end of the storage trie", ], ) -def test_conversions( +@pytest.mark.parametrize( + "fill_first_block", + [False, True], +) +@pytest.mark.parametrize( + "fill_last_block", + [False, True], +) +def test_partial( blockchain_test: BlockchainTestFiller, account_configs: list[AccountConfig], fill_first_block: bool, + fill_last_block: bool, ): """ - Test conversion cases. + Test partial account conversions. """ - stride = 7 + _generic_conversion(blockchain_test, account_configs, fill_first_block, fill_last_block) + + +def _generic_conversion( + blockchain_test: BlockchainTestFiller, + account_configs: list[AccountConfig], + fill_first_block: bool, + fill_last_block: bool, +): conversion_units = 0 pre_state = {} + account_idx = 0 if fill_first_block: for i in range(stride): conversion_units += 1 - pre_state[accounts[i]] = Account(balance=100 + 1000 * i) + pre_state[accounts[account_idx]] = Account(balance=100 + 1000 * i) + account_idx += 1 - for i, account_config in enumerate(account_configs, start=len(pre_state)): + for i, account_config in enumerate(account_configs): storage = {} for j in range(account_config.storage_slots_count): conversion_units += 1 storage[j] = j + 1 - pre_state[accounts[i]] = Account( + pre_state[accounts[account_idx]] = Account( balance=100 + 1000 * i, + nonce=i, code=Op.JUMPDEST * account_config.code_length, storage=storage, ) - conversion_units += 1 + math.ceil(account_config.code_length / 31) + account_idx += 1 + + conversion_units += 1 # Account basic data + num_code_chunks = math.ceil(account_config.code_length / 31) + # Code is always converted in one go, but it counts for stride quota usage + conversion_units += min(num_code_chunks, stride - conversion_units % stride) + + if fill_last_block: + for i in range((-conversion_units) % stride + stride): + conversion_units += 1 + pre_state[accounts[account_idx]] = Account(balance=100 + 1000 * i) + account_idx += 1 _state_conversion(blockchain_test, pre_state, stride, math.ceil(conversion_units / stride)) From 4f6479525514a5cbacd6ae62ad7c21f3f8166d7f Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Wed, 12 Mar 2025 16:36:34 +0100 Subject: [PATCH 11/24] code chunk stride overflow tests --- tests/verkle/eip7748/accounts.py | 33 +++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index bc5461e67c2..5ef5b909b8d 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -31,7 +31,6 @@ def __init__(self, code_length: int, storage_slot_count: int): self.storage_slots_count = storage_slot_count -@pytest.mark.skip("TEMPORAL") @pytest.mark.valid_from("EIP6800Transition") @pytest.mark.parametrize( "account_configs", @@ -146,6 +145,38 @@ def test_partial( _generic_conversion(blockchain_test, account_configs, fill_first_block, fill_last_block) +@pytest.mark.valid_from("EIP6800Transition") +@pytest.mark.parametrize( + "account_configs", + [ + [AccountConfig(31 * (stride + 10) + 1, 4)], + [AccountConfig(31 * (stride + 3), 1), AccountConfig(0, 0)], + ], + ids=[ + "Stride overflow", + "Stride overflow followed by EOA", + ], +) +@pytest.mark.parametrize( + "fill_first_block", + [False, True], +) +@pytest.mark.parametrize( + "fill_last_block", + [False, True], +) +def test_codechunks_stride_overflow( + blockchain_test: BlockchainTestFiller, + account_configs: list[AccountConfig], + fill_first_block: bool, + fill_last_block: bool, +): + """ + Test code-chunks stride overflow. + """ + _generic_conversion(blockchain_test, account_configs, fill_first_block, fill_last_block) + + def _generic_conversion( blockchain_test: BlockchainTestFiller, account_configs: list[AccountConfig], From 48056a9329f0764bcaba9ea5014f32f4a70bcd73 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 13 Mar 2025 14:52:56 -0300 Subject: [PATCH 12/24] revert hack Signed-off-by: Ignacio Hagopian --- src/ethereum_test_specs/blockchain.py | 2 +- tests/verkle/eip7748/accounts.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ethereum_test_specs/blockchain.py b/src/ethereum_test_specs/blockchain.py index 1a45c14b421..e8cdbbf275a 100644 --- a/src/ethereum_test_specs/blockchain.py +++ b/src/ethereum_test_specs/blockchain.py @@ -582,7 +582,7 @@ def generate_block_data( ) transition_tool_output.alloc = previous_alloc # TODO: hack for now, replace with actual witness output once available from t8n - if transition_tool_output.result.verkle_conversion_ended and False: + if transition_tool_output.result.verkle_conversion_ended: witness_parent_root = transition_tool_output.result.parent_state_root transition_tool_output.witness = Witness( verkle_proof=transition_tool_output.result.verkle_proof, diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index 5ef5b909b8d..2947b42bc6a 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -7,7 +7,6 @@ import pytest import math -import sys from ethereum_test_tools import ( Account, From ddc92097bfc8fe01f59ec70808007d58ffd8f572 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Fri, 14 Mar 2025 11:25:23 -0300 Subject: [PATCH 13/24] hack fix Signed-off-by: Ignacio Hagopian --- src/ethereum_test_specs/blockchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethereum_test_specs/blockchain.py b/src/ethereum_test_specs/blockchain.py index e8cdbbf275a..1a45c14b421 100644 --- a/src/ethereum_test_specs/blockchain.py +++ b/src/ethereum_test_specs/blockchain.py @@ -582,7 +582,7 @@ def generate_block_data( ) transition_tool_output.alloc = previous_alloc # TODO: hack for now, replace with actual witness output once available from t8n - if transition_tool_output.result.verkle_conversion_ended: + if transition_tool_output.result.verkle_conversion_ended and False: witness_parent_root = transition_tool_output.result.parent_state_root transition_tool_output.witness = Witness( verkle_proof=transition_tool_output.result.verkle_proof, From c4bb35be6a553b5018c4dcf3603dccd55d8f4070 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 17 Mar 2025 10:39:39 -0300 Subject: [PATCH 14/24] conversion: generalize non-partial and add empty code dimension Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/accounts.py | 71 +++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index 2947b42bc6a..716ead7b12a 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -7,6 +7,7 @@ import pytest import math +from typing import Optional from ethereum_test_tools import ( Account, @@ -97,50 +98,70 @@ def test_non_partial( @pytest.mark.valid_from("EIP6800Transition") @pytest.mark.parametrize( - "account_configs", + "storage_slots_overlow", + [ + 2, + 1, + 0, + ], + ids=[ + "Two storage slots overflowed to next block", + "One storage slot overflowed to next block", + "Storage slots fit in one block", + ], +) +@pytest.mark.parametrize( + "account_prefix", [ - # No prefix - [AccountConfig(31 * 2 + 1, stride + 2)], - [AccountConfig(31 * 2 + 1, stride + 1)], - [AccountConfig(31 + 2 + 1, stride)], - # EOA prefix - [AccountConfig(0, 0), AccountConfig(42, -1 + stride + 2)], - [AccountConfig(0, 0), AccountConfig(42, -1 + stride + 1)], - [AccountConfig(0, 0), AccountConfig(42, -1 + stride)], - # Contract prefix - [AccountConfig(10, 1), AccountConfig(42, -3 + stride + 2)], - [AccountConfig(10, 1), AccountConfig(42, -3 + stride + 1)], - [AccountConfig(10, 1), AccountConfig(42, -3 + stride)], + None, + AccountConfig(0, 0), + AccountConfig(10, 1), ], ids=[ - "No prefix & boundary at two storage slots before finishing storage trie", - "No prefix & boundary at one storage slot before finishing storage trie", - "No prefix & boundary matching exactly the end of the storage trie", - "EOA prefix & boundary at two storage slots before finishing storage trie", - "EOA prefix & boundary at one storage slot before finishing storage trie", - "EOA prefix & boundary matching exactly the end of the storage trie", - "Contract prefix & boundary at two storage slots before finishing storage trie", - "Contract prefix & boundary at one storage slot before finishing storage trie", - "Contract prefix & boundary matching exactly the end of the storage trie", + "No prefix", + "EOA prefix", + "Contract prefix", ], ) +@pytest.mark.parametrize( + "empty_code", + [True, False], +) @pytest.mark.parametrize( "fill_first_block", - [False, True], + [True, False], ) @pytest.mark.parametrize( "fill_last_block", - [False, True], + [True, False], ) def test_partial( blockchain_test: BlockchainTestFiller, - account_configs: list[AccountConfig], + storage_slots_overlow: int, + account_prefix: Optional[AccountConfig], + empty_code: bool, fill_first_block: bool, fill_last_block: bool, ): """ Test partial account conversions. """ + conversion_unit_offset = 0 + account_configs = [] + if account_prefix is not None: + conversion_unit_offset += 1 # Account basic data + conversion_unit_offset += math.ceil(account_prefix.code_length / 31) # Code-chunks + conversion_unit_offset += account_prefix.storage_slots_count + account_configs.append(account_prefix) + + code_length = 0 if empty_code else 31 + # For the `stride` quota in this block, we already used `conversion_unit_offset` units from the accounts prefixes. + # For the remaining quota (i.e., `stride-conversion_unit_offset`), we should configure the contract having + # `(stride - conversion_unit_offset)+storage_slots_overlow` so we can overflow the desired storage slots to the + # next block. + num_storage_slots = stride - conversion_unit_offset + storage_slots_overlow + account_configs.append(AccountConfig(code_length, num_storage_slots)) + _generic_conversion(blockchain_test, account_configs, fill_first_block, fill_last_block) From 240e6263b228584acb9eb7fb7610d6932fa8c365 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 17 Mar 2025 10:46:21 -0300 Subject: [PATCH 15/24] conversion: add test case for empty-code with non-empty storage non-partial conversion Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/accounts.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index 716ead7b12a..b5e7fa89753 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -61,6 +61,7 @@ def __init__(self, code_length: int, storage_slot_count: int): AccountConfig(5, 2), AccountConfig(8, 1), ], + [AccountConfig(0, stride - 1)], ], ids=[ "EOA", @@ -74,15 +75,16 @@ def __init__(self, code_length: int, storage_slot_count: int): "Contract and EOA perfect-fit", "Contract and Contract under-fit", "Contract and Contract perfect-fit", + "Empty code with storage slots perfect-fit", ], ) @pytest.mark.parametrize( "fill_first_block", - [False, True], + [True, False], ) @pytest.mark.parametrize( "fill_last_block", - [False, True], + [True, False], ) def test_non_partial( blockchain_test: BlockchainTestFiller, From d01e5ad720cf58ebb55ae86689db5fcb51566e89 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 17 Mar 2025 11:21:34 -0300 Subject: [PATCH 16/24] refactor Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/accounts.py | 90 +------------------------------- tests/verkle/eip7748/utils.py | 90 ++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 88 deletions(-) create mode 100644 tests/verkle/eip7748/utils.py diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/accounts.py index b5e7fa89753..b7d2cf6c679 100644 --- a/tests/verkle/eip7748/accounts.py +++ b/tests/verkle/eip7748/accounts.py @@ -9,27 +9,12 @@ import math from typing import Optional -from ethereum_test_tools import ( - Account, - Address, - Block, - BlockchainTestFiller, - Environment, -) -from ethereum_test_tools.vm.opcode import Opcodes as Op +from ethereum_test_tools import BlockchainTestFiller +from .utils import AccountConfig, stride, _generic_conversion REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7748.md" REFERENCE_SPEC_VERSION = "TODO" -stride = 7 -accounts = sorted([Address(i) for i in range(0, 100)], key=lambda x: x.keccak256()) - - -class AccountConfig: - def __init__(self, code_length: int, storage_slot_count: int): - self.code_length = code_length - self.storage_slots_count = storage_slot_count - @pytest.mark.valid_from("EIP6800Transition") @pytest.mark.parametrize( @@ -199,49 +184,6 @@ def test_codechunks_stride_overflow( _generic_conversion(blockchain_test, account_configs, fill_first_block, fill_last_block) -def _generic_conversion( - blockchain_test: BlockchainTestFiller, - account_configs: list[AccountConfig], - fill_first_block: bool, - fill_last_block: bool, -): - conversion_units = 0 - pre_state = {} - account_idx = 0 - if fill_first_block: - for i in range(stride): - conversion_units += 1 - pre_state[accounts[account_idx]] = Account(balance=100 + 1000 * i) - account_idx += 1 - - for i, account_config in enumerate(account_configs): - storage = {} - for j in range(account_config.storage_slots_count): - conversion_units += 1 - storage[j] = j + 1 - - pre_state[accounts[account_idx]] = Account( - balance=100 + 1000 * i, - nonce=i, - code=Op.JUMPDEST * account_config.code_length, - storage=storage, - ) - account_idx += 1 - - conversion_units += 1 # Account basic data - num_code_chunks = math.ceil(account_config.code_length / 31) - # Code is always converted in one go, but it counts for stride quota usage - conversion_units += min(num_code_chunks, stride - conversion_units % stride) - - if fill_last_block: - for i in range((-conversion_units) % stride + stride): - conversion_units += 1 - pre_state[accounts[account_idx]] = Account(balance=100 + 1000 * i) - account_idx += 1 - - _state_conversion(blockchain_test, pre_state, stride, math.ceil(conversion_units / stride)) - - # @pytest.mark.skip("stride config not supported yet") # @pytest.mark.valid_from("EIP6800Transition") # @pytest.mark.parametrize( @@ -309,31 +251,3 @@ def _generic_conversion( # ) # _state_conversion(blockchain_test, pre_state, stride, num_expected_blocks) - - -def _state_conversion( - blockchain_test: BlockchainTestFiller, - pre_state: dict[Address, Account], - stride: int, - num_blocks: int, -): - # TODO: test library should allow passing stride - env = Environment( - fee_recipient="0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - difficulty=0x20000, - gas_limit=10000000000, - ) - - blocks: list[Block] = [] - for i in range(num_blocks): - blocks.append(Block(txs=[])) - - # TODO: witness assertion - # TODO: see if possible last block switch to finished conversion - - blockchain_test( - genesis_environment=env, - pre=pre_state, - post=pre_state.copy(), - blocks=blocks, - ) diff --git a/tests/verkle/eip7748/utils.py b/tests/verkle/eip7748/utils.py new file mode 100644 index 00000000000..752fa42288b --- /dev/null +++ b/tests/verkle/eip7748/utils.py @@ -0,0 +1,90 @@ +from ethereum_test_tools.vm.opcode import Opcodes as Op +import math +from ethereum_test_tools import ( + Account, + Address, + Block, + BlockchainTestFiller, + Environment, +) + +stride = 7 + +accounts = sorted([Address(i) for i in range(0, 100)], key=lambda x: x.keccak256()) + + +class AccountConfig: + def __init__(self, code_length: int, storage_slot_count: int): + self.code_length = code_length + self.storage_slots_count = storage_slot_count + + +def _generic_conversion( + blockchain_test: BlockchainTestFiller, + account_configs: list[AccountConfig], + fill_first_block: bool, + fill_last_block: bool, +): + conversion_units = 0 + pre_state = {} + account_idx = 0 + if fill_first_block: + for i in range(stride): + conversion_units += 1 + pre_state[accounts[account_idx]] = Account(balance=100 + 1000 * i) + account_idx += 1 + + for i, account_config in enumerate(account_configs): + storage = {} + for j in range(account_config.storage_slots_count): + conversion_units += 1 + storage[j] = j + 1 + + pre_state[accounts[account_idx]] = Account( + balance=100 + 1000 * i, + nonce=i, + code=Op.JUMPDEST * account_config.code_length, + storage=storage, + ) + account_idx += 1 + + conversion_units += 1 # Account basic data + num_code_chunks = math.ceil(account_config.code_length / 31) + # Code is always converted in one go, but it counts for stride quota usage + conversion_units += min(num_code_chunks, stride - conversion_units % stride) + + if fill_last_block: + for i in range((-conversion_units) % stride + stride): + conversion_units += 1 + pre_state[accounts[account_idx]] = Account(balance=100 + 1000 * i) + account_idx += 1 + + _state_conversion(blockchain_test, pre_state, stride, math.ceil(conversion_units / stride)) + + +def _state_conversion( + blockchain_test: BlockchainTestFiller, + pre_state: dict[Address, Account], + stride: int, + num_blocks: int, +): + # TODO: test library should allow passing stride + env = Environment( + fee_recipient="0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + difficulty=0x20000, + gas_limit=10000000000, + ) + + blocks: list[Block] = [] + for i in range(num_blocks): + blocks.append(Block(txs=[])) + + # TODO: witness assertion + # TODO: see if possible last block switch to finished conversion + + blockchain_test( + genesis_environment=env, + pre=pre_state, + post=pre_state.copy(), + blocks=blocks, + ) From 69315cdd7f0d04f8b543ed8d5fc1d606ecfe7d6c Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 18 Mar 2025 09:28:18 -0300 Subject: [PATCH 17/24] stale keys tests Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/stale_keys.py | 133 +++++++++++++++++++++++++++++ tests/verkle/eip7748/utils.py | 19 +++++ 2 files changed, 152 insertions(+) create mode 100644 tests/verkle/eip7748/stale_keys.py diff --git a/tests/verkle/eip7748/stale_keys.py b/tests/verkle/eip7748/stale_keys.py new file mode 100644 index 00000000000..8c8051febba --- /dev/null +++ b/tests/verkle/eip7748/stale_keys.py @@ -0,0 +1,133 @@ +import pytest + +from ethereum_test_tools import BlockchainTestFiller, Transaction, Account, TestAddress +from ethereum_test_tools.vm.opcode import Opcodes as Op +from .utils import stride, _state_conversion, accounts, ConversionTx + +REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7748.md" +REFERENCE_SPEC_VERSION = "TODO" + + +@pytest.mark.valid_from("EIP6800Transition") +@pytest.mark.parametrize( + "storage_slot_write", + [ + 0, + 1, + 300, + 301, + ], + ids=[ + "Stale storage slot in the header", + "New storage slot in the account header", + "Stale storage slot outside the header", + "New storage slot outside the header", + ], +) +@pytest.mark.parametrize( + "stale_basic_data", + [True, False], +) +def test_stale_contract_writes( + blockchain_test: BlockchainTestFiller, + storage_slot_write: int, + stale_basic_data: bool, +): + """ + Test converting a contract where a previous transaction writes to: + - Existing storage slots (i.e., storage slots that must not be converted (stale)) + - New storage slots (i.e., storage slots that must not be converted (not overriden with zeros)) + - Basic data (i.e., balance/nonce which must not be converted (stale)) + """ + pre_state = {} + pre_state[TestAddress] = Account(balance=1000000000000000000000) + + # TODO(hack): today the testing-framework does not support us signaling that we want to + # put the `ConversionTx(tx, **0**)` at the first block after genesis. To simulate that, we have + # to do this here so we "shift" the target account to the second block in the fork. + # If this is ever supported, remove this. + for i in range(stride): + pre_state[accounts[i]] = Account(balance=100 + 1000 * i) + + target_account = accounts[stride] + pre_state[target_account] = Account( + balance=1_000, + nonce=0, + code=Op.SSTORE(storage_slot_write, 9999), + storage={ + 0: 100, + 300: 200, + }, + ) + + tx = Transaction( + ty=0x0, + chain_id=0x01, + to=target_account, + value=100 if stale_basic_data else 0, + gas_limit=100_000, + gas_price=10, + ) + + _state_conversion(blockchain_test, pre_state, stride, 1, [ConversionTx(tx, 0)]) + + +# @pytest.mark.valid_from("EIP6800Transition") +# @pytest.mark.parametrize( +# "num_storage_slots, num_stale_storage_slots", +# [ +# (0, 0), +# (4, 0), +# (4, 2), +# (stride, stride), +# ], +# ids=[ +# "EOA", +# "Contract without stale storage", +# "Contract with some stale storage", +# "Contract with all stale storage", +# ], +# ) +# @pytest.mark.parametrize( +# "stale_basic_data", +# [True, False], +# ) +# @pytest.mark.parametrize( +# "fill_first_block", +# [True, False], +# ) +# @pytest.mark.parametrize( +# "fill_last_block", +# [True, False], +# ) +# def test_stale_keys( +# blockchain_test: BlockchainTestFiller, +# account_configs: list[AccountConfig], +# fill_first_block: bool, +# fill_last_block: bool, +# ): +# """ +# Test account conversion with full/partial stale storage slots and basic data. +# """ +# _generic_conversion(blockchain_test, account_configs, fill_first_block, fill_last_block) + + +# @pytest.mark.valid_from("EIP6800Transition") +# @pytest.mark.parametrize( +# "fill_first_block", +# [True, False], +# ) +# @pytest.mark.parametrize( +# "fill_last_block", +# [True, False], +# ) +# def test_stride_stale_eoas( +# blockchain_test: BlockchainTestFiller, +# account_configs: list[AccountConfig], +# fill_first_block: bool, +# fill_last_block: bool, +# ): +# """ +# Test converting a stride number of stale EOAs. +# """ +# _generic_conversion(blockchain_test, account_configs, fill_first_block, fill_last_block) diff --git a/tests/verkle/eip7748/utils.py b/tests/verkle/eip7748/utils.py index 752fa42288b..ec4a9f519ec 100644 --- a/tests/verkle/eip7748/utils.py +++ b/tests/verkle/eip7748/utils.py @@ -5,6 +5,7 @@ Address, Block, BlockchainTestFiller, + Transaction, Environment, ) @@ -19,6 +20,12 @@ def __init__(self, code_length: int, storage_slot_count: int): self.storage_slots_count = storage_slot_count +class StaleBasicDataTx: + def __init__(self, account_config_idx: int, block_num: int): + self.account_config_idx = account_config_idx + self.block_num = block_num + + def _generic_conversion( blockchain_test: BlockchainTestFiller, account_configs: list[AccountConfig], @@ -34,6 +41,7 @@ def _generic_conversion( pre_state[accounts[account_idx]] = Account(balance=100 + 1000 * i) account_idx += 1 + target_accounts: list[Address] = {} for i, account_config in enumerate(account_configs): storage = {} for j in range(account_config.storage_slots_count): @@ -46,6 +54,7 @@ def _generic_conversion( code=Op.JUMPDEST * account_config.code_length, storage=storage, ) + target_accounts.append(accounts[account_idx]) account_idx += 1 conversion_units += 1 # Account basic data @@ -62,11 +71,18 @@ def _generic_conversion( _state_conversion(blockchain_test, pre_state, stride, math.ceil(conversion_units / stride)) +class ConversionTx: + def __init__(self, tx: Transaction, block_num: int): + self.tx = tx + self.block_num = block_num + + def _state_conversion( blockchain_test: BlockchainTestFiller, pre_state: dict[Address, Account], stride: int, num_blocks: int, + txs: list[ConversionTx] = [], ): # TODO: test library should allow passing stride env = Environment( @@ -79,6 +95,9 @@ def _state_conversion( for i in range(num_blocks): blocks.append(Block(txs=[])) + for tx in txs: + blocks[tx.block_num].txs.append(tx.tx) + # TODO: witness assertion # TODO: see if possible last block switch to finished conversion From 0134e5cebaaf93e77beda8c5e62617c5254f4bba Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 18 Mar 2025 10:01:04 -0300 Subject: [PATCH 18/24] add modified contract test & refactor Signed-off-by: Ignacio Hagopian --- .../{stale_keys.py => modified_accounts.py} | 67 ++----------------- .../{accounts.py => unmodified_accounts.py} | 0 2 files changed, 4 insertions(+), 63 deletions(-) rename tests/verkle/eip7748/{stale_keys.py => modified_accounts.py} (56%) rename tests/verkle/eip7748/{accounts.py => unmodified_accounts.py} (100%) diff --git a/tests/verkle/eip7748/stale_keys.py b/tests/verkle/eip7748/modified_accounts.py similarity index 56% rename from tests/verkle/eip7748/stale_keys.py rename to tests/verkle/eip7748/modified_accounts.py index 8c8051febba..f8550468378 100644 --- a/tests/verkle/eip7748/stale_keys.py +++ b/tests/verkle/eip7748/modified_accounts.py @@ -12,12 +12,14 @@ @pytest.mark.parametrize( "storage_slot_write", [ + None, 0, 1, 300, 301, ], ids=[ + "No storage slot modified", "Stale storage slot in the header", "New storage slot in the account header", "Stale storage slot outside the header", @@ -28,7 +30,7 @@ "stale_basic_data", [True, False], ) -def test_stale_contract_writes( +def test_modified_contract( blockchain_test: BlockchainTestFiller, storage_slot_write: int, stale_basic_data: bool, @@ -53,7 +55,7 @@ def test_stale_contract_writes( pre_state[target_account] = Account( balance=1_000, nonce=0, - code=Op.SSTORE(storage_slot_write, 9999), + code=Op.SSTORE(storage_slot_write, 9999) if storage_slot_write is not None else [], storage={ 0: 100, 300: 200, @@ -70,64 +72,3 @@ def test_stale_contract_writes( ) _state_conversion(blockchain_test, pre_state, stride, 1, [ConversionTx(tx, 0)]) - - -# @pytest.mark.valid_from("EIP6800Transition") -# @pytest.mark.parametrize( -# "num_storage_slots, num_stale_storage_slots", -# [ -# (0, 0), -# (4, 0), -# (4, 2), -# (stride, stride), -# ], -# ids=[ -# "EOA", -# "Contract without stale storage", -# "Contract with some stale storage", -# "Contract with all stale storage", -# ], -# ) -# @pytest.mark.parametrize( -# "stale_basic_data", -# [True, False], -# ) -# @pytest.mark.parametrize( -# "fill_first_block", -# [True, False], -# ) -# @pytest.mark.parametrize( -# "fill_last_block", -# [True, False], -# ) -# def test_stale_keys( -# blockchain_test: BlockchainTestFiller, -# account_configs: list[AccountConfig], -# fill_first_block: bool, -# fill_last_block: bool, -# ): -# """ -# Test account conversion with full/partial stale storage slots and basic data. -# """ -# _generic_conversion(blockchain_test, account_configs, fill_first_block, fill_last_block) - - -# @pytest.mark.valid_from("EIP6800Transition") -# @pytest.mark.parametrize( -# "fill_first_block", -# [True, False], -# ) -# @pytest.mark.parametrize( -# "fill_last_block", -# [True, False], -# ) -# def test_stride_stale_eoas( -# blockchain_test: BlockchainTestFiller, -# account_configs: list[AccountConfig], -# fill_first_block: bool, -# fill_last_block: bool, -# ): -# """ -# Test converting a stride number of stale EOAs. -# """ -# _generic_conversion(blockchain_test, account_configs, fill_first_block, fill_last_block) diff --git a/tests/verkle/eip7748/accounts.py b/tests/verkle/eip7748/unmodified_accounts.py similarity index 100% rename from tests/verkle/eip7748/accounts.py rename to tests/verkle/eip7748/unmodified_accounts.py From 06f3ba390a5bd0c0d6561d469baf99b1bf83a2f1 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 18 Mar 2025 10:15:24 -0300 Subject: [PATCH 19/24] add modified EOA test and refactor Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/modified_accounts.py | 54 +++++++++++++++++------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/tests/verkle/eip7748/modified_accounts.py b/tests/verkle/eip7748/modified_accounts.py index f8550468378..73b4ed0b106 100644 --- a/tests/verkle/eip7748/modified_accounts.py +++ b/tests/verkle/eip7748/modified_accounts.py @@ -1,5 +1,6 @@ import pytest +from typing import Optional from ethereum_test_tools import BlockchainTestFiller, Transaction, Account, TestAddress from ethereum_test_tools.vm.opcode import Opcodes as Op from .utils import stride, _state_conversion, accounts, ConversionTx @@ -27,20 +28,43 @@ ], ) @pytest.mark.parametrize( - "stale_basic_data", + "tx_send_value", [True, False], ) def test_modified_contract( blockchain_test: BlockchainTestFiller, storage_slot_write: int, - stale_basic_data: bool, + tx_send_value: bool, ): """ - Test converting a contract where a previous transaction writes to: + Test converting a modified contract where a previous transaction writes to: - Existing storage slots (i.e., storage slots that must not be converted (stale)) - New storage slots (i.e., storage slots that must not be converted (not overriden with zeros)) - Basic data (i.e., balance/nonce which must not be converted (stale)) """ + _convert_modified_account(blockchain_test, tx_send_value, ContractSetup(storage_slot_write)) + + +@pytest.mark.valid_from("EIP6800Transition") +def test_modified_eoa( + blockchain_test: BlockchainTestFiller, +): + """ + Test converting a modified EOA. + """ + _convert_modified_account(blockchain_test, True, None) + + +class ContractSetup: + def __init__(self, storage_slot_write: Optional[int]): + self.storage_slot_write = storage_slot_write + + +def _convert_modified_account( + blockchain_test: BlockchainTestFiller, + tx_send_value: bool, + contract_setup: Optional[ContractSetup], +): pre_state = {} pre_state[TestAddress] = Account(balance=1000000000000000000000) @@ -52,21 +76,25 @@ def test_modified_contract( pre_state[accounts[i]] = Account(balance=100 + 1000 * i) target_account = accounts[stride] - pre_state[target_account] = Account( - balance=1_000, - nonce=0, - code=Op.SSTORE(storage_slot_write, 9999) if storage_slot_write is not None else [], - storage={ - 0: 100, - 300: 200, - }, - ) + if contract_setup is not None: + pre_state[target_account] = Account( + balance=1_000, + nonce=0, + code=( + Op.SSTORE(contract_setup.storage_slot_write, 9999) + if contract_setup.storage_slot_write is not None + else [] + ), + storage={0: 100, 300: 200}, + ) + else: + pre_state[target_account] = Account(balance=1_000, nonce=0) tx = Transaction( ty=0x0, chain_id=0x01, to=target_account, - value=100 if stale_basic_data else 0, + value=100 if tx_send_value else 0, gas_limit=100_000, gas_price=10, ) From c5ccd54086b60f59a974fe6d1cb1a3983621f73b Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 18 Mar 2025 11:12:09 -0300 Subject: [PATCH 20/24] add stride stale contract storage slots Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/modified_accounts.py | 78 +++++++++++++++++++++-- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/tests/verkle/eip7748/modified_accounts.py b/tests/verkle/eip7748/modified_accounts.py index 73b4ed0b106..174a98df91c 100644 --- a/tests/verkle/eip7748/modified_accounts.py +++ b/tests/verkle/eip7748/modified_accounts.py @@ -32,9 +32,7 @@ [True, False], ) def test_modified_contract( - blockchain_test: BlockchainTestFiller, - storage_slot_write: int, - tx_send_value: bool, + blockchain_test: BlockchainTestFiller, storage_slot_write: int, tx_send_value: bool ): """ Test converting a modified contract where a previous transaction writes to: @@ -99,4 +97,76 @@ def _convert_modified_account( gas_price=10, ) - _state_conversion(blockchain_test, pre_state, stride, 1, [ConversionTx(tx, 0)]) + _state_conversion(blockchain_test, pre_state, stride, 2, [ConversionTx(tx, 0)]) + + +@pytest.mark.valid_from("EIP6800Transition") +def test_modified_eoa_conversion_units(blockchain_test: BlockchainTestFiller): + """ + Test stale EOA are properly counted as used conversion units. + """ + pre_state = {} + pre_state[TestAddress] = Account(balance=1000000000000000000000) + + # TODO(hack): today the testing-framework does not support us signaling that we want to + # put the `ConversionTx(tx, **0**)` at the first block after genesis. To simulate that, we have + # to do this here so we "shift" the target account to the second block in the fork. + # If this is ever supported, remove this. + for i in range(stride): + pre_state[accounts[i]] = Account(balance=100 + 1000 * i) + + txs = [] + for i in range(stride + 3): + pre_state[accounts[stride + i]] = Account(balance=1_000, nonce=0) + if i < stride: + tx = Transaction( + ty=0x0, + chain_id=0x01, + nonce=i, + to=accounts[stride + i], + value=100, + gas_limit=100_000, + gas_price=10, + ) + txs.append(ConversionTx(tx, 0)) + + _state_conversion(blockchain_test, pre_state, stride, 3, txs) + + +@pytest.mark.valid_from("EIP6800Transition") +def test_modified_contract_conversion_units(blockchain_test: BlockchainTestFiller): + """ + Test stale contract storage slots are properly counted as used conversion units. + """ + pre_state = {} + pre_state[TestAddress] = Account(balance=1000000000000000000000) + + # TODO(hack): today the testing-framework does not support us signaling that we want to + # put the `ConversionTx(tx, **0**)` at the first block after genesis. To simulate that, we have + # to do this here so we "shift" the target account to the second block in the fork. + # If this is ever supported, remove this. + for i in range(stride): + pre_state[accounts[i]] = Account(balance=100 + 1000 * i) + + target_account = accounts[stride] + pre_state[target_account] = Account( + balance=1_000, + nonce=0, + code=sum([Op.SSTORE(ss, 10_000 + i) for i, ss in enumerate(range(stride))]), + storage={ss: 100 + i for i, ss in enumerate(range(stride))}, + ) + for i in range(3): + pre_state[accounts[stride + 1 + i]] = Account(balance=1_000 + i, nonce=0) + + # Send tx that writes all existing storage slots. + tx = Transaction( + ty=0x0, + chain_id=0x01, + nonce=0, + to=target_account, + value=100, + gas_limit=100_000, + gas_price=10, + ) + + _state_conversion(blockchain_test, pre_state, stride, 3, [ConversionTx(tx, 0)]) From 8cc7f9ee77f43c60682691d814a5ff96b4f7f6b3 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 18 Mar 2025 11:33:47 -0300 Subject: [PATCH 21/24] fix Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/verkle/eip7748/utils.py b/tests/verkle/eip7748/utils.py index ec4a9f519ec..8e6d81c86e9 100644 --- a/tests/verkle/eip7748/utils.py +++ b/tests/verkle/eip7748/utils.py @@ -41,7 +41,7 @@ def _generic_conversion( pre_state[accounts[account_idx]] = Account(balance=100 + 1000 * i) account_idx += 1 - target_accounts: list[Address] = {} + target_accounts: list[Address] = [] for i, account_config in enumerate(account_configs): storage = {} for j in range(account_config.storage_slots_count): From 55af9890c85282fcdc2956636b16df33a59133fb Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 18 Mar 2025 12:55:12 -0300 Subject: [PATCH 22/24] cleanup Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/unmodified_accounts.py | 25 +++++---------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/tests/verkle/eip7748/unmodified_accounts.py b/tests/verkle/eip7748/unmodified_accounts.py index b7d2cf6c679..9ca89e8673e 100644 --- a/tests/verkle/eip7748/unmodified_accounts.py +++ b/tests/verkle/eip7748/unmodified_accounts.py @@ -26,26 +26,11 @@ [AccountConfig(15, 2)], [AccountConfig(31 * 2 + 1, 3)], # 3 code-chunks + 3 slots + account data = stride [AccountConfig(0, 0), AccountConfig(15, 2)], - [ - AccountConfig(0, 0), - AccountConfig(31 + 1, 3), - ], - [ - AccountConfig(15, 2), - AccountConfig(0, 0), - ], - [ - AccountConfig(31 + 1, 3), - AccountConfig(0, 0), - ], - [ - AccountConfig(5, 1), - AccountConfig(8, 1), - ], - [ - AccountConfig(5, 2), - AccountConfig(8, 1), - ], + [AccountConfig(0, 0), AccountConfig(31 + 1, 3)], + [AccountConfig(15, 2), AccountConfig(0, 0)], + [AccountConfig(31 + 1, 3), AccountConfig(0, 0)], + [AccountConfig(5, 1), AccountConfig(8, 1)], + [AccountConfig(5, 2), AccountConfig(8, 1)], [AccountConfig(0, stride - 1)], ], ids=[ From fd7c0ae75f8d9f303f0b2fe039914ec77c99240c Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 31 Mar 2025 17:41:30 -0300 Subject: [PATCH 23/24] eip-7748: add tests for stale data happening in the same block as the one converting accounts Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/modified_accounts.py | 63 +++++++++++++++++------ 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/tests/verkle/eip7748/modified_accounts.py b/tests/verkle/eip7748/modified_accounts.py index 174a98df91c..2e1f94102ad 100644 --- a/tests/verkle/eip7748/modified_accounts.py +++ b/tests/verkle/eip7748/modified_accounts.py @@ -31,26 +31,43 @@ "tx_send_value", [True, False], ) +@pytest.mark.parametrize( + "modification_overlap_conversion", + [True, False], +) def test_modified_contract( - blockchain_test: BlockchainTestFiller, storage_slot_write: int, tx_send_value: bool + blockchain_test: BlockchainTestFiller, + storage_slot_write: int, + tx_send_value: bool, + modification_overlap_conversion: bool, ): """ Test converting a modified contract where a previous transaction writes to: - Existing storage slots (i.e., storage slots that must not be converted (stale)) - New storage slots (i.e., storage slots that must not be converted (not overriden with zeros)) - Basic data (i.e., balance/nonce which must not be converted (stale)) + The conversion transaction can be in the same block or previous block as the conversion. """ - _convert_modified_account(blockchain_test, tx_send_value, ContractSetup(storage_slot_write)) + _convert_modified_account( + blockchain_test, + tx_send_value, + ContractSetup(storage_slot_write), + modification_overlap_conversion, + ) @pytest.mark.valid_from("EIP6800Transition") +@pytest.mark.parametrize( + "modification_overlap_conversion", + [True, False], +) def test_modified_eoa( - blockchain_test: BlockchainTestFiller, + blockchain_test: BlockchainTestFiller, modification_overlap_conversion: bool ): """ - Test converting a modified EOA. + Test converting a modified EOA in the same block or previous block as the conversion. """ - _convert_modified_account(blockchain_test, True, None) + _convert_modified_account(blockchain_test, True, None, modification_overlap_conversion) class ContractSetup: @@ -62,18 +79,24 @@ def _convert_modified_account( blockchain_test: BlockchainTestFiller, tx_send_value: bool, contract_setup: Optional[ContractSetup], + modification_overlap_conversion: bool, ): pre_state = {} pre_state[TestAddress] = Account(balance=1000000000000000000000) - # TODO(hack): today the testing-framework does not support us signaling that we want to - # put the `ConversionTx(tx, **0**)` at the first block after genesis. To simulate that, we have - # to do this here so we "shift" the target account to the second block in the fork. - # If this is ever supported, remove this. - for i in range(stride): - pre_state[accounts[i]] = Account(balance=100 + 1000 * i) - - target_account = accounts[stride] + expected_conversion_blocks = 1 + accounts_idx = 0 + if not modification_overlap_conversion: + expected_conversion_blocks = 2 + # TODO(hack): today the testing-framework does not support us signaling that we want to + # put the `ConversionTx(tx, **0**)` at the first block after genesis. To simulate that, we have + # to do this here so we "shift" the target account to the second block in the fork. + # If this is ever supported, remove this. + for i in range(stride): + pre_state[accounts[accounts_idx]] = Account(balance=100 + 1000 * i) + accounts_idx += 1 + + target_account = accounts[accounts_idx] if contract_setup is not None: pre_state[target_account] = Account( balance=1_000, @@ -86,18 +109,24 @@ def _convert_modified_account( storage={0: 100, 300: 200}, ) else: - pre_state[target_account] = Account(balance=1_000, nonce=0) + pre_state[target_account] = Account(balance=10_000, nonce=0) tx = Transaction( ty=0x0, chain_id=0x01, to=target_account, - value=100 if tx_send_value else 0, + value=500 if tx_send_value else 0, gas_limit=100_000, gas_price=10, ) - _state_conversion(blockchain_test, pre_state, stride, 2, [ConversionTx(tx, 0)]) + _state_conversion( + blockchain_test, + pre_state, + stride, + expected_conversion_blocks, + [ConversionTx(tx, 0)], + ) @pytest.mark.valid_from("EIP6800Transition") @@ -116,6 +145,8 @@ def test_modified_eoa_conversion_units(blockchain_test: BlockchainTestFiller): pre_state[accounts[i]] = Account(balance=100 + 1000 * i) txs = [] + # Add stride+3 extra EOAs, and invalidate the first stride ones. This is to check that + # the conversion units are properly counted, and the last 3 aren't converted in that block. for i in range(stride + 3): pre_state[accounts[stride + i]] = Account(balance=1_000, nonce=0) if i < stride: From 0354ccd35dbf2952d391a43c894dd24f98a9c45a Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 1 Apr 2025 11:52:58 -0300 Subject: [PATCH 24/24] eip7748: cover tx revertions in tx generating stale data accounts Signed-off-by: Ignacio Hagopian --- tests/verkle/eip7748/modified_accounts.py | 62 ++++++++++++++++++----- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/tests/verkle/eip7748/modified_accounts.py b/tests/verkle/eip7748/modified_accounts.py index 2e1f94102ad..eafb82530b9 100644 --- a/tests/verkle/eip7748/modified_accounts.py +++ b/tests/verkle/eip7748/modified_accounts.py @@ -9,6 +9,19 @@ REFERENCE_SPEC_VERSION = "TODO" +class StaleAccountTx: + """ + Class to represent a transaction that modifies an account to be converted, making it completely/partially stale. + It can be configured to: + - be in the same block as the conversion transaction or in a previous block + - revert or not revert + """ + + def __init__(self, same_block_as_conversion: bool, revert: bool): + self.same_block_as_conversion = same_block_as_conversion + self.revert = revert + + @pytest.mark.valid_from("EIP6800Transition") @pytest.mark.parametrize( "storage_slot_write", @@ -32,14 +45,23 @@ [True, False], ) @pytest.mark.parametrize( - "modification_overlap_conversion", - [True, False], + "tx_stale_account_config", + [ + StaleAccountTx(False, False), + StaleAccountTx(True, False), + StaleAccountTx(True, True), + ], + ids=[ + "Tx generating stale account in previous block", + "Tx generating stale account in the same block", + "Reverted tx generating stale account in the same block", + ], ) def test_modified_contract( blockchain_test: BlockchainTestFiller, storage_slot_write: int, tx_send_value: bool, - modification_overlap_conversion: bool, + tx_stale_account_config: StaleAccountTx, ): """ Test converting a modified contract where a previous transaction writes to: @@ -52,22 +74,29 @@ def test_modified_contract( blockchain_test, tx_send_value, ContractSetup(storage_slot_write), - modification_overlap_conversion, + tx_stale_account_config, ) @pytest.mark.valid_from("EIP6800Transition") @pytest.mark.parametrize( - "modification_overlap_conversion", - [True, False], + "tx_stale_account_config", + [ + StaleAccountTx(False, False), + StaleAccountTx(True, False), + ], + ids=[ + "Tx generating stale account in previous block", + "Tx generating stale account in the same block", + ], ) def test_modified_eoa( - blockchain_test: BlockchainTestFiller, modification_overlap_conversion: bool + blockchain_test: BlockchainTestFiller, tx_stale_account_config: StaleAccountTx ): """ Test converting a modified EOA in the same block or previous block as the conversion. """ - _convert_modified_account(blockchain_test, True, None, modification_overlap_conversion) + _convert_modified_account(blockchain_test, True, None, tx_stale_account_config) class ContractSetup: @@ -79,14 +108,14 @@ def _convert_modified_account( blockchain_test: BlockchainTestFiller, tx_send_value: bool, contract_setup: Optional[ContractSetup], - modification_overlap_conversion: bool, + tx_stale_account_config: StaleAccountTx, ): pre_state = {} pre_state[TestAddress] = Account(balance=1000000000000000000000) expected_conversion_blocks = 1 accounts_idx = 0 - if not modification_overlap_conversion: + if not tx_stale_account_config.same_block_as_conversion: expected_conversion_blocks = 2 # TODO(hack): today the testing-framework does not support us signaling that we want to # put the `ConversionTx(tx, **0**)` at the first block after genesis. To simulate that, we have @@ -102,13 +131,20 @@ def _convert_modified_account( balance=1_000, nonce=0, code=( - Op.SSTORE(contract_setup.storage_slot_write, 9999) - if contract_setup.storage_slot_write is not None - else [] + ( + Op.SSTORE(contract_setup.storage_slot_write, 9999) + if contract_setup.storage_slot_write is not None + else Op.RETURN + ) + + Op.REVERT + if tx_stale_account_config.revert + else Op.RETURN ), storage={0: 100, 300: 200}, ) else: + if tx_stale_account_config.revert: + raise Exception("Invalid test case -- EOA transfers can't revert") pre_state[target_account] = Account(balance=10_000, nonce=0) tx = Transaction(