Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

verkle: more test fixes #51

Merged
merged 13 commits into from
Sep 10, 2024
3 changes: 2 additions & 1 deletion src/ethereum_test_types/verkle/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from typing import List

from ethereum_test_base_types.base_types import Hash
from ethereum_test_types.verkle.types import Hash


def chunkify_code(code: bytes) -> List[Hash]:
Expand All @@ -13,6 +13,7 @@ def chunkify_code(code: bytes) -> List[Hash]:

Used to generate code chunks for Witness state diff verification.
"""
code = bytes(code)
if len(code) % 31 != 0:
code += b"\x00" * (31 - (len(code) % 31))
bytes_to_exec_data = [0] * (len(code) + 32)
Expand Down
99 changes: 58 additions & 41 deletions tests/verkle/eip4762_verkle_gas_witness/test_contract_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,29 @@ def code_with_jumps(size, jumps: list[Jump | Jumpi] = []):
"""
Returns the requested code with defined size, jumps, and PUSHNs.
"""
code = Op.PUSH0 * size
code = Op.JUMPDEST * size
result_code = bytes()
last_offset = 0
for j in jumps:
result_code += bytes(code)[last_offset : j.offset]
if isinstance(j, Jump):
jump_code = bytes(Op.JUMP(j.pc))
elif isinstance(j, Jumpi):
if isinstance(j, Jumpi):
jump_code = bytes(Op.JUMPI(j.pc, 1 if j.condition else 0))
elif isinstance(j, Jump):
jump_code = bytes(Op.JUMP(j.pc))
result_code += jump_code
last_offset = j.offset + len(jump_code)
result_code += bytes(Op.JUMPDEST)

result_code += bytes(code)[last_offset:]

assert len(result_code) == size

return result_code


# TODO(verkle): update to Osaka when t8n supports the fork.
@pytest.mark.valid_from("Verkle")
@pytest.mark.parametrize(
"bytecode, gas_limit, witness_code_chunk_numbers",
"bytecode, gas_limit, witness_code_chunk_ranges",
[
( # only_code_in_account_header
code_with_jumps(10),
Expand All @@ -82,75 +84,89 @@ def code_with_jumps(size, jumps: list[Jump | Jumpi] = []):
( # chunks_both_in_and_out_account_header
code_with_jumps(128 * 31 + 100),
1_000_000,
[[0, 132]],
[[0, 131]],
),
( # touches_only_first_byte_code_chunk
code_with_jumps(128 * 31 + 1),
1_000_000,
[[0, 129]],
[[0, 128]],
),
( # touches_only_last_byte_code_chunk
code_with_jumps(128 * 31 + 100, [Jump(10, 128 * 31 + 100)]),
code_with_jumps(128 * 31 + 100, [Jump(10, 128 * 31 + 9)]),
1_000_000,
[[0, 0], [132, 132]],
[[0, 0], [131, 131]],
),
( # pushn_with_data_in_chunk_that_cant_be_paid
Op.PUSH0 * 30 + Op.PUSH1(42),
42,
21000,
[[0, 0]],
),
( # jump_to_jumpdest_in_pushn_data
Op.PUSH0 * 10 + Op.JUMP(10 + 2 + 1 + 1000) + Op.PUSH0 * 1000 + Op.PUSH1(0x5B),
Op.PUSH0 * 10
+ Op.JUMP(10 + 3 + 1 + 1000 + 1) # 3+1=PUSH2+JUMP
+ Op.PUSH0 * 1000
+ Op.PUSH1(0x5B)
+ Op.PUSH0 * 100, # Add more code, but can't be executed due to the invalid jump
1_000_000,
[[0, 0], [33, 33]],
[[0, 0], [32, 32]],
),
( # jumpi_to_jumpdest_in_pushn_data
Op.PUSH0 * 10 + Op.JUMPI(10 + 3 + 1 + 1000, 1) + Op.PUSH0 * 1000 + Op.PUSH1(0x5B),
bytes(
Op.PUSH0 * 10
+ Op.JUMPI(10 + 5 + 1 + 1000 + 1, 1) # 5+1=PUSH1+PUSH2+JUMPI
+ Op.PUSH0 * 1000
+ Op.PUSH1(0x5B)
+ Op.PUSH0 * 100
),
1_000_000,
[[0, 0], [33, 33]],
[[0, 0], [32, 32]],
),
( # jump_to_non_jumpdest_destiny
Op.PUSH0 * 10 + Op.JUMP(10 + 2 + 1 + 1000) + Op.PUSH0 * 1000 + Op.ORIGIN,
bytes(
Op.PUSH0 * 10 + Op.JUMP(10 + 3 + 1 + 1000) + Op.PUSH0 * 1000 + Op.ORIGIN
), # 3+1=PUSH2+JUMP
1_000_000,
[[0, 0], [33, 33]],
[[0, 0], [32, 32]],
),
( # jumpi_to_non_jumpdest_destiny
Op.PUSH0 * 10 + Op.JUMPI(10 + 3 + 1 + 1000, 1) + Op.PUSH0 * 1000 + Op.ORIGIN,
bytes(
Op.PUSH0 * 10 + Op.JUMPI(10 + 5 + 1 + 1000, 1) + Op.PUSH0 * 1000 + Op.ORIGIN
), # 5+1=PUSH1+PUSH2+JUMPI
1_000_000,
[[0, 0], [33, 33]],
[[0, 0], [32, 32]],
),
( # linear_execution_stopping_at_first_byte_of_next_chunk
code_with_jumps(128 * 31 + 1),
1_000_000,
[[0, 129]],
[[0, 128]],
),
( # false_jumpi
code_with_jumps(150 * 31 + 10, [Jumpi(50, 1000, False)]),
1_000_000,
[[0, 151]],
[[0, 150]],
),
( # insufficient_gas_for_jump_instruction
code_with_jumps(150 * 31, [Jump(50, 1000)]),
142,
[[0, 1]],
code_with_jumps(150 * 31, [Jump(10, 1000)]),
21000 + 200 + 10 + 3,
[[0, 0]],
),
( # insufficient_gas_for_jumpi_instruction
code_with_jumps(150 * 31, [Jumpi(50, 1000, True)]),
142,
[[0, 1]],
code_with_jumps(150 * 31, [Jumpi(10, 1000, True)]),
21000 + 200 + 10 + 3 + 3,
[[0, 0]],
),
( # sufficient_gas_for_jump_instruction_but_not_for_code_chunk
code_with_jumps(150 * 31, [Jump(50, 1000)]),
42,
[[0, 0], [33, 33]],
code_with_jumps(150 * 31, [Jump(10, 1000)]),
21000 + 200 + 10 + 3 + 8,
[[0, 0]],
),
( # sufficient_gas_for_jumpi_instruction_but_not_for_code_chunk
code_with_jumps(150 * 31, [Jumpi(50, 1000, True)]),
42,
[[0, 0], [33, 33]],
code_with_jumps(150 * 31, [Jumpi(10, 1000, True)]),
21000 + 200 + 10 + 3 + 3 + 10,
[[0, 0]],
),
( # jump_outside_code_size
code_with_jumps(150 * 31, [Jump(50, 150 * 31 + 42)]),
code_with_jumps(150 * 31, [Jump(10, 150 * 31 + 42)]),
1_000_000,
[[0, 0]],
),
Expand Down Expand Up @@ -200,9 +216,9 @@ def code_with_jumps(size, jumps: list[Jump | Jumpi] = []):
)
def test_contract_execution(
blockchain_test: BlockchainTestFiller,
bytecode,
gas_limit,
witness_code_chunk_numbers,
bytecode: bytes,
gas_limit: int,
witness_code_chunk_ranges,
):
"""
Test that contract execution generates expected witness.
Expand All @@ -228,16 +244,17 @@ def test_contract_execution(
)

code_chunks = chunkify_code(bytecode)
assert len(code_chunks) > 1
assert len(code_chunks) > 0

witness_check = WitnessCheck()
for address in [TestAddress, TestAddress2, env.fee_recipient]:
witness_check.add_account_full(
address=address,
account=(None if address == env.fee_recipient else pre[address]),
account=pre.get(address),
)
for chunk_number in witness_code_chunk_numbers:
witness_check.add_code_chunk(TestAddress2, chunk_number, code_chunks[chunk_number])
for chunk_ranges in witness_code_chunk_ranges:
for chunk_number in range(chunk_ranges[0], chunk_ranges[1] + 1):
witness_check.add_code_chunk(TestAddress2, chunk_number, code_chunks[chunk_number])

blocks = [
Block(
Expand Down
83 changes: 56 additions & 27 deletions tests/verkle/eip4762_verkle_gas_witness/test_creates.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,50 +41,74 @@
],
)
@pytest.mark.parametrize(
"code_size, value",
"code_size",
[
(0, 0),
(127 * 32, 0),
(130 * 32, 0),
(130 * 32 + 1, 0),
(130 * 32 + 1, 1),
0,
127 * 31,
130 * 31,
130 * 31 + 1,
],
ids=[
"empty",
"all_chunks_in_account_header",
"chunks_outside_account_header",
"with_partial_code_chunk",
"with_value",
],
)
def test_create(
blockchain_test: BlockchainTestFiller, create_instruction: Opcode, value, code_size
):
def test_create(blockchain_test: BlockchainTestFiller, create_instruction: Opcode, code_size):
"""
Test tx contract creation and *CREATE witness.
"""
contract_code = Op.PUSH0 * code_size
if create_instruction is None or create_instruction == Op.CREATE:
contract_code = bytes(Op.PUSH0 * code_size)
if create_instruction is None:
contract_address = compute_create_address(address=TestAddress, nonce=0)
elif create_instruction == Op.CREATE:
contract_address = compute_create_address(address=TestAddress2, nonce=0)
else:
contract_address = compute_create2_address(
TestAddress, 0xDEADBEEF, Initcode(deploy_code=contract_code)
TestAddress2, 0xDEADBEEF, Initcode(deploy_code=contract_code)
)

num_code_chunks = (len(contract_code) + 30) // 31
code_chunks = chunkify_code(contract_code)

witness_check_extra = WitnessCheck()
witness_check_extra.add_account_full(contract_address, None)
for i in range(num_code_chunks):
witness_check_extra.add_code_chunk(contract_address, i, code_chunks[i]) # type: ignore
witness_check_extra.add_code_chunk(contract_address, i, None)

_create(
blockchain_test,
create_instruction,
witness_check_extra,
contract_code,
value=value,
value=0,
)


@pytest.mark.valid_from("Verkle")
@pytest.mark.parametrize(
"create_instruction",
[
Op.CREATE,
Op.CREATE2,
],
)
def test_create_with_value_insufficient_balance(
blockchain_test: BlockchainTestFiller,
create_instruction: Opcode,
):
"""
Test tx contract creation and *CREATE value-bearing without sufficient balance.
"""
contract_code = bytes(Op.PUSH0 * 10)

_create(
blockchain_test,
create_instruction,
WitnessCheck(),
contract_code,
value=100,
creator_balance=0,
)


Expand Down Expand Up @@ -197,7 +221,7 @@ def test_create_collision(
create_instruction,
):
"""
Test *CREATE with address collision.
Test tx contract creation and *CREATE with address collision.
"""
_create(
blockchain_test,
Expand Down Expand Up @@ -227,12 +251,14 @@ def test_big_calldata(
Test *CREATE checking that code-chunk touching in the witness is not calculated from calldata
size but actual returned code from initcode execution.
"""
contract_code = Op.PUSH0 * (1000 * 31 + 42)
if create_instruction is None or create_instruction == Op.CREATE:
contract_code = bytes(Op.PUSH0 * (1000 * 31 + 42))
if create_instruction is None:
contract_address = compute_create_address(address=TestAddress, nonce=0)
elif create_instruction == Op.CREATE:
contract_address = compute_create_address(address=TestAddress2, nonce=0)
else:
contract_address = compute_create2_address(
TestAddress, 0xDEADBEEF, Initcode(deploy_code=contract_code)
TestAddress2, 0xDEADBEEF, Initcode(initcode_prefix=Op.STOP, deploy_code=contract_code)
)

witness_check_extra = WitnessCheck()
Expand All @@ -242,7 +268,7 @@ def test_big_calldata(
_create(
blockchain_test,
create_instruction,
WitnessCheck(),
witness_check_extra,
contract_code,
value=0,
initcode_stop_prefix=True,
Expand All @@ -254,10 +280,11 @@ def _create(
create_instruction: Opcode | None,
witness_check_extra: WitnessCheck,
contract_code,
value=1,
value: int = 0,
gas_limit=10000000000,
generate_collision: bool = False,
initcode_stop_prefix: bool = False,
creator_balance: int = 0,
):
env = Environment(
fee_recipient="0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
Expand All @@ -267,32 +294,34 @@ def _create(
timestamp=1000,
)
pre = {
TestAddress: Account(balance=1000000000000000000000),
TestAddress: Account(balance=1_000_000_000_000),
}

deploy_code = Initcode(
initcode_prefix=Op.STOP if initcode_stop_prefix else Bytecode(), deploy_code=contract_code
)
if create_instruction is not None and create_instruction.int() == Op.CREATE.int():
pre[TestAddress2] = Account(
code=Op.CALLDATACOPY(0, 0, len(deploy_code)) + Op.CREATE(value, 0, len(deploy_code))
code=Op.CALLDATACOPY(0, 0, len(deploy_code)) + Op.CREATE(value, 0, len(deploy_code)),
balance=creator_balance,
)
tx_target = TestAddress2
tx_value = 0
tx_data = deploy_code
if generate_collision:
contract_address = compute_create_address(address=TestAddress, nonce=0)
contract_address = compute_create_address(address=TestAddress2, nonce=0)
pre[contract_address] = Account(nonce=1)
elif create_instruction is not None and create_instruction.int() == Op.CREATE2.int():
pre[TestAddress2] = Account(
code=Op.CALLDATACOPY(0, 0, len(deploy_code))
+ Op.CREATE2(value, 0, len(deploy_code), 0xDEADBEEF)
+ Op.CREATE2(value, 0, len(deploy_code), 0xDEADBEEF),
balance=creator_balance,
)
tx_target = TestAddress2
tx_value = 0
tx_data = deploy_code
if generate_collision:
contract_address = compute_create2_address(TestAddress, 0xDEADBEEF, deploy_code)
contract_address = compute_create2_address(TestAddress2, 0xDEADBEEF, deploy_code)
pre[contract_address] = Account(nonce=1)
else:
tx_target = None
Expand Down
Loading
Loading