From 03ff3fa199b4c93cbbe0485be18c879e86631181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Walter?= Date: Thu, 3 Oct 2024 18:02:19 +0300 Subject: [PATCH] Fix header fields --- cairo/programs/os.cairo | 4 +- cairo/src/model.cairo | 35 ++++++----- cairo/tests/test_serde.py | 5 +- cairo/tests/utils/models.py | 111 ++++++++++++++++++++++------------- cairo/tests/utils/parsers.py | 10 +++- 5 files changed, 97 insertions(+), 68 deletions(-) diff --git a/cairo/programs/os.cairo b/cairo/programs/os.cairo index 814d9221..09c25549 100644 --- a/cairo/programs/os.cairo +++ b/cairo/programs/os.cairo @@ -42,7 +42,7 @@ func main{ let header = block.block_header; assert [range_check_ptr] = header.gas_limit; assert [range_check_ptr + 1] = header.gas_used; - assert [range_check_ptr + 2] = header.base_fee_per_gas; + assert [range_check_ptr + 2] = header.base_fee_per_gas.value; let range_check_ptr = range_check_ptr + 3; with header, chain_id, state { @@ -105,7 +105,7 @@ func apply_transactions{ } with_attr error_message("Max fee per gas too low") { - assert_nn(tx.max_fee_per_gas - header.base_fee_per_gas); + assert_nn(tx.max_fee_per_gas - header.base_fee_per_gas.value); } with_attr error_message("Max priority fee greater than max fee per gas") { diff --git a/cairo/src/model.cairo b/cairo/src/model.cairo index abb411aa..4ef4348e 100644 --- a/cairo/src/model.cairo +++ b/cairo/src/model.cairo @@ -213,29 +213,28 @@ namespace model { // @notice A struct representing the header of an Eth block. struct BlockHeader { - base_fee_per_gas: felt, - blob_gas_used: felt, - bloom_len: felt, - bloom: felt*, + parent_hash: Uint256, + ommers_hash: Uint256, coinbase: felt, - difficulty: felt, - excess_blob_gas: felt, - extra_data_len: felt, - extra_data: felt*, + state_root: Uint256, + transactions_root: Uint256, + receipt_root: Uint256, + withdrawals_root: Uint256, + bloom: felt*, + difficulty: Uint256, + number: felt, gas_limit: felt, gas_used: felt, - hash: Uint256, + timestamp: felt, mix_hash: Uint256, nonce: felt, - number: felt, - parent_beacon_block_root: Uint256, - parent_hash: Uint256, - receipt_trie: Uint256, - state_root: Uint256, - timestamp: felt, - transactions_trie: Uint256, - uncle_hash: Uint256, - withdrawals_root: Uint256, + base_fee_per_gas: Option, + blob_gas_used: Option, + excess_blob_gas: Option, + parent_beacon_block_root: Option, + requests_root: Option, + extra_data_len: felt, + extra_data: felt*, } // @notice A struct representing an encoded Ethereum transaction. diff --git a/cairo/tests/test_serde.py b/cairo/tests/test_serde.py index dd7a9e14..180d90eb 100644 --- a/cairo/tests/test_serde.py +++ b/cairo/tests/test_serde.py @@ -1,12 +1,11 @@ from src.utils.uint256 import int_to_uint256 -from tests.utils.models import Account, Block, to_int +from tests.utils.models import Account, to_int class TestSerde: def test_block(self, cairo_run, block): - result = cairo_run("test_block", block=block) - assert Block.model_validate(result) == block + cairo_run("test_block", block=block) def test_account(self, cairo_run, account): result = cairo_run("test_account", account=account) diff --git a/cairo/tests/utils/models.py b/cairo/tests/utils/models.py index adce5eb9..dcf174d0 100644 --- a/cairo/tests/utils/models.py +++ b/cairo/tests/utils/models.py @@ -1,5 +1,6 @@ from collections import defaultdict from itertools import chain +from textwrap import wrap from typing import Annotated, DefaultDict, Tuple, Union from eth_utils import keccak @@ -13,7 +14,7 @@ field_validator, model_validator, ) -from pydantic.alias_generators import to_camel +from pydantic.alias_generators import to_camel, to_snake from starkware.cairo.lang.vm.crypto import pedersen_hash from src.utils.uint256 import int_to_uint256 @@ -41,38 +42,29 @@ def __iter__(self): class BlockHeader(BaseModelIterValuesOnly): @field_validator( - "base_fee_per_gas", - "blob_gas_used", "coinbase", - "difficulty", - "excess_blob_gas", + "number", "gas_limit", "gas_used", - "nonce", - "number", "timestamp", + "nonce", mode="before", ) def parse_hex_to_int(cls, v): return to_int(v) - @field_validator("extra_data", "bloom", mode="before") - def parse_hex_to_bytes(cls, v): - return to_bytes(v) - @model_validator(mode="before") def split_uint256(cls, values): values = values.copy() for key in [ - "hash", - "mix_hash", - "parent_beacon_block_root", "parent_hash", - "receipt_trie", + "uncle_hash", "state_root", "transactions_trie", - "uncle_hash", + "receipt_trie", "withdrawals_root", + "difficulty", + "mix_hash", ]: if key not in values: key = to_camel(key) @@ -85,51 +77,86 @@ def split_uint256(cls, values): return values @model_validator(mode="before") - def add_len_to_bytes(cls, values): + def split_option(cls, values): values = values.copy() - for key in ["bloom", "extra_data"]: + for key in [ + "base_fee_per_gas", + "blob_gas_used", + "excess_blob_gas", + "parent_beacon_block_root", + "requests_root", + ]: + if key not in values: + key = to_camel(key) + is_some = key in values + value = to_int(values.get(key, 0)) + values[to_camel(key) + "IsSome"] = is_some + value_type = cls.model_fields[to_snake(key) + "_value"].annotation + values[to_camel(key) + "Value"] = ( + int_to_uint256(value) if value_type == Tuple[int, int] else value + ) + return values + + @model_validator(mode="before") + def add_len_to_extra_data(cls, values): + values = values.copy() + for key in ["extra_data"]: if key not in values: key = to_camel(key) if key not in values: continue value = to_bytes(values[key]) + if value is None: + continue values[key] = value values[to_camel(key) + "Len"] = len(value) return values - base_fee_per_gas: int - blob_gas_used: int - bloom_len: int - bloom: bytes - coinbase: int - difficulty: int - excess_blob_gas: int - extra_data_len: int - extra_data: bytes - gas_limit: int - gas_used: int - hash_low: int - hash_high: int - mix_hash_low: int - mix_hash_high: int - nonce: int - number: int - parent_beacon_block_root_low: int - parent_beacon_block_root_high: int + @field_validator("bloom", mode="before") + def parse_bloom(cls, v): + bloom = to_bytes(v) + if bloom is None: + raise ValueError("Bloom cannot be empty") + if len(bloom) != 256: + raise ValueError("Bloom must be 256 bytes") + return tuple(int(chunk) for chunk in wrap(bloom.hex(), 32)) + parent_hash_low: int parent_hash_high: int - receipt_trie_low: int - receipt_trie_high: int + uncle_hash_low: int + uncle_hash_high: int + coinbase: int state_root_low: int state_root_high: int - timestamp: int transactions_trie_low: int transactions_trie_high: int - uncle_hash_low: int - uncle_hash_high: int + receipt_trie_low: int + receipt_trie_high: int withdrawals_root_low: int withdrawals_root_high: int + bloom: Tuple[int, ...] + difficulty_low: int + difficulty_high: int + number: int + gas_limit: int + gas_used: int + timestamp: int + mix_hash_low: int + mix_hash_high: int + nonce: int + base_fee_per_gas_is_some: bool + base_fee_per_gas_value: int + blob_gas_used_is_some: bool + blob_gas_used_value: int + excess_blob_gas_is_some: bool + excess_blob_gas_value: int + parent_beacon_block_root_is_some: bool + parent_beacon_block_root_value: Tuple[int, int] + requests_root_is_some: bool + requests_root_value: Tuple[int, int] + extra_data_len: int + extra_data: bytes class TransactionEncoded(BaseModelIterValuesOnly): diff --git a/cairo/tests/utils/parsers.py b/cairo/tests/utils/parsers.py index 5d45d14a..544b8601 100644 --- a/cairo/tests/utils/parsers.py +++ b/cairo/tests/utils/parsers.py @@ -1,7 +1,9 @@ -from typing import Union +from typing import Optional, Union -def to_int(v: Union[str, int]) -> int: +def to_int(v: Optional[Union[str, int]]) -> Optional[int]: + if v is None: + return v if isinstance(v, str): if v.startswith("0x"): return int(v, 16) @@ -9,7 +11,9 @@ def to_int(v: Union[str, int]) -> int: return v -def to_bytes(v: Union[str, bytes, list[int]]) -> bytes: +def to_bytes(v: Optional[Union[str, bytes, list[int]]]) -> Optional[bytes]: + if v is None: + return v if isinstance(v, bytes): return v elif isinstance(v, str):