Skip to content

Commit

Permalink
Merge pull request #51 from jsign/jsign-fx-ce
Browse files Browse the repository at this point in the history
verkle: more test fixes
  • Loading branch information
jsign authored Sep 10, 2024
2 parents 7e16825 + 565c85c commit f2bc134
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 73 deletions.
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

0 comments on commit f2bc134

Please sign in to comment.