Skip to content

Commit

Permalink
Fix withdrawals is Option and improve block serde
Browse files Browse the repository at this point in the history
  • Loading branch information
ClementWalter committed Oct 4, 2024
1 parent 03ff3fa commit 0a12daa
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 29 deletions.
2 changes: 1 addition & 1 deletion cairo/src/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ namespace model {
state_root: Uint256,
transactions_root: Uint256,
receipt_root: Uint256,
withdrawals_root: Uint256,
withdrawals_root: Option,
bloom: felt*,
difficulty: Uint256,
number: felt,
Expand Down
33 changes: 19 additions & 14 deletions cairo/tests/test_serde.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
from src.utils.uint256 import int_to_uint256
from tests.utils.models import Account, to_int
from tests.utils.models import Account, Block, to_int


class TestSerde:

def test_block(self, cairo_run, block):
cairo_run("test_block", block=block)

def test_account(self, cairo_run, account):
result = cairo_run("test_account", account=account)
# Storage needs to handle differently because of the hashing of the keys
assert {
k: int_to_uint256(to_int(v)) for k, v in result["storage"].items()
} == account.storage
result["storage"] = {}
account.storage = {}

assert Account.model_validate(result) == account
result = cairo_run("test_block", block=block)
assert Block.model_validate(result) == block

def test_state(self, cairo_run, state):
cairo_run("test_state", state=state)
result = cairo_run("test_state", state=state)
assert [int(key, 16) for key in result["accounts"].keys()] == list(
state.accounts.keys()
)
for result, account in zip(
result["accounts"].values(), state.accounts.values()
):
# Storage needs to be handled differently because of the hashing of the keys
assert {
k: int_to_uint256(to_int(v))
for k, v in result["storage"].items()
if v is not None
} == account.storage
result["storage"] = {}
account.storage = {}
assert Account.model_validate(result) == account
57 changes: 47 additions & 10 deletions cairo/tests/utils/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,12 @@ def split_uint256(cls, values):
for key in [
"parent_hash",
"uncle_hash",
"ommers_hash",
"state_root",
"transactions_trie",
"transactions_root",
"receipt_trie",
"receipt_root",
"withdrawals_root",
"difficulty",
"mix_hash",
Expand All @@ -80,6 +83,7 @@ def split_uint256(cls, values):
def split_option(cls, values):
values = values.copy()
for key in [
"withdrawals_root",
"base_fee_per_gas",
"blob_gas_used",
"excess_blob_gas",
Expand All @@ -88,8 +92,9 @@ def split_option(cls, values):
]:
if key not in values:
key = to_camel(key)
is_some = key in values
value = to_int(values.get(key, 0))
is_some = key in values and values[key] is not None
# it's possible that values[key] exists and is None, that why we can't use get default value
value = to_int(values.get(key) or 0)
values[to_camel(key) + "IsSome"] = is_some
value_type = cls.model_fields[to_snake(key) + "_value"].annotation
values[to_camel(key) + "Value"] = (
Expand Down Expand Up @@ -124,17 +129,49 @@ def parse_bloom(cls, v):

parent_hash_low: int
parent_hash_high: int
uncle_hash_low: int
uncle_hash_high: int
uncle_hash_low: int = Field(
validation_alias=AliasChoices(
"ommersHash", "uncleHash", "ommers_hash", "uncle_hash", "ommers_hash_low"
)
)
uncle_hash_high: int = Field(
validation_alias=AliasChoices(
"ommersHashHigh", "uncleHashHigh", "ommers_hash_high"
)
)
coinbase: int
state_root_low: int
state_root_high: int
transactions_trie_low: int
transactions_trie_high: int
receipt_trie_low: int
receipt_trie_high: int
withdrawals_root_low: int
withdrawals_root_high: int
transactions_trie_low: int = Field(
validation_alias=AliasChoices(
"transactionsTrie",
"transactionsRoot",
"transactions_trie",
"transactions_root",
"transactions_root_low",
)
)
transactions_trie_high: int = Field(
validation_alias=AliasChoices(
"transactionsTrieHigh", "transactionsRootHigh", "transactions_root_high"
)
)
receipt_trie_low: int = Field(
validation_alias=AliasChoices(
"transactionsTrie",
"transactionsRoot",
"transactions_trie",
"transactions_root",
"transactions_root_low",
)
)
receipt_trie_high: int = Field(
validation_alias=AliasChoices(
"receiptTrieHigh", "receiptRootHigh", "receipt_root_high"
)
)
withdrawals_root_is_some: bool
withdrawals_root_value: Tuple[int, int]
bloom: Tuple[int, ...]
difficulty_low: int
difficulty_high: int
Expand Down
40 changes: 36 additions & 4 deletions cairo/tests/utils/serde.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

from eth_utils.address import to_checksum_address
from starkware.cairo.lang.compiler.ast.cairo_types import (
TypeFelt,
Expand Down Expand Up @@ -94,7 +96,7 @@ def serialize_pointers(self, name, ptr):
output[name] = member_ptr
return output

def serialize_struct(self, name, ptr):
def serialize_struct(self, name, ptr) -> Optional[dict]:
"""
Serialize a struct, e.g. Uint256.
"""
Expand Down Expand Up @@ -233,17 +235,45 @@ def serialize_rlp_item(self, ptr):

def serialize_block(self, ptr):
raw = self.serialize_pointers("model.Block", ptr)
return {
"block_header": self.serialize_struct(
"model.BlockHeader", raw["block_header"]
header = self.serialize_struct("model.BlockHeader", raw["block_header"])
if header is None:
raise ValueError("Block header is None")
header = {
**header,
"withdrawals_root": (
self.serialize_uint256(header["withdrawals_root"])
if header["withdrawals_root"] is not None
else None
),
"parent_beacon_block_root": (
self.serialize_uint256(header["parent_beacon_block_root"])
if header["parent_beacon_block_root"] is not None
else None
),
"requests_root": (
self.serialize_uint256(header["requests_root"])
if header["requests_root"] is not None
else None
),
"extra_data": bytes(header["extra_data"][: header["extra_data_len"]]),
"bloom": bytes.fromhex("".join(f"{b:032x}" for b in header["bloom"])),
}
del header["extra_data_len"]
return {
"block_header": header,
"transactions": self.serialize_list(
raw["transactions"],
"model.TransactionEncoded",
list_len=raw["transactions_len"],
),
}

def serialize_option(self, ptr):
raw = self.serialize_pointers("model.Option", ptr)
if raw["is_some"] == 0:
return None
return raw["value"]

def serialize_scope(self, scope, scope_ptr):
if scope.path[-1] == "State":
return self.serialize_state(scope_ptr)
Expand All @@ -265,6 +295,8 @@ def serialize_scope(self, scope, scope_ptr):
return self.serialize_rlp_item(scope_ptr)
if scope.path[-1] == ("Block"):
return self.serialize_block(scope_ptr)
if scope.path[-1] == ("Option"):
return self.serialize_option(scope_ptr)
try:
return self.serialize_struct(str(scope), scope_ptr)
except MissingIdentifierError:
Expand Down

0 comments on commit 0a12daa

Please sign in to comment.